All files / features / auth CallbackPage.tsx

92.59% Statements 25/27
86.66% Branches 13/15
87.5% Functions 7/8
95.83% Lines 23/24

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70                        6x 22x 6x   6x 5x 5x   5x 4x 4x           4x 4x 4x   1x       6x 1x     5x               1x 1x 1x     1x                 4x 4x 4x          
import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useAuthStore } from './authStore'
 
interface JwtPayload {
  sub?: string
  preferred_username?: string
  email?: string
  groups?: string[]
}
 
export function CallbackPage() {
  const navigate = useNavigate()
  const setAuth = useAuthStore((s) => s.setAuth)
  const [error, setError] = useState<string | null>(null)
 
  useEffect(() => {
    const token = sessionStorage.getItem('oidc-token')
    sessionStorage.removeItem('oidc-token')
 
    if (token) {
      const decoded = decodeJwtPayload(token)
      setAuth(token, {
        sub: decoded.sub ?? '',
        preferred_username: decoded.preferred_username ?? decoded.sub ?? '',
        email: decoded.email ?? '',
        groups: decoded.groups ?? [],
      })
      const returnUrl = sessionStorage.getItem('returnUrl')
      sessionStorage.removeItem('returnUrl')
      navigate(returnUrl ?? '/', { replace: true })
    } else {
      setError('No access token found in callback')
    }
  }, [navigate, setAuth])
 
  if (error) {
    return <CallbackError error={error} navigate={navigate} />
  }
 
  return (
    <div className="flex min-h-screen items-center justify-center">
      <p className="text-muted-foreground">Signing in...</p>
    </div>
  )
}
 
function CallbackError({ error, navigate }: { error: string; navigate: (path: string, opts?: { replace?: boolean }) => void }) {
  useEffect(() => {
    const timer = setTimeout(() => navigate('/login', { replace: true }), 3000)
    return () => clearTimeout(timer)
  }, [navigate])
 
  return (
    <div className="flex min-h-screen flex-col items-center justify-center gap-4">
      <p className="text-destructive">{error}</p>
      <p className="text-sm text-muted-foreground">Redirecting to login...</p>
    </div>
  )
}
 
function decodeJwtPayload(token: string): JwtPayload {
  try {
    const base64 = token.split('.')[1]
    return JSON.parse(atob(base64)) as JwtPayload
  } catch {
    return {}
  }
}