69 lines
2.0 KiB
TypeScript
69 lines
2.0 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
|
|
import {
|
|
clearLoginState,
|
|
exchangeKeycloakCallback,
|
|
loadLoginState,
|
|
parseAuthCallbackUrl,
|
|
} from '@/lib/keycloak-oidc';
|
|
import { getMe } from '@/lib/api';
|
|
import { pickAppText } from '@/lib/i18n/core';
|
|
import { useAppI18n } from '@/lib/i18n/provider';
|
|
import { useChatStore } from '@/lib/store';
|
|
|
|
export default function AuthCallbackPage() {
|
|
const { locale } = useAppI18n();
|
|
const router = useRouter();
|
|
const setUser = useChatStore((s) => s.setUser);
|
|
const [error, setError] = useState('');
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
const completeLogin = async () => {
|
|
const callback = parseAuthCallbackUrl();
|
|
if (callback.error) {
|
|
throw new Error(callback.errorDescription || callback.error);
|
|
}
|
|
if (!callback.code || !callback.state) {
|
|
throw new Error('Missing Keycloak callback code or state');
|
|
}
|
|
const loginState = loadLoginState();
|
|
if (!loginState || loginState.state !== callback.state) {
|
|
throw new Error('Invalid Keycloak login state');
|
|
}
|
|
|
|
const result = await exchangeKeycloakCallback({ code: callback.code, state: loginState });
|
|
clearLoginState();
|
|
const user = await getMe().catch(() => result.user);
|
|
if (cancelled) return;
|
|
setUser(user);
|
|
router.replace(loginState.nextPath || '/');
|
|
};
|
|
|
|
completeLogin().catch((exc) => {
|
|
clearLoginState();
|
|
if (!cancelled) {
|
|
setError(exc instanceof Error ? exc.message : String(exc));
|
|
}
|
|
});
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [router, setUser]);
|
|
|
|
return (
|
|
<div className="flex min-h-screen items-center justify-center px-4">
|
|
<div className="max-w-lg text-center text-sm text-muted-foreground">
|
|
{error
|
|
? pickAppText(locale, `登录失败:${error}`, `Sign-in failed: ${error}`)
|
|
: pickAppText(locale, '正在完成登录...', 'Completing sign-in...')}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|