mirror of
https://github.com/dat515-2025/Group-8.git
synced 2026-03-22 15:12:08 +01:00
feat(oauth): add csas connection, allow oauth from react
This commit is contained in:
@@ -3,21 +3,54 @@ import './App.css';
|
||||
import LoginRegisterPage from './pages/LoginRegisterPage';
|
||||
import Dashboard from './pages/Dashboard';
|
||||
import { logout } from './api';
|
||||
import { BACKEND_URL } from './config';
|
||||
|
||||
function App() {
|
||||
const [hasToken, setHasToken] = useState<boolean>(!!localStorage.getItem('token'));
|
||||
const [processingCallback, setProcessingCallback] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Handle OAuth callback: /oauth-callback?access_token=...&token_type=...
|
||||
if (window.location.pathname === '/oauth-callback') {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const token = params.get('access_token');
|
||||
if (token) {
|
||||
localStorage.setItem('token', token);
|
||||
setHasToken(true);
|
||||
const path = window.location.pathname;
|
||||
|
||||
// Minimal handling for provider callbacks: /auth|/oauth/:provider/callback?code=...&state=...
|
||||
const parts = path.split('/').filter(Boolean);
|
||||
const isCallback = parts.length === 3 && (parts[0] === 'auth') && parts[2] === 'callback';
|
||||
|
||||
if (isCallback) {
|
||||
// Guard against double invocation in React 18 StrictMode/dev
|
||||
const w = window as any;
|
||||
if (w.__oauthCallbackHandled) {
|
||||
return;
|
||||
}
|
||||
// Clean URL and redirect to home
|
||||
window.history.replaceState({}, '', '/');
|
||||
w.__oauthCallbackHandled = true;
|
||||
|
||||
setProcessingCallback(true);
|
||||
|
||||
const provider = parts[1];
|
||||
const qs = window.location.search || '';
|
||||
const base = BACKEND_URL.replace(/\/$/, '');
|
||||
const url = `${base}/auth/${encodeURIComponent(provider)}/callback${qs}`;
|
||||
(async () => {
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
const res = await fetch(url, {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : undefined,
|
||||
});
|
||||
let data: any = null;
|
||||
try {
|
||||
data = await res.json();
|
||||
} catch {}
|
||||
if (provider !== 'csas' && res.ok && data?.access_token) {
|
||||
localStorage.setItem('token', data?.access_token);
|
||||
setHasToken(true);
|
||||
}
|
||||
} catch {}
|
||||
// Clean URL and go home regardless of result
|
||||
setProcessingCallback(false);
|
||||
window.history.replaceState({}, '', '/');
|
||||
})();
|
||||
}
|
||||
|
||||
const onStorage = (e: StorageEvent) => {
|
||||
@@ -27,6 +60,24 @@ function App() {
|
||||
return () => window.removeEventListener('storage', onStorage);
|
||||
}, []);
|
||||
|
||||
if (processingCallback) {
|
||||
return (
|
||||
<div style={{ display: 'grid', placeItems: 'center', height: '100vh' }}>
|
||||
<div className="card" style={{ width: 360, textAlign: 'center', padding: 24 }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
|
||||
<svg width="48" height="48" viewBox="0 0 50 50" aria-label="Loading">
|
||||
<circle cx="25" cy="25" r="20" fill="none" stroke="#3b82f6" strokeWidth="5" strokeLinecap="round" strokeDasharray="31.4 31.4">
|
||||
<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.9s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
</svg>
|
||||
<div>Finishing sign-in…</div>
|
||||
<div className="muted">Please wait</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!hasToken) {
|
||||
return <LoginRegisterPage onLoggedIn={() => setHasToken(true)} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user