feat(auth): wire AuthProvider around app providers and mirror token to sessionStorage
This commit is contained in:
73
auth/AuthProvider.tsx
Normal file
73
auth/AuthProvider.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import { createContext, useContext, useEffect, useMemo, useState, PropsWithChildren } from 'react'
|
||||
import { userManager } from './oidc'
|
||||
import type { User } from 'oidc-client-ts'
|
||||
|
||||
|
||||
interface AuthCtx {
|
||||
user: User | null
|
||||
isAuthenticated: boolean
|
||||
signIn: (returnTo?: string) => Promise<void>
|
||||
signOut: () => Promise<void>
|
||||
getAccessToken: () => string | null
|
||||
}
|
||||
|
||||
|
||||
const Ctx = createContext<AuthCtx | null>(null)
|
||||
|
||||
|
||||
export function AuthProvider({ children }: PropsWithChildren) {
|
||||
const [user, setUser] = useState<User | null>(null)
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
userManager.getUser().then(u => setUser(u))
|
||||
|
||||
|
||||
const onLoaded = (u: User) => setUser(u)
|
||||
const onUnloaded = () => setUser(null)
|
||||
const onExpired = async () => {
|
||||
try { await userManager.signinSilent() } catch {/* ignore */}
|
||||
}
|
||||
|
||||
|
||||
userManager.events.addUserLoaded(onLoaded)
|
||||
userManager.events.addUserUnloaded(onUnloaded)
|
||||
userManager.events.addAccessTokenExpired(onExpired)
|
||||
return () => {
|
||||
userManager.events.removeUserLoaded(onLoaded)
|
||||
userManager.events.removeUserUnloaded(onUnloaded)
|
||||
userManager.events.removeAccessTokenExpired(onExpired)
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
||||
// Spegla token till sessionStorage så ky kan läsa den
|
||||
useEffect(() => {
|
||||
const token = user?.access_token ?? null
|
||||
if (token) sessionStorage.setItem('access_token', token)
|
||||
else sessionStorage.removeItem('access_token')
|
||||
}, [user])
|
||||
|
||||
|
||||
const api: AuthCtx = useMemo(() => ({
|
||||
user,
|
||||
isAuthenticated: !!user && !user.expired,
|
||||
signIn: async (returnTo) => {
|
||||
await userManager.signinRedirect({ state: { returnTo } })
|
||||
},
|
||||
signOut: async () => {
|
||||
await userManager.signoutRedirect()
|
||||
},
|
||||
getAccessToken: () => user?.access_token ?? null,
|
||||
}), [user])
|
||||
|
||||
|
||||
return <Ctx.Provider value={api}>{children}</Ctx.Provider>
|
||||
}
|
||||
|
||||
|
||||
export function useAuth() {
|
||||
const ctx = useContext(Ctx)
|
||||
if (!ctx) throw new Error('useAuth must be used within <AuthProvider>')
|
||||
return ctx
|
||||
}
|
||||
Reference in New Issue
Block a user