feat(oauth): add csas connection, allow oauth from react

This commit is contained in:
2025-10-21 22:01:09 +02:00
parent be4a3b401a
commit 3ebf47e371
13 changed files with 509 additions and 20 deletions

View File

@@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from 'react';
import { type Category, type Transaction, createTransaction, getCategories, getTransactions } from '../api';
import AccountPage from './AccountPage';
import AppearancePage from './AppearancePage';
import { BACKEND_URL } from '../config';
function formatAmount(n: number) {
return new Intl.NumberFormat(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(n);
@@ -14,6 +15,27 @@ export default function Dashboard({ onLogout }: { onLogout: () => void }) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// Start CSAS (George) OAuth after login
async function startOauthCsas() {
const base = BACKEND_URL.replace(/\/$/, '');
const url = `${base}/auth/csas/authorize`;
try {
const token = localStorage.getItem('token');
const res = await fetch(url, {
credentials: 'include',
headers: token ? { Authorization: `Bearer ${token}` } : undefined,
});
const data = await res.json();
if (data && typeof data.authorization_url === 'string') {
window.location.assign(data.authorization_url);
} else {
alert('Cannot start CSAS OAuth.');
}
} catch (e) {
alert('Cannot start CSAS OAuth.');
}
}
// New transaction form state
const [amount, setAmount] = useState<string>('');
const [description, setDescription] = useState('');
@@ -97,6 +119,12 @@ export default function Dashboard({ onLogout }: { onLogout: () => void }) {
<main className="page space-y">
{current === 'home' && (
<>
<section className="card">
<h3>Bank connections</h3>
<p className="muted">Connect your CSAS (George) account.</p>
<button className="btn" onClick={startOauthCsas}>Connect CSAS (George)</button>
</section>
<section className="card">
<h3>Add Transaction</h3>
<form onSubmit={handleCreate} className="form-row">

View File

@@ -2,10 +2,21 @@ import { useState, useEffect } from 'react';
import { login, register } from '../api';
import { BACKEND_URL } from '../config';
function oauthUrl(provider: 'mojeid' | 'bankid') {
// Minimal helper to start OAuth: fetch authorization_url and redirect
async function startOauth(provider: 'mojeid' | 'bankid') {
const base = BACKEND_URL.replace(/\/$/, '');
const redirect = encodeURIComponent(window.location.origin + '/oauth-callback');
return `${base}/auth/${provider}/authorize?redirect_url=${redirect}`;
const url = `${base}/auth/${provider}/authorize`;
try {
const res = await fetch(url, { credentials: 'include' });
const data = await res.json();
if (data && typeof data.authorization_url === 'string') {
window.location.assign(data.authorization_url);
} else {
alert('Cannot start OAuth.');
}
} catch (e) {
alert('Cannot start OAuth.');
}
}
export default function LoginRegisterPage({ onLoggedIn }: { onLoggedIn: () => void }) {
@@ -84,8 +95,8 @@ export default function LoginRegisterPage({ onLoggedIn }: { onLoggedIn: () => vo
<div className="actions" style={{ justifyContent: 'space-between' }}>
<div className="muted">Or continue with</div>
<div className="actions">
<a className="btn" href={oauthUrl('mojeid')}>MojeID</a>
<a className="btn" href={oauthUrl('bankid')}>BankID</a>
<button type="button" className="btn" onClick={() => startOauth('mojeid')}>MojeID</button>
<button type="button" className="btn" onClick={() => startOauth('bankid')}>BankID</button>
<button className="btn primary" type="submit" disabled={loading}>{loading ? 'Please wait…' : (mode === 'login' ? 'Login' : 'Register')}</button>
</div>
</div>