template-monorepo/frontend/src/pages/user/OAuthCallbackPage.tsx

91 lines
2.8 KiB
TypeScript
Raw Normal View History

2026-05-27 09:28:13 +00:00
import { useEffect, useState } from 'react';
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
import * as authApi from '../../api/auth';
import { ApiError } from '../../api/http';
import { DEFAULT_TENANT } from '../../config';
import { useAuth } from '../../context/AuthContext';
import * as permApi from '../../api/permission';
type OAuthMode = 'login' | 'register';
export function OAuthCallbackPage({ mode }: { mode: OAuthMode }) {
const navigate = useNavigate();
const [search] = useSearchParams();
const { syncSession, refreshRoles } = useAuth();
const [error, setError] = useState('');
useEffect(() => {
const code = search.get('code');
const state = search.get('state');
const oauthError = search.get('error');
const tenant =
localStorage.getItem('tenant_slug') ?? DEFAULT_TENANT;
if (oauthError) {
setError(search.get('error_description') ?? oauthError);
return;
}
if (!code || !state) {
setError('缺少 OAuth 參數code / state');
return;
}
let cancelled = false;
(async () => {
try {
if (mode === 'login') {
const result = await authApi.loginSocialCallback(code, state);
if (result.mfa_required && result.mfa_challenge_id) {
navigate('/login', {
replace: true,
state: {
message: '請完成雙因素驗證以完成登入',
mfa_challenge_id: result.mfa_challenge_id,
tenant,
},
});
return;
}
} else {
await authApi.registerSocialCallback(code, state);
}
if (cancelled) return;
syncSession();
await refreshRoles();
const me = await permApi.getMyPermissions();
const admin = permApi.isAdminRole(me.roles ?? []);
navigate(admin ? '/admin' : '/app', { replace: true });
} catch (err) {
if (cancelled) return;
if (err instanceof ApiError && err.code === 29301000 && mode === 'login') {
setError('尚無帳號,請先註冊或聯絡管理員開通 LDAP 帳號。');
} else {
setError(err instanceof ApiError ? err.message : '登入失敗');
}
}
})();
return () => {
cancelled = true;
};
}, [mode, navigate, refreshRoles, search, syncSession]);
return (
<div className="auth-card">
<h1>{mode === 'login' ? '登入處理中' : '註冊處理中'}</h1>
{error ? (
<>
<p className="form-error">{error}</p>
<p className="auth-footer">
<Link to={mode === 'login' ? '/login' : '/register'}></Link>
</p>
</>
) : (
<p className="auth-hint"></p>
)}
</div>
);
}