allauth/ (44 files) is a django-allauth React UI — a separate concern from the Mizan protocol. Moved to legacy/ pending extraction into a standalone mizan-django-allauth package. Also moved to legacy/: - client/AuthContext.tsx — generic auth state from /me endpoint - client/RouterContext.tsx — framework-agnostic router adapter - client/routing.tsx — UserRoute/StaffRoute/AnonymousRoute guards - client/nextjs.tsx — Next.js router adapter for auth These are auth UI infrastructure, not Mizan protocol. The Mizan core only needs JWT for auth header selection (jwt/ stays — MizanProvider depends on useJWT() to decide between Bearer and session auth). Cleaned up re-exports in client/react.ts and vitest aliases. 33 React tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
117 lines
3.8 KiB
TypeScript
117 lines
3.8 KiB
TypeScript
'use client'
|
|
|
|
import { ReactNode, useEffect, useState } from 'react'
|
|
import { useDjangoCSRClient, Auth } from 'mizan/client/react'
|
|
import type { RouterAdapter } from '../adapters/router'
|
|
import type { InitialAuth } from '../hydration'
|
|
import { AuthContext } from './AuthContext'
|
|
import { ConfigContext } from './ConfigContext'
|
|
import { StylesContext } from './StylesContext'
|
|
import { RouterContext } from './RouterContext'
|
|
import { AllauthConfig } from '../config'
|
|
import { AuthClassNames } from '../styles/types'
|
|
import { createAPI } from '../api'
|
|
|
|
export interface AllauthContextProps {
|
|
children: ReactNode
|
|
|
|
/** Router adapter for navigation */
|
|
router: RouterAdapter
|
|
|
|
/** Optional initial auth state from getInitialAuth() - if not provided, fetches client-side */
|
|
hydration?: InitialAuth
|
|
|
|
/** Library configuration (basePath, routes) */
|
|
allauthConfig?: Partial<AllauthConfig>
|
|
|
|
/** CSS class names for styling components */
|
|
classNames?: AuthClassNames
|
|
}
|
|
|
|
/**
|
|
* Core AllauthContext - sets up all contexts for the allauth library.
|
|
*
|
|
* IMPORTANT: AllauthContext must be wrapped by DjangoContext, which provides
|
|
* user data via useUser(). The typical setup is:
|
|
*
|
|
* ```tsx
|
|
* <DjangoContext client={client} hydration={djangoHydration}>
|
|
* <AllauthContext hydration={allauthHydration}>
|
|
* {children}
|
|
* </AllauthContext>
|
|
* </DjangoContext>
|
|
* ```
|
|
*
|
|
* If hydration is provided (from SSR), uses it immediately.
|
|
* If not provided, fetches initial auth state client-side using the CSR client.
|
|
*
|
|
* For Next.js apps, use NextAllauthContext instead which handles the router automatically.
|
|
*/
|
|
export function AllauthContext({
|
|
children,
|
|
router,
|
|
hydration,
|
|
allauthConfig,
|
|
classNames,
|
|
}: AllauthContextProps) {
|
|
const client = useDjangoCSRClient(Auth.SESSION)
|
|
const [initialAuth, setInitialAuth] = useState<InitialAuth | null>(hydration ?? null)
|
|
const [loading, setLoading] = useState(!hydration)
|
|
|
|
useEffect(() => {
|
|
if (hydration) return // Already have SSR hydration
|
|
|
|
const fetchInitialAuth = async () => {
|
|
try {
|
|
const authRequest = async (method: string, path: string, data?: any, headers?: Record<string, string>) => {
|
|
const resp = await client.request(method, `/_allauth/browser/v1${path}`, data, headers)
|
|
if (resp.status >= 500) {
|
|
throw new Error(`Allauth request failed: ${resp.status} ${resp.statusText}`)
|
|
}
|
|
return resp.json()
|
|
}
|
|
|
|
const api = createAPI((method, path, data?, headers?) =>
|
|
authRequest(method, path, { ...(data as object), client: 'browser' }, headers)
|
|
)
|
|
|
|
const [config, auth] = await Promise.all([
|
|
api.getConfig(),
|
|
api.session.getStatus(),
|
|
])
|
|
|
|
setInitialAuth({ config, auth })
|
|
} catch (e) {
|
|
console.error('[AllauthContext] Failed to fetch initial auth:', e)
|
|
setInitialAuth({
|
|
config: { status: 200, data: {} },
|
|
auth: { status: 401, data: {} },
|
|
})
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
fetchInitialAuth()
|
|
}, [client, hydration])
|
|
|
|
if (loading || !initialAuth) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<RouterContext router={router}>
|
|
<ConfigContext config={allauthConfig}>
|
|
<StylesContext classNames={classNames}>
|
|
<AuthContext
|
|
config={initialAuth.config}
|
|
auth={initialAuth.auth}
|
|
>
|
|
{children}
|
|
</AuthContext>
|
|
</StylesContext>
|
|
</ConfigContext>
|
|
</RouterContext>
|
|
)
|
|
}
|