feat(auth): sync OAuth provider tokens to backend via NextAuth session

Wire the Google/Facebook OAuth callback flow end-to-end so the provider's id_token (Google) and access_token (Facebook) captured by NextAuth are forwarded to the backend in exchange for business tokens:

- Extend the NextAuth config with jwt/session callbacks that surface `account.id_token` / `account.access_token` to the client during first sign-in
- Add an `OAuthSessionSync` bridge mounted inside `RootProviders` that listens to `useSession()` and dispatches new `AuthGoogleSyncSubmitted` / `AuthFacebookSyncSubmitted` events
- Add corresponding actors in the auth XState machine that call `authRepository.googleLogin` / `facebookLogin`, persisting the backend's `LoginResponse` through the existing repository path

This keeps all authentication orchestrated by the auth state machine while preserving NextAuth's OAuth redirect UX.
This commit is contained in:
2026-06-11 12:59:23 +08:00
parent b50191ed50
commit 0c63b7dee4
6 changed files with 203 additions and 2 deletions
+5
View File
@@ -5,6 +5,7 @@
*
* 把所有功能 Provider 串起来:
* AuthProvider → UserProvider → SidebarProvider → ChatProvider
* + OAuthSessionSync 桥接器(监听 NextAuth session → auth machine
*
* 它们始终挂载,保证各页面直接 `use*State()` / `use*Dispatch()` 即可,
* 无需在每个页面单独包裹。
@@ -13,6 +14,7 @@
import type { ReactNode } from "react";
import { AuthProvider } from "@/stores/auth/auth-context";
import { OAuthSessionSync } from "@/stores/auth/oauth-session-sync";
import { ChatProvider } from "@/stores/chat/chat-context";
import { SidebarProvider } from "@/stores/sidebar/sidebar-context";
import { UserProvider } from "@/stores/user/user-context";
@@ -24,6 +26,9 @@ export interface RootProvidersProps {
export function RootProviders({ children }: RootProvidersProps) {
return (
<AuthProvider>
{/* OAuthSessionSync 必须放在 AuthProvider 内部(依赖 useAuthDispatch/useAuthState),
* 也必须在 ChatProvider 外部(ChatProvider 内部组件可能在 sync 完成前就需要登录态) */}
<OAuthSessionSync />
<UserProvider>
<SidebarProvider>
<ChatProvider>