@oauth42/next 0.2.9 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -342,6 +342,6 @@ declare function logout(options?: {
342
342
  * };
343
343
  * ```
344
344
  */
345
- declare function logoutEverywhere(issuer: string, callbackUrl?: string, cookiePrefix?: string): void;
345
+ declare function logoutEverywhere(issuer: string, callbackUrl?: string): Promise<void>;
346
346
 
347
347
  export { type AuthError, AuthStatus, type AuthStatusProps, DEFAULT_HOSTED_AUTH_CONFIG, type HostedAuthConfig, type HostedAuthOptions, type LoginWithAuthenticatorOptions, type LoginWithPasswordOptions, type OAuth42Session, ProtectedComponent, type ProtectedComponentProps, SignInButton, type SignInButtonProps, SignOutButton, type SignOutButtonProps, type UseOAuth42SessionReturn, UserProfile, type UserProfileProps, completeAuthenticatorLogin, initiateAuthenticatorLogin, loginWithPassword, logout, logoutEverywhere, redirectToHostedAuth, simulateApproval, useOAuth42Session, useOAuth42Tokens, useOAuth42User, useRequireAuth, verifyState };
@@ -342,6 +342,6 @@ declare function logout(options?: {
342
342
  * };
343
343
  * ```
344
344
  */
345
- declare function logoutEverywhere(issuer: string, callbackUrl?: string, cookiePrefix?: string): void;
345
+ declare function logoutEverywhere(issuer: string, callbackUrl?: string): Promise<void>;
346
346
 
347
347
  export { type AuthError, AuthStatus, type AuthStatusProps, DEFAULT_HOSTED_AUTH_CONFIG, type HostedAuthConfig, type HostedAuthOptions, type LoginWithAuthenticatorOptions, type LoginWithPasswordOptions, type OAuth42Session, ProtectedComponent, type ProtectedComponentProps, SignInButton, type SignInButtonProps, SignOutButton, type SignOutButtonProps, type UseOAuth42SessionReturn, UserProfile, type UserProfileProps, completeAuthenticatorLogin, initiateAuthenticatorLogin, loginWithPassword, logout, logoutEverywhere, redirectToHostedAuth, simulateApproval, useOAuth42Session, useOAuth42Tokens, useOAuth42User, useRequireAuth, verifyState };
@@ -406,13 +406,24 @@ async function logout(options) {
406
406
  redirect: true
407
407
  });
408
408
  }
409
- function logoutEverywhere(issuer, callbackUrl, cookiePrefix) {
409
+ async function logoutEverywhere(issuer, callbackUrl) {
410
410
  const redirectUri = callbackUrl || window.location.origin;
411
- const prefixes = cookiePrefix ? [cookiePrefix] : ["oauth42-portal", "oauth42-admin", "next-auth"];
412
- for (const prefix of prefixes) {
413
- document.cookie = `${prefix}.session-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;
414
- document.cookie = `${prefix}.csrf-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;
415
- document.cookie = `${prefix}.callback-url=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;
411
+ try {
412
+ const csrfResponse = await fetch("/api/auth/csrf");
413
+ const { csrfToken } = await csrfResponse.json();
414
+ await fetch("/api/auth/signout", {
415
+ method: "POST",
416
+ headers: {
417
+ "Content-Type": "application/x-www-form-urlencoded"
418
+ },
419
+ body: new URLSearchParams({
420
+ csrfToken,
421
+ json: "true"
422
+ }),
423
+ credentials: "include"
424
+ });
425
+ } catch (err) {
426
+ console.warn("[OAuth42] Failed to call NextAuth signout:", err);
416
427
  }
417
428
  window.location.href = `${issuer}/auth/logout?redirect_uri=${encodeURIComponent(redirectUri)}`;
418
429
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/index.ts","../../src/client/hooks.ts","../../src/client/components.tsx","../../src/utils/hosted-auth.ts","../../src/client/auth.ts"],"sourcesContent":["// Client-side exports\n\n// Re-export commonly used next-auth/react functions\nexport { signIn, signOut, useSession, SessionProvider } from 'next-auth/react';\nexport type { Session } from 'next-auth';\nexport {\n useOAuth42Session,\n useOAuth42User,\n useOAuth42Tokens,\n useRequireAuth,\n} from './hooks';\n\nexport type {\n OAuth42Session,\n UseOAuth42SessionReturn,\n} from './hooks';\n\nexport {\n SignInButton,\n SignOutButton,\n UserProfile,\n AuthStatus,\n ProtectedComponent,\n} from './components';\n\nexport type {\n SignInButtonProps,\n SignOutButtonProps,\n UserProfileProps,\n AuthStatusProps,\n ProtectedComponentProps,\n} from './components';\n\n// Hosted auth utilities\nexport {\n redirectToHostedAuth,\n verifyState,\n DEFAULT_HOSTED_AUTH_CONFIG,\n} from '../utils/hosted-auth';\n\nexport type {\n HostedAuthOptions,\n HostedAuthConfig,\n} from '../utils/hosted-auth';\n\n// Custom authentication utilities\nexport {\n loginWithPassword,\n initiateAuthenticatorLogin,\n completeAuthenticatorLogin,\n simulateApproval,\n logout,\n logoutEverywhere,\n} from './auth';\n\nexport type {\n LoginWithPasswordOptions,\n LoginWithAuthenticatorOptions,\n AuthError,\n} from './auth';","import { useSession, signIn, signOut } from 'next-auth/react';\nimport { useCallback, useEffect, useState } from 'react';\n\nexport type OAuth42Session<E = {}> = ({\n user?: {\n email?: string | null;\n name?: string | null;\n image?: string | null;\n username?: string;\n emailVerified?: boolean;\n };\n accessToken?: string;\n idToken?: string;\n expires?: string;\n}) & E;\n\nexport interface UseOAuth42SessionReturn<E = {}> {\n session: OAuth42Session<E> | null;\n loading: boolean;\n error: Error | null;\n isAuthenticated: boolean;\n signIn: () => Promise<void>;\n signOut: () => Promise<void>;\n}\n\n/**\n * Hook to manage OAuth42 session with optional extra fields\n */\nexport function useOAuth42Session<E = {}>(): UseOAuth42SessionReturn<E> {\n const { data: session, status } = useSession();\n const [error, setError] = useState<Error | null>(null);\n \n const handleSignIn = useCallback(async () => {\n try {\n setError(null);\n await signIn('oauth42');\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n const handleSignOut = useCallback(async () => {\n try {\n setError(null);\n await signOut();\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n return {\n session: session as unknown as OAuth42Session<E> | null,\n loading: status === 'loading',\n error,\n isAuthenticated: status === 'authenticated',\n signIn: handleSignIn,\n signOut: handleSignOut,\n };\n}\n\n/**\n * Hook to get the current OAuth42 user\n */\nexport function useOAuth42User<E = {}>() {\n const { session, isAuthenticated } = useOAuth42Session<E>();\n \n return {\n user: isAuthenticated ? session?.user : null,\n isAuthenticated,\n };\n}\n\n/**\n * Hook to manage OAuth42 tokens\n */\nexport function useOAuth42Tokens<E = {}>() {\n const { session } = useOAuth42Session<E>();\n const [isExpired, setIsExpired] = useState(false);\n \n useEffect(() => {\n if (session?.expires) {\n const expiryTime = new Date(session.expires).getTime();\n const now = Date.now();\n setIsExpired(now >= expiryTime);\n \n // Set a timer to update expiry status\n const timeUntilExpiry = expiryTime - now;\n if (timeUntilExpiry > 0) {\n const timer = setTimeout(() => {\n setIsExpired(true);\n }, timeUntilExpiry);\n \n return () => clearTimeout(timer);\n }\n }\n }, [session?.expires]);\n \n return {\n accessToken: session?.accessToken,\n idToken: session?.idToken,\n isExpired,\n refreshToken: async () => {\n // Trigger a session refresh\n await signIn('oauth42');\n },\n };\n}\n\n/**\n * Hook for protected routes\n */\nexport function useRequireAuth(redirectTo: string = '/auth/signin') {\n const { isAuthenticated, loading } = useOAuth42Session();\n const [isRedirecting, setIsRedirecting] = useState(false);\n \n useEffect(() => {\n if (!loading && !isAuthenticated && !isRedirecting) {\n setIsRedirecting(true);\n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [isAuthenticated, loading, redirectTo, isRedirecting]);\n \n return {\n isAuthenticated,\n loading: loading || isRedirecting,\n };\n}\n","import React from 'react';\nimport { signIn, signOut } from 'next-auth/react';\nimport { useOAuth42Session, useOAuth42User } from './hooks';\n\nexport interface SignInButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign in button component\n */\nexport function SignInButton({ \n children = 'Sign in with OAuth42', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignInButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signIn('oauth42', { callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface SignOutButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign out button component\n */\nexport function SignOutButton({ \n children = 'Sign out', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignOutButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signOut({ callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface UserProfileProps {\n className?: string;\n showEmail?: boolean;\n showName?: boolean;\n showImage?: boolean;\n loadingComponent?: React.ReactNode;\n notAuthenticatedComponent?: React.ReactNode;\n}\n\n/**\n * User profile display component\n */\nexport function UserProfile({\n className = '',\n showEmail = true,\n showName = true,\n showImage = true,\n loadingComponent = <div>Loading...</div>,\n notAuthenticatedComponent = <div>Not authenticated</div>,\n}: UserProfileProps) {\n const { session, loading, isAuthenticated } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated || !session?.user) {\n return <>{notAuthenticatedComponent}</>;\n }\n \n const { user } = session;\n \n return (\n <div className={className}>\n {showImage && user.image && (\n <img \n src={user.image} \n alt={user.name || 'User'} \n style={{ width: 50, height: 50, borderRadius: '50%' }}\n />\n )}\n {showName && user.name && <div>{user.name}</div>}\n {showEmail && user.email && <div>{user.email}</div>}\n </div>\n );\n}\n\nexport interface AuthStatusProps {\n authenticatedComponent?: React.ReactNode;\n unauthenticatedComponent?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Conditional rendering based on auth status\n */\nexport function AuthStatus({\n authenticatedComponent,\n unauthenticatedComponent,\n loadingComponent = <div>Loading...</div>,\n}: AuthStatusProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n return <>{isAuthenticated ? authenticatedComponent : unauthenticatedComponent}</>;\n}\n\nexport interface ProtectedComponentProps {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Wrapper component for protected content\n */\nexport function ProtectedComponent({\n children,\n fallback = <SignInButton />,\n loadingComponent = <div>Loading...</div>,\n}: ProtectedComponentProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n \n return <>{children}</>;\n}","/**\n * Utilities for OAuth42 Hosted Authentication\n */\n\nexport interface HostedAuthOptions {\n /** OAuth2 client ID */\n clientId: string;\n /** Redirect URI after authentication */\n redirectUri: string;\n /** OAuth2 scopes (space-separated) */\n scope?: string;\n /** OAuth2 state parameter for CSRF protection */\n state?: string;\n /** Base URL for OAuth42 issuer/API (defaults to production) */\n issuer?: string;\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nfunction generateState(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Redirect to OAuth42 hosted authentication pages\n *\n * @example\n * ```ts\n * import { redirectToHostedAuth } from '@oauth42/next/client';\n *\n * function LoginButton() {\n * return (\n * <button onClick={() => redirectToHostedAuth({\n * clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID!,\n * redirectUri: `${window.location.origin}/api/auth/callback`,\n * })}>\n * Sign in with OAuth42\n * </button>\n * );\n * }\n * ```\n */\nexport function redirectToHostedAuth(options: HostedAuthOptions): void {\n const {\n clientId,\n redirectUri,\n scope = 'openid profile email',\n state = generateState(),\n issuer = 'https://api.oauth42.com',\n } = options;\n\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope,\n state,\n });\n\n // Redirect to the backend's authorize endpoint, which handles the OAuth flow\n // and redirects to hosted auth login if user is not authenticated\n const authUrl = `${issuer}/oauth2/authorize?${params.toString()}`;\n\n // Store state in sessionStorage for verification on callback\n if (typeof window !== 'undefined') {\n sessionStorage.setItem('oauth42_state', state);\n window.location.href = authUrl;\n }\n}\n\n/**\n * Verify state parameter on OAuth2 callback\n * Call this in your callback page to verify the state matches\n *\n * @example\n * ```ts\n * import { verifyState } from '@oauth42/next/client';\n *\n * export default function CallbackPage() {\n * const searchParams = useSearchParams();\n * const state = searchParams.get('state');\n *\n * if (!verifyState(state)) {\n * return <div>Invalid state parameter</div>;\n * }\n *\n * // Continue with token exchange...\n * }\n * ```\n */\nexport function verifyState(state: string | null): boolean {\n if (typeof window === 'undefined') return false;\n if (!state) return false;\n\n const storedState = sessionStorage.getItem('oauth42_state');\n sessionStorage.removeItem('oauth42_state');\n\n return storedState === state;\n}\n\n/**\n * Configuration for hosted authentication\n */\nexport interface HostedAuthConfig {\n /** Enable hosted authentication */\n enabled: boolean;\n /** Base URL for hosted auth pages (optional, defaults to production) */\n baseUrl?: string;\n /** Feature flags for hosted auth */\n features?: {\n /** Allow user signup */\n signup?: boolean;\n /** Allow social login */\n socialLogin?: boolean;\n /** Allow password reset */\n passwordReset?: boolean;\n };\n}\n\n/**\n * Default hosted auth configuration\n */\nexport const DEFAULT_HOSTED_AUTH_CONFIG: HostedAuthConfig = {\n enabled: true,\n baseUrl: 'https://auth.oauth42.com',\n features: {\n signup: true,\n socialLogin: false,\n passwordReset: true,\n },\n};\n","/**\n * OAuth42 Custom Authentication Utilities\n *\n * Provides functions for implementing custom login UIs in customer apps\n * while properly handling OAuth2 PKCE flows and next-auth integration.\n */\n\nimport { signIn } from 'next-auth/react';\n\n// PKCE utilities\nfunction base64URLEncode(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '');\n}\n\nasync function generateCodeVerifier(): Promise<string> {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64URLEncode(array.buffer);\n}\n\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return base64URLEncode(hash);\n}\n\nfunction generateState(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64URLEncode(array.buffer);\n}\n\nexport interface LoginWithPasswordOptions {\n email: string;\n password: string;\n mfaCode?: string;\n /**\n * URL to redirect to after successful authentication.\n * If not provided, will redirect to '/'\n */\n callbackUrl?: string;\n}\n\nexport interface LoginWithAuthenticatorOptions {\n email: string;\n sessionId: string;\n /**\n * URL to redirect to after successful authentication.\n * If not provided, will redirect to '/'\n */\n callbackUrl?: string;\n}\n\nexport interface AuthError {\n error: string;\n error_description?: string;\n requires_enrollment?: boolean;\n enrollment_token?: string;\n requires_mfa?: boolean;\n}\n\n/**\n * Authenticate with email/password and complete OAuth PKCE flow\n *\n * This function handles the full authentication flow:\n * 1. Authenticates with the backend using credentials\n * 2. Uses the access token to authorize the OAuth client\n * 3. Exchanges authorization code for tokens via next-auth\n *\n * @example\n * ```tsx\n * const result = await loginWithPassword({\n * email: 'user@example.com',\n * password: 'password123',\n * callbackUrl: '/dashboard'\n * });\n *\n * if (result.success) {\n * // User is authenticated, next-auth session is set\n * } else if (result.requires_mfa) {\n * // Prompt for MFA code and call again with mfaCode\n * }\n * ```\n */\nexport async function loginWithPassword(\n options: LoginWithPasswordOptions\n): Promise<{ success: boolean; access_token?: string } & Partial<AuthError>> {\n try {\n // Step 1: Authenticate with backend to get access token\n const loginResponse = await fetch('/api/auth/login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email: options.email,\n password: options.password,\n ...(options.mfaCode ? { mfa_code: options.mfaCode } : {}),\n }),\n });\n\n if (!loginResponse.ok) {\n const errorData = await loginResponse.json().catch(() => ({}));\n const desc: string = errorData?.error_description || errorData?.message || '';\n\n // Handle special cases\n if (loginResponse.status === 403 && errorData?.requires_enrollment) {\n return {\n success: false,\n error: 'enrollment_required',\n error_description: 'MFA enrollment required',\n requires_enrollment: true,\n enrollment_token: errorData.enrollment_token,\n };\n }\n\n if (loginResponse.status === 403 && /mfa required/i.test(desc)) {\n return {\n success: false,\n error: 'mfa_required',\n error_description: 'MFA code required',\n requires_mfa: true,\n };\n }\n\n if (loginResponse.status === 401 && /invalid mfa code/i.test(desc)) {\n return {\n success: false,\n error: 'invalid_mfa_code',\n error_description: 'Invalid MFA code',\n requires_mfa: true,\n };\n }\n\n return {\n success: false,\n error: 'authentication_failed',\n error_description: desc || 'Invalid credentials',\n };\n }\n\n const { access_token } = await loginResponse.json();\n\n // Return access token for the application to use in OAuth authorize flow\n return { success: true, access_token };\n } catch (error) {\n console.error('Login error:', error);\n return {\n success: false,\n error: 'network_error',\n error_description: 'Failed to connect to authentication server',\n };\n }\n}\n\n/**\n * Initiate authenticator (passwordless) login and return challenge code\n *\n * @example\n * ```tsx\n * const result = await initiateAuthenticatorLogin({\n * email: 'user@example.com',\n * sessionId: crypto.randomUUID()\n * });\n *\n * if (result.success) {\n * // Display result.challengeCode to user\n * // Wait for WebSocket approval or poll for completion\n * }\n * ```\n */\nexport async function initiateAuthenticatorLogin(\n options: LoginWithAuthenticatorOptions\n): Promise<{\n success: boolean;\n challengeCode?: string;\n challengeId?: string;\n} & Partial<AuthError>> {\n try {\n const response = await fetch('/api/auth/passwordless/initiate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email: options.email,\n session_id: options.sessionId,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n return {\n success: false,\n error: 'initiation_failed',\n error_description: errorData.error_description || 'Failed to create challenge',\n };\n }\n\n const data = await response.json();\n\n return {\n success: true,\n challengeCode: data.challenge_code,\n challengeId: data.challenge_id,\n };\n } catch (error) {\n console.error('Authenticator login error:', error);\n return {\n success: false,\n error: 'network_error',\n error_description: 'Failed to connect to authentication server',\n };\n }\n}\n\n/**\n * Complete authenticator login after approval\n *\n * Call this after receiving approval notification via WebSocket\n * or after the user has approved on their mobile device.\n * Returns the access token for the application to use in OAuth authorize flow.\n *\n * @param accessToken - Access token received from approval payload\n */\nexport async function completeAuthenticatorLogin(\n accessToken: string\n): Promise<{ success: boolean; access_token: string }> {\n return { success: true, access_token: accessToken };\n}\n\n/**\n * Simulate approval for testing (development only)\n *\n * @param challengeId - The challenge ID to approve\n * @param selectedCode - The code that was displayed to the user\n */\nexport async function simulateApproval(\n challengeId: string,\n selectedCode: string\n): Promise<{ success: boolean }> {\n try {\n await fetch('/api/auth/challenge/approve', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n challenge_id: challengeId,\n selected_code: selectedCode,\n }),\n });\n\n return { success: true };\n } catch (error) {\n console.error('Simulated approval failed:', error);\n return { success: false };\n }\n}\n\n/**\n * Logout the current user from the app only (app-level logout)\n *\n * This clears the next-auth session for this app AND removes the app-user\n * mapping from the session registry. On next login, the user will see\n * the account picker to choose which account to use.\n *\n * For provider-level logout (sign out of all apps), use logoutEverywhere().\n *\n * @param options.callbackUrl - URL to redirect to after logout (default: '/')\n * @param options.clientId - OAuth client ID to clear from registry\n * @param options.issuer - OAuth42 issuer URL for the logout endpoint\n *\n * @example\n * ```tsx\n * import { logout } from '@oauth42/next/client';\n *\n * const handleLogout = async () => {\n * await logout({\n * callbackUrl: '/login',\n * clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID,\n * issuer: process.env.NEXT_PUBLIC_OAUTH_ISSUER || 'https://localhost:8443'\n * });\n * };\n * ```\n */\nexport async function logout(options?: {\n callbackUrl?: string;\n clientId?: string;\n issuer?: string;\n}): Promise<void> {\n // If clientId and issuer provided, call backend to clear app_user from registry\n if (options?.clientId && options?.issuer) {\n try {\n await fetch(`${options.issuer}/oauth2/logout?client_id=${encodeURIComponent(options.clientId)}`, {\n method: 'POST',\n credentials: 'include', // Include cookies (registry cookie)\n });\n } catch (err) {\n console.warn('[OAuth42] Failed to clear app user from registry:', err);\n // Continue with NextAuth signOut even if this fails\n }\n }\n\n // Import signOut from next-auth/react\n const { signOut } = await import('next-auth/react');\n\n // Clear next-auth session (app-level only)\n await signOut({\n callbackUrl: options?.callbackUrl || '/',\n redirect: true\n });\n}\n\n/**\n * Logout the current user from ALL apps using OAuth42 (provider-level logout)\n *\n * This redirects to the OAuth42 provider's logout endpoint to clear the\n * oauth42_session cookie, effectively logging the user out of all apps.\n *\n * Use this when testing fresh login flows or when the user explicitly\n * wants to sign out of everything.\n *\n * @param issuer - The OAuth42 issuer URL (e.g., 'https://localhost:8443')\n * @param callbackUrl - URL to redirect to after logout (default: current origin)\n *\n * @example\n * ```tsx\n * import { logoutEverywhere } from '@oauth42/next/client';\n *\n * const handleLogoutEverywhere = () => {\n * const issuer = process.env.NEXT_PUBLIC_OAUTH42_ISSUER || 'https://localhost:8443';\n * logoutEverywhere(issuer, '/auth/signin');\n * };\n * ```\n */\nexport function logoutEverywhere(issuer: string, callbackUrl?: string, cookiePrefix?: string): void {\n const redirectUri = callbackUrl || window.location.origin;\n\n // Clear NextAuth session cookies before redirect\n // This ensures the local app session is cleared even if the redirect happens fast\n // Cookie prefix defaults to common patterns, but can be overridden\n const prefixes = cookiePrefix\n ? [cookiePrefix]\n : ['oauth42-portal', 'oauth42-admin', 'next-auth'];\n\n for (const prefix of prefixes) {\n document.cookie = `${prefix}.session-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;\n document.cookie = `${prefix}.csrf-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;\n document.cookie = `${prefix}.callback-url=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;\n }\n\n // Redirect to OAuth42 provider logout endpoint\n // This clears the oauth42_session cookie and redirects back\n window.location.href = `${issuer}/auth/logout?redirect_uri=${encodeURIComponent(redirectUri)}`;\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","signOut"]}
1
+ {"version":3,"sources":["../../src/client/index.ts","../../src/client/hooks.ts","../../src/client/components.tsx","../../src/utils/hosted-auth.ts","../../src/client/auth.ts"],"sourcesContent":["// Client-side exports\n\n// Re-export commonly used next-auth/react functions\nexport { signIn, signOut, useSession, SessionProvider } from 'next-auth/react';\nexport type { Session } from 'next-auth';\nexport {\n useOAuth42Session,\n useOAuth42User,\n useOAuth42Tokens,\n useRequireAuth,\n} from './hooks';\n\nexport type {\n OAuth42Session,\n UseOAuth42SessionReturn,\n} from './hooks';\n\nexport {\n SignInButton,\n SignOutButton,\n UserProfile,\n AuthStatus,\n ProtectedComponent,\n} from './components';\n\nexport type {\n SignInButtonProps,\n SignOutButtonProps,\n UserProfileProps,\n AuthStatusProps,\n ProtectedComponentProps,\n} from './components';\n\n// Hosted auth utilities\nexport {\n redirectToHostedAuth,\n verifyState,\n DEFAULT_HOSTED_AUTH_CONFIG,\n} from '../utils/hosted-auth';\n\nexport type {\n HostedAuthOptions,\n HostedAuthConfig,\n} from '../utils/hosted-auth';\n\n// Custom authentication utilities\nexport {\n loginWithPassword,\n initiateAuthenticatorLogin,\n completeAuthenticatorLogin,\n simulateApproval,\n logout,\n logoutEverywhere,\n} from './auth';\n\nexport type {\n LoginWithPasswordOptions,\n LoginWithAuthenticatorOptions,\n AuthError,\n} from './auth';","import { useSession, signIn, signOut } from 'next-auth/react';\nimport { useCallback, useEffect, useState } from 'react';\n\nexport type OAuth42Session<E = {}> = ({\n user?: {\n email?: string | null;\n name?: string | null;\n image?: string | null;\n username?: string;\n emailVerified?: boolean;\n };\n accessToken?: string;\n idToken?: string;\n expires?: string;\n}) & E;\n\nexport interface UseOAuth42SessionReturn<E = {}> {\n session: OAuth42Session<E> | null;\n loading: boolean;\n error: Error | null;\n isAuthenticated: boolean;\n signIn: () => Promise<void>;\n signOut: () => Promise<void>;\n}\n\n/**\n * Hook to manage OAuth42 session with optional extra fields\n */\nexport function useOAuth42Session<E = {}>(): UseOAuth42SessionReturn<E> {\n const { data: session, status } = useSession();\n const [error, setError] = useState<Error | null>(null);\n \n const handleSignIn = useCallback(async () => {\n try {\n setError(null);\n await signIn('oauth42');\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n const handleSignOut = useCallback(async () => {\n try {\n setError(null);\n await signOut();\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n return {\n session: session as unknown as OAuth42Session<E> | null,\n loading: status === 'loading',\n error,\n isAuthenticated: status === 'authenticated',\n signIn: handleSignIn,\n signOut: handleSignOut,\n };\n}\n\n/**\n * Hook to get the current OAuth42 user\n */\nexport function useOAuth42User<E = {}>() {\n const { session, isAuthenticated } = useOAuth42Session<E>();\n \n return {\n user: isAuthenticated ? session?.user : null,\n isAuthenticated,\n };\n}\n\n/**\n * Hook to manage OAuth42 tokens\n */\nexport function useOAuth42Tokens<E = {}>() {\n const { session } = useOAuth42Session<E>();\n const [isExpired, setIsExpired] = useState(false);\n \n useEffect(() => {\n if (session?.expires) {\n const expiryTime = new Date(session.expires).getTime();\n const now = Date.now();\n setIsExpired(now >= expiryTime);\n \n // Set a timer to update expiry status\n const timeUntilExpiry = expiryTime - now;\n if (timeUntilExpiry > 0) {\n const timer = setTimeout(() => {\n setIsExpired(true);\n }, timeUntilExpiry);\n \n return () => clearTimeout(timer);\n }\n }\n }, [session?.expires]);\n \n return {\n accessToken: session?.accessToken,\n idToken: session?.idToken,\n isExpired,\n refreshToken: async () => {\n // Trigger a session refresh\n await signIn('oauth42');\n },\n };\n}\n\n/**\n * Hook for protected routes\n */\nexport function useRequireAuth(redirectTo: string = '/auth/signin') {\n const { isAuthenticated, loading } = useOAuth42Session();\n const [isRedirecting, setIsRedirecting] = useState(false);\n \n useEffect(() => {\n if (!loading && !isAuthenticated && !isRedirecting) {\n setIsRedirecting(true);\n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [isAuthenticated, loading, redirectTo, isRedirecting]);\n \n return {\n isAuthenticated,\n loading: loading || isRedirecting,\n };\n}\n","import React from 'react';\nimport { signIn, signOut } from 'next-auth/react';\nimport { useOAuth42Session, useOAuth42User } from './hooks';\n\nexport interface SignInButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign in button component\n */\nexport function SignInButton({ \n children = 'Sign in with OAuth42', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignInButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signIn('oauth42', { callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface SignOutButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign out button component\n */\nexport function SignOutButton({ \n children = 'Sign out', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignOutButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signOut({ callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface UserProfileProps {\n className?: string;\n showEmail?: boolean;\n showName?: boolean;\n showImage?: boolean;\n loadingComponent?: React.ReactNode;\n notAuthenticatedComponent?: React.ReactNode;\n}\n\n/**\n * User profile display component\n */\nexport function UserProfile({\n className = '',\n showEmail = true,\n showName = true,\n showImage = true,\n loadingComponent = <div>Loading...</div>,\n notAuthenticatedComponent = <div>Not authenticated</div>,\n}: UserProfileProps) {\n const { session, loading, isAuthenticated } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated || !session?.user) {\n return <>{notAuthenticatedComponent}</>;\n }\n \n const { user } = session;\n \n return (\n <div className={className}>\n {showImage && user.image && (\n <img \n src={user.image} \n alt={user.name || 'User'} \n style={{ width: 50, height: 50, borderRadius: '50%' }}\n />\n )}\n {showName && user.name && <div>{user.name}</div>}\n {showEmail && user.email && <div>{user.email}</div>}\n </div>\n );\n}\n\nexport interface AuthStatusProps {\n authenticatedComponent?: React.ReactNode;\n unauthenticatedComponent?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Conditional rendering based on auth status\n */\nexport function AuthStatus({\n authenticatedComponent,\n unauthenticatedComponent,\n loadingComponent = <div>Loading...</div>,\n}: AuthStatusProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n return <>{isAuthenticated ? authenticatedComponent : unauthenticatedComponent}</>;\n}\n\nexport interface ProtectedComponentProps {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Wrapper component for protected content\n */\nexport function ProtectedComponent({\n children,\n fallback = <SignInButton />,\n loadingComponent = <div>Loading...</div>,\n}: ProtectedComponentProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n \n return <>{children}</>;\n}","/**\n * Utilities for OAuth42 Hosted Authentication\n */\n\nexport interface HostedAuthOptions {\n /** OAuth2 client ID */\n clientId: string;\n /** Redirect URI after authentication */\n redirectUri: string;\n /** OAuth2 scopes (space-separated) */\n scope?: string;\n /** OAuth2 state parameter for CSRF protection */\n state?: string;\n /** Base URL for OAuth42 issuer/API (defaults to production) */\n issuer?: string;\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nfunction generateState(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Redirect to OAuth42 hosted authentication pages\n *\n * @example\n * ```ts\n * import { redirectToHostedAuth } from '@oauth42/next/client';\n *\n * function LoginButton() {\n * return (\n * <button onClick={() => redirectToHostedAuth({\n * clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID!,\n * redirectUri: `${window.location.origin}/api/auth/callback`,\n * })}>\n * Sign in with OAuth42\n * </button>\n * );\n * }\n * ```\n */\nexport function redirectToHostedAuth(options: HostedAuthOptions): void {\n const {\n clientId,\n redirectUri,\n scope = 'openid profile email',\n state = generateState(),\n issuer = 'https://api.oauth42.com',\n } = options;\n\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope,\n state,\n });\n\n // Redirect to the backend's authorize endpoint, which handles the OAuth flow\n // and redirects to hosted auth login if user is not authenticated\n const authUrl = `${issuer}/oauth2/authorize?${params.toString()}`;\n\n // Store state in sessionStorage for verification on callback\n if (typeof window !== 'undefined') {\n sessionStorage.setItem('oauth42_state', state);\n window.location.href = authUrl;\n }\n}\n\n/**\n * Verify state parameter on OAuth2 callback\n * Call this in your callback page to verify the state matches\n *\n * @example\n * ```ts\n * import { verifyState } from '@oauth42/next/client';\n *\n * export default function CallbackPage() {\n * const searchParams = useSearchParams();\n * const state = searchParams.get('state');\n *\n * if (!verifyState(state)) {\n * return <div>Invalid state parameter</div>;\n * }\n *\n * // Continue with token exchange...\n * }\n * ```\n */\nexport function verifyState(state: string | null): boolean {\n if (typeof window === 'undefined') return false;\n if (!state) return false;\n\n const storedState = sessionStorage.getItem('oauth42_state');\n sessionStorage.removeItem('oauth42_state');\n\n return storedState === state;\n}\n\n/**\n * Configuration for hosted authentication\n */\nexport interface HostedAuthConfig {\n /** Enable hosted authentication */\n enabled: boolean;\n /** Base URL for hosted auth pages (optional, defaults to production) */\n baseUrl?: string;\n /** Feature flags for hosted auth */\n features?: {\n /** Allow user signup */\n signup?: boolean;\n /** Allow social login */\n socialLogin?: boolean;\n /** Allow password reset */\n passwordReset?: boolean;\n };\n}\n\n/**\n * Default hosted auth configuration\n */\nexport const DEFAULT_HOSTED_AUTH_CONFIG: HostedAuthConfig = {\n enabled: true,\n baseUrl: 'https://auth.oauth42.com',\n features: {\n signup: true,\n socialLogin: false,\n passwordReset: true,\n },\n};\n","/**\n * OAuth42 Custom Authentication Utilities\n *\n * Provides functions for implementing custom login UIs in customer apps\n * while properly handling OAuth2 PKCE flows and next-auth integration.\n */\n\nimport { signIn } from 'next-auth/react';\n\n// PKCE utilities\nfunction base64URLEncode(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '');\n}\n\nasync function generateCodeVerifier(): Promise<string> {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64URLEncode(array.buffer);\n}\n\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return base64URLEncode(hash);\n}\n\nfunction generateState(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64URLEncode(array.buffer);\n}\n\nexport interface LoginWithPasswordOptions {\n email: string;\n password: string;\n mfaCode?: string;\n /**\n * URL to redirect to after successful authentication.\n * If not provided, will redirect to '/'\n */\n callbackUrl?: string;\n}\n\nexport interface LoginWithAuthenticatorOptions {\n email: string;\n sessionId: string;\n /**\n * URL to redirect to after successful authentication.\n * If not provided, will redirect to '/'\n */\n callbackUrl?: string;\n}\n\nexport interface AuthError {\n error: string;\n error_description?: string;\n requires_enrollment?: boolean;\n enrollment_token?: string;\n requires_mfa?: boolean;\n}\n\n/**\n * Authenticate with email/password and complete OAuth PKCE flow\n *\n * This function handles the full authentication flow:\n * 1. Authenticates with the backend using credentials\n * 2. Uses the access token to authorize the OAuth client\n * 3. Exchanges authorization code for tokens via next-auth\n *\n * @example\n * ```tsx\n * const result = await loginWithPassword({\n * email: 'user@example.com',\n * password: 'password123',\n * callbackUrl: '/dashboard'\n * });\n *\n * if (result.success) {\n * // User is authenticated, next-auth session is set\n * } else if (result.requires_mfa) {\n * // Prompt for MFA code and call again with mfaCode\n * }\n * ```\n */\nexport async function loginWithPassword(\n options: LoginWithPasswordOptions\n): Promise<{ success: boolean; access_token?: string } & Partial<AuthError>> {\n try {\n // Step 1: Authenticate with backend to get access token\n const loginResponse = await fetch('/api/auth/login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email: options.email,\n password: options.password,\n ...(options.mfaCode ? { mfa_code: options.mfaCode } : {}),\n }),\n });\n\n if (!loginResponse.ok) {\n const errorData = await loginResponse.json().catch(() => ({}));\n const desc: string = errorData?.error_description || errorData?.message || '';\n\n // Handle special cases\n if (loginResponse.status === 403 && errorData?.requires_enrollment) {\n return {\n success: false,\n error: 'enrollment_required',\n error_description: 'MFA enrollment required',\n requires_enrollment: true,\n enrollment_token: errorData.enrollment_token,\n };\n }\n\n if (loginResponse.status === 403 && /mfa required/i.test(desc)) {\n return {\n success: false,\n error: 'mfa_required',\n error_description: 'MFA code required',\n requires_mfa: true,\n };\n }\n\n if (loginResponse.status === 401 && /invalid mfa code/i.test(desc)) {\n return {\n success: false,\n error: 'invalid_mfa_code',\n error_description: 'Invalid MFA code',\n requires_mfa: true,\n };\n }\n\n return {\n success: false,\n error: 'authentication_failed',\n error_description: desc || 'Invalid credentials',\n };\n }\n\n const { access_token } = await loginResponse.json();\n\n // Return access token for the application to use in OAuth authorize flow\n return { success: true, access_token };\n } catch (error) {\n console.error('Login error:', error);\n return {\n success: false,\n error: 'network_error',\n error_description: 'Failed to connect to authentication server',\n };\n }\n}\n\n/**\n * Initiate authenticator (passwordless) login and return challenge code\n *\n * @example\n * ```tsx\n * const result = await initiateAuthenticatorLogin({\n * email: 'user@example.com',\n * sessionId: crypto.randomUUID()\n * });\n *\n * if (result.success) {\n * // Display result.challengeCode to user\n * // Wait for WebSocket approval or poll for completion\n * }\n * ```\n */\nexport async function initiateAuthenticatorLogin(\n options: LoginWithAuthenticatorOptions\n): Promise<{\n success: boolean;\n challengeCode?: string;\n challengeId?: string;\n} & Partial<AuthError>> {\n try {\n const response = await fetch('/api/auth/passwordless/initiate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email: options.email,\n session_id: options.sessionId,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n return {\n success: false,\n error: 'initiation_failed',\n error_description: errorData.error_description || 'Failed to create challenge',\n };\n }\n\n const data = await response.json();\n\n return {\n success: true,\n challengeCode: data.challenge_code,\n challengeId: data.challenge_id,\n };\n } catch (error) {\n console.error('Authenticator login error:', error);\n return {\n success: false,\n error: 'network_error',\n error_description: 'Failed to connect to authentication server',\n };\n }\n}\n\n/**\n * Complete authenticator login after approval\n *\n * Call this after receiving approval notification via WebSocket\n * or after the user has approved on their mobile device.\n * Returns the access token for the application to use in OAuth authorize flow.\n *\n * @param accessToken - Access token received from approval payload\n */\nexport async function completeAuthenticatorLogin(\n accessToken: string\n): Promise<{ success: boolean; access_token: string }> {\n return { success: true, access_token: accessToken };\n}\n\n/**\n * Simulate approval for testing (development only)\n *\n * @param challengeId - The challenge ID to approve\n * @param selectedCode - The code that was displayed to the user\n */\nexport async function simulateApproval(\n challengeId: string,\n selectedCode: string\n): Promise<{ success: boolean }> {\n try {\n await fetch('/api/auth/challenge/approve', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n challenge_id: challengeId,\n selected_code: selectedCode,\n }),\n });\n\n return { success: true };\n } catch (error) {\n console.error('Simulated approval failed:', error);\n return { success: false };\n }\n}\n\n/**\n * Logout the current user from the app only (app-level logout)\n *\n * This clears the next-auth session for this app AND removes the app-user\n * mapping from the session registry. On next login, the user will see\n * the account picker to choose which account to use.\n *\n * For provider-level logout (sign out of all apps), use logoutEverywhere().\n *\n * @param options.callbackUrl - URL to redirect to after logout (default: '/')\n * @param options.clientId - OAuth client ID to clear from registry\n * @param options.issuer - OAuth42 issuer URL for the logout endpoint\n *\n * @example\n * ```tsx\n * import { logout } from '@oauth42/next/client';\n *\n * const handleLogout = async () => {\n * await logout({\n * callbackUrl: '/login',\n * clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID,\n * issuer: process.env.NEXT_PUBLIC_OAUTH_ISSUER || 'https://localhost:8443'\n * });\n * };\n * ```\n */\nexport async function logout(options?: {\n callbackUrl?: string;\n clientId?: string;\n issuer?: string;\n}): Promise<void> {\n // If clientId and issuer provided, call backend to clear app_user from registry\n if (options?.clientId && options?.issuer) {\n try {\n await fetch(`${options.issuer}/oauth2/logout?client_id=${encodeURIComponent(options.clientId)}`, {\n method: 'POST',\n credentials: 'include', // Include cookies (registry cookie)\n });\n } catch (err) {\n console.warn('[OAuth42] Failed to clear app user from registry:', err);\n // Continue with NextAuth signOut even if this fails\n }\n }\n\n // Import signOut from next-auth/react\n const { signOut } = await import('next-auth/react');\n\n // Clear next-auth session (app-level only)\n await signOut({\n callbackUrl: options?.callbackUrl || '/',\n redirect: true\n });\n}\n\n/**\n * Logout the current user from ALL apps using OAuth42 (provider-level logout)\n *\n * This redirects to the OAuth42 provider's logout endpoint to clear the\n * oauth42_session cookie, effectively logging the user out of all apps.\n *\n * Use this when testing fresh login flows or when the user explicitly\n * wants to sign out of everything.\n *\n * @param issuer - The OAuth42 issuer URL (e.g., 'https://localhost:8443')\n * @param callbackUrl - URL to redirect to after logout (default: current origin)\n *\n * @example\n * ```tsx\n * import { logoutEverywhere } from '@oauth42/next/client';\n *\n * const handleLogoutEverywhere = () => {\n * const issuer = process.env.NEXT_PUBLIC_OAUTH42_ISSUER || 'https://localhost:8443';\n * logoutEverywhere(issuer, '/auth/signin');\n * };\n * ```\n */\nexport async function logoutEverywhere(issuer: string, callbackUrl?: string): Promise<void> {\n const redirectUri = callbackUrl || window.location.origin;\n\n // First, call NextAuth's signOut API to properly clear session cookies\n // This uses NextAuth's built-in cookie management which handles all the\n // cookie attributes (domain, path, secure, httpOnly) correctly\n try {\n // Get CSRF token from NextAuth\n const csrfResponse = await fetch('/api/auth/csrf');\n const { csrfToken } = await csrfResponse.json();\n\n // Call NextAuth signOut endpoint to clear session\n await fetch('/api/auth/signout', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n csrfToken,\n json: 'true',\n }),\n credentials: 'include',\n });\n } catch (err) {\n console.warn('[OAuth42] Failed to call NextAuth signout:', err);\n // Continue with redirect even if this fails\n }\n\n // Then redirect to OAuth42 provider logout endpoint\n // This clears the oauth42_session cookie and redirects back\n window.location.href = `${issuer}/auth/logout?redirect_uri=${encodeURIComponent(redirectUri)}`;\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","signOut"]}
@@ -351,13 +351,24 @@ async function logout(options) {
351
351
  redirect: true
352
352
  });
353
353
  }
354
- function logoutEverywhere(issuer, callbackUrl, cookiePrefix) {
354
+ async function logoutEverywhere(issuer, callbackUrl) {
355
355
  const redirectUri = callbackUrl || window.location.origin;
356
- const prefixes = cookiePrefix ? [cookiePrefix] : ["oauth42-portal", "oauth42-admin", "next-auth"];
357
- for (const prefix of prefixes) {
358
- document.cookie = `${prefix}.session-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;
359
- document.cookie = `${prefix}.csrf-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;
360
- document.cookie = `${prefix}.callback-url=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;
356
+ try {
357
+ const csrfResponse = await fetch("/api/auth/csrf");
358
+ const { csrfToken } = await csrfResponse.json();
359
+ await fetch("/api/auth/signout", {
360
+ method: "POST",
361
+ headers: {
362
+ "Content-Type": "application/x-www-form-urlencoded"
363
+ },
364
+ body: new URLSearchParams({
365
+ csrfToken,
366
+ json: "true"
367
+ }),
368
+ credentials: "include"
369
+ });
370
+ } catch (err) {
371
+ console.warn("[OAuth42] Failed to call NextAuth signout:", err);
361
372
  }
362
373
  window.location.href = `${issuer}/auth/logout?redirect_uri=${encodeURIComponent(redirectUri)}`;
363
374
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/index.ts","../../src/client/hooks.ts","../../src/client/components.tsx","../../src/utils/hosted-auth.ts","../../src/client/auth.ts"],"sourcesContent":["// Client-side exports\n\n// Re-export commonly used next-auth/react functions\nexport { signIn, signOut, useSession, SessionProvider } from 'next-auth/react';\nexport type { Session } from 'next-auth';\nexport {\n useOAuth42Session,\n useOAuth42User,\n useOAuth42Tokens,\n useRequireAuth,\n} from './hooks';\n\nexport type {\n OAuth42Session,\n UseOAuth42SessionReturn,\n} from './hooks';\n\nexport {\n SignInButton,\n SignOutButton,\n UserProfile,\n AuthStatus,\n ProtectedComponent,\n} from './components';\n\nexport type {\n SignInButtonProps,\n SignOutButtonProps,\n UserProfileProps,\n AuthStatusProps,\n ProtectedComponentProps,\n} from './components';\n\n// Hosted auth utilities\nexport {\n redirectToHostedAuth,\n verifyState,\n DEFAULT_HOSTED_AUTH_CONFIG,\n} from '../utils/hosted-auth';\n\nexport type {\n HostedAuthOptions,\n HostedAuthConfig,\n} from '../utils/hosted-auth';\n\n// Custom authentication utilities\nexport {\n loginWithPassword,\n initiateAuthenticatorLogin,\n completeAuthenticatorLogin,\n simulateApproval,\n logout,\n logoutEverywhere,\n} from './auth';\n\nexport type {\n LoginWithPasswordOptions,\n LoginWithAuthenticatorOptions,\n AuthError,\n} from './auth';","import { useSession, signIn, signOut } from 'next-auth/react';\nimport { useCallback, useEffect, useState } from 'react';\n\nexport type OAuth42Session<E = {}> = ({\n user?: {\n email?: string | null;\n name?: string | null;\n image?: string | null;\n username?: string;\n emailVerified?: boolean;\n };\n accessToken?: string;\n idToken?: string;\n expires?: string;\n}) & E;\n\nexport interface UseOAuth42SessionReturn<E = {}> {\n session: OAuth42Session<E> | null;\n loading: boolean;\n error: Error | null;\n isAuthenticated: boolean;\n signIn: () => Promise<void>;\n signOut: () => Promise<void>;\n}\n\n/**\n * Hook to manage OAuth42 session with optional extra fields\n */\nexport function useOAuth42Session<E = {}>(): UseOAuth42SessionReturn<E> {\n const { data: session, status } = useSession();\n const [error, setError] = useState<Error | null>(null);\n \n const handleSignIn = useCallback(async () => {\n try {\n setError(null);\n await signIn('oauth42');\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n const handleSignOut = useCallback(async () => {\n try {\n setError(null);\n await signOut();\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n return {\n session: session as unknown as OAuth42Session<E> | null,\n loading: status === 'loading',\n error,\n isAuthenticated: status === 'authenticated',\n signIn: handleSignIn,\n signOut: handleSignOut,\n };\n}\n\n/**\n * Hook to get the current OAuth42 user\n */\nexport function useOAuth42User<E = {}>() {\n const { session, isAuthenticated } = useOAuth42Session<E>();\n \n return {\n user: isAuthenticated ? session?.user : null,\n isAuthenticated,\n };\n}\n\n/**\n * Hook to manage OAuth42 tokens\n */\nexport function useOAuth42Tokens<E = {}>() {\n const { session } = useOAuth42Session<E>();\n const [isExpired, setIsExpired] = useState(false);\n \n useEffect(() => {\n if (session?.expires) {\n const expiryTime = new Date(session.expires).getTime();\n const now = Date.now();\n setIsExpired(now >= expiryTime);\n \n // Set a timer to update expiry status\n const timeUntilExpiry = expiryTime - now;\n if (timeUntilExpiry > 0) {\n const timer = setTimeout(() => {\n setIsExpired(true);\n }, timeUntilExpiry);\n \n return () => clearTimeout(timer);\n }\n }\n }, [session?.expires]);\n \n return {\n accessToken: session?.accessToken,\n idToken: session?.idToken,\n isExpired,\n refreshToken: async () => {\n // Trigger a session refresh\n await signIn('oauth42');\n },\n };\n}\n\n/**\n * Hook for protected routes\n */\nexport function useRequireAuth(redirectTo: string = '/auth/signin') {\n const { isAuthenticated, loading } = useOAuth42Session();\n const [isRedirecting, setIsRedirecting] = useState(false);\n \n useEffect(() => {\n if (!loading && !isAuthenticated && !isRedirecting) {\n setIsRedirecting(true);\n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [isAuthenticated, loading, redirectTo, isRedirecting]);\n \n return {\n isAuthenticated,\n loading: loading || isRedirecting,\n };\n}\n","import React from 'react';\nimport { signIn, signOut } from 'next-auth/react';\nimport { useOAuth42Session, useOAuth42User } from './hooks';\n\nexport interface SignInButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign in button component\n */\nexport function SignInButton({ \n children = 'Sign in with OAuth42', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignInButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signIn('oauth42', { callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface SignOutButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign out button component\n */\nexport function SignOutButton({ \n children = 'Sign out', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignOutButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signOut({ callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface UserProfileProps {\n className?: string;\n showEmail?: boolean;\n showName?: boolean;\n showImage?: boolean;\n loadingComponent?: React.ReactNode;\n notAuthenticatedComponent?: React.ReactNode;\n}\n\n/**\n * User profile display component\n */\nexport function UserProfile({\n className = '',\n showEmail = true,\n showName = true,\n showImage = true,\n loadingComponent = <div>Loading...</div>,\n notAuthenticatedComponent = <div>Not authenticated</div>,\n}: UserProfileProps) {\n const { session, loading, isAuthenticated } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated || !session?.user) {\n return <>{notAuthenticatedComponent}</>;\n }\n \n const { user } = session;\n \n return (\n <div className={className}>\n {showImage && user.image && (\n <img \n src={user.image} \n alt={user.name || 'User'} \n style={{ width: 50, height: 50, borderRadius: '50%' }}\n />\n )}\n {showName && user.name && <div>{user.name}</div>}\n {showEmail && user.email && <div>{user.email}</div>}\n </div>\n );\n}\n\nexport interface AuthStatusProps {\n authenticatedComponent?: React.ReactNode;\n unauthenticatedComponent?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Conditional rendering based on auth status\n */\nexport function AuthStatus({\n authenticatedComponent,\n unauthenticatedComponent,\n loadingComponent = <div>Loading...</div>,\n}: AuthStatusProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n return <>{isAuthenticated ? authenticatedComponent : unauthenticatedComponent}</>;\n}\n\nexport interface ProtectedComponentProps {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Wrapper component for protected content\n */\nexport function ProtectedComponent({\n children,\n fallback = <SignInButton />,\n loadingComponent = <div>Loading...</div>,\n}: ProtectedComponentProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n \n return <>{children}</>;\n}","/**\n * Utilities for OAuth42 Hosted Authentication\n */\n\nexport interface HostedAuthOptions {\n /** OAuth2 client ID */\n clientId: string;\n /** Redirect URI after authentication */\n redirectUri: string;\n /** OAuth2 scopes (space-separated) */\n scope?: string;\n /** OAuth2 state parameter for CSRF protection */\n state?: string;\n /** Base URL for OAuth42 issuer/API (defaults to production) */\n issuer?: string;\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nfunction generateState(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Redirect to OAuth42 hosted authentication pages\n *\n * @example\n * ```ts\n * import { redirectToHostedAuth } from '@oauth42/next/client';\n *\n * function LoginButton() {\n * return (\n * <button onClick={() => redirectToHostedAuth({\n * clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID!,\n * redirectUri: `${window.location.origin}/api/auth/callback`,\n * })}>\n * Sign in with OAuth42\n * </button>\n * );\n * }\n * ```\n */\nexport function redirectToHostedAuth(options: HostedAuthOptions): void {\n const {\n clientId,\n redirectUri,\n scope = 'openid profile email',\n state = generateState(),\n issuer = 'https://api.oauth42.com',\n } = options;\n\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope,\n state,\n });\n\n // Redirect to the backend's authorize endpoint, which handles the OAuth flow\n // and redirects to hosted auth login if user is not authenticated\n const authUrl = `${issuer}/oauth2/authorize?${params.toString()}`;\n\n // Store state in sessionStorage for verification on callback\n if (typeof window !== 'undefined') {\n sessionStorage.setItem('oauth42_state', state);\n window.location.href = authUrl;\n }\n}\n\n/**\n * Verify state parameter on OAuth2 callback\n * Call this in your callback page to verify the state matches\n *\n * @example\n * ```ts\n * import { verifyState } from '@oauth42/next/client';\n *\n * export default function CallbackPage() {\n * const searchParams = useSearchParams();\n * const state = searchParams.get('state');\n *\n * if (!verifyState(state)) {\n * return <div>Invalid state parameter</div>;\n * }\n *\n * // Continue with token exchange...\n * }\n * ```\n */\nexport function verifyState(state: string | null): boolean {\n if (typeof window === 'undefined') return false;\n if (!state) return false;\n\n const storedState = sessionStorage.getItem('oauth42_state');\n sessionStorage.removeItem('oauth42_state');\n\n return storedState === state;\n}\n\n/**\n * Configuration for hosted authentication\n */\nexport interface HostedAuthConfig {\n /** Enable hosted authentication */\n enabled: boolean;\n /** Base URL for hosted auth pages (optional, defaults to production) */\n baseUrl?: string;\n /** Feature flags for hosted auth */\n features?: {\n /** Allow user signup */\n signup?: boolean;\n /** Allow social login */\n socialLogin?: boolean;\n /** Allow password reset */\n passwordReset?: boolean;\n };\n}\n\n/**\n * Default hosted auth configuration\n */\nexport const DEFAULT_HOSTED_AUTH_CONFIG: HostedAuthConfig = {\n enabled: true,\n baseUrl: 'https://auth.oauth42.com',\n features: {\n signup: true,\n socialLogin: false,\n passwordReset: true,\n },\n};\n","/**\n * OAuth42 Custom Authentication Utilities\n *\n * Provides functions for implementing custom login UIs in customer apps\n * while properly handling OAuth2 PKCE flows and next-auth integration.\n */\n\nimport { signIn } from 'next-auth/react';\n\n// PKCE utilities\nfunction base64URLEncode(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '');\n}\n\nasync function generateCodeVerifier(): Promise<string> {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64URLEncode(array.buffer);\n}\n\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return base64URLEncode(hash);\n}\n\nfunction generateState(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64URLEncode(array.buffer);\n}\n\nexport interface LoginWithPasswordOptions {\n email: string;\n password: string;\n mfaCode?: string;\n /**\n * URL to redirect to after successful authentication.\n * If not provided, will redirect to '/'\n */\n callbackUrl?: string;\n}\n\nexport interface LoginWithAuthenticatorOptions {\n email: string;\n sessionId: string;\n /**\n * URL to redirect to after successful authentication.\n * If not provided, will redirect to '/'\n */\n callbackUrl?: string;\n}\n\nexport interface AuthError {\n error: string;\n error_description?: string;\n requires_enrollment?: boolean;\n enrollment_token?: string;\n requires_mfa?: boolean;\n}\n\n/**\n * Authenticate with email/password and complete OAuth PKCE flow\n *\n * This function handles the full authentication flow:\n * 1. Authenticates with the backend using credentials\n * 2. Uses the access token to authorize the OAuth client\n * 3. Exchanges authorization code for tokens via next-auth\n *\n * @example\n * ```tsx\n * const result = await loginWithPassword({\n * email: 'user@example.com',\n * password: 'password123',\n * callbackUrl: '/dashboard'\n * });\n *\n * if (result.success) {\n * // User is authenticated, next-auth session is set\n * } else if (result.requires_mfa) {\n * // Prompt for MFA code and call again with mfaCode\n * }\n * ```\n */\nexport async function loginWithPassword(\n options: LoginWithPasswordOptions\n): Promise<{ success: boolean; access_token?: string } & Partial<AuthError>> {\n try {\n // Step 1: Authenticate with backend to get access token\n const loginResponse = await fetch('/api/auth/login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email: options.email,\n password: options.password,\n ...(options.mfaCode ? { mfa_code: options.mfaCode } : {}),\n }),\n });\n\n if (!loginResponse.ok) {\n const errorData = await loginResponse.json().catch(() => ({}));\n const desc: string = errorData?.error_description || errorData?.message || '';\n\n // Handle special cases\n if (loginResponse.status === 403 && errorData?.requires_enrollment) {\n return {\n success: false,\n error: 'enrollment_required',\n error_description: 'MFA enrollment required',\n requires_enrollment: true,\n enrollment_token: errorData.enrollment_token,\n };\n }\n\n if (loginResponse.status === 403 && /mfa required/i.test(desc)) {\n return {\n success: false,\n error: 'mfa_required',\n error_description: 'MFA code required',\n requires_mfa: true,\n };\n }\n\n if (loginResponse.status === 401 && /invalid mfa code/i.test(desc)) {\n return {\n success: false,\n error: 'invalid_mfa_code',\n error_description: 'Invalid MFA code',\n requires_mfa: true,\n };\n }\n\n return {\n success: false,\n error: 'authentication_failed',\n error_description: desc || 'Invalid credentials',\n };\n }\n\n const { access_token } = await loginResponse.json();\n\n // Return access token for the application to use in OAuth authorize flow\n return { success: true, access_token };\n } catch (error) {\n console.error('Login error:', error);\n return {\n success: false,\n error: 'network_error',\n error_description: 'Failed to connect to authentication server',\n };\n }\n}\n\n/**\n * Initiate authenticator (passwordless) login and return challenge code\n *\n * @example\n * ```tsx\n * const result = await initiateAuthenticatorLogin({\n * email: 'user@example.com',\n * sessionId: crypto.randomUUID()\n * });\n *\n * if (result.success) {\n * // Display result.challengeCode to user\n * // Wait for WebSocket approval or poll for completion\n * }\n * ```\n */\nexport async function initiateAuthenticatorLogin(\n options: LoginWithAuthenticatorOptions\n): Promise<{\n success: boolean;\n challengeCode?: string;\n challengeId?: string;\n} & Partial<AuthError>> {\n try {\n const response = await fetch('/api/auth/passwordless/initiate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email: options.email,\n session_id: options.sessionId,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n return {\n success: false,\n error: 'initiation_failed',\n error_description: errorData.error_description || 'Failed to create challenge',\n };\n }\n\n const data = await response.json();\n\n return {\n success: true,\n challengeCode: data.challenge_code,\n challengeId: data.challenge_id,\n };\n } catch (error) {\n console.error('Authenticator login error:', error);\n return {\n success: false,\n error: 'network_error',\n error_description: 'Failed to connect to authentication server',\n };\n }\n}\n\n/**\n * Complete authenticator login after approval\n *\n * Call this after receiving approval notification via WebSocket\n * or after the user has approved on their mobile device.\n * Returns the access token for the application to use in OAuth authorize flow.\n *\n * @param accessToken - Access token received from approval payload\n */\nexport async function completeAuthenticatorLogin(\n accessToken: string\n): Promise<{ success: boolean; access_token: string }> {\n return { success: true, access_token: accessToken };\n}\n\n/**\n * Simulate approval for testing (development only)\n *\n * @param challengeId - The challenge ID to approve\n * @param selectedCode - The code that was displayed to the user\n */\nexport async function simulateApproval(\n challengeId: string,\n selectedCode: string\n): Promise<{ success: boolean }> {\n try {\n await fetch('/api/auth/challenge/approve', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n challenge_id: challengeId,\n selected_code: selectedCode,\n }),\n });\n\n return { success: true };\n } catch (error) {\n console.error('Simulated approval failed:', error);\n return { success: false };\n }\n}\n\n/**\n * Logout the current user from the app only (app-level logout)\n *\n * This clears the next-auth session for this app AND removes the app-user\n * mapping from the session registry. On next login, the user will see\n * the account picker to choose which account to use.\n *\n * For provider-level logout (sign out of all apps), use logoutEverywhere().\n *\n * @param options.callbackUrl - URL to redirect to after logout (default: '/')\n * @param options.clientId - OAuth client ID to clear from registry\n * @param options.issuer - OAuth42 issuer URL for the logout endpoint\n *\n * @example\n * ```tsx\n * import { logout } from '@oauth42/next/client';\n *\n * const handleLogout = async () => {\n * await logout({\n * callbackUrl: '/login',\n * clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID,\n * issuer: process.env.NEXT_PUBLIC_OAUTH_ISSUER || 'https://localhost:8443'\n * });\n * };\n * ```\n */\nexport async function logout(options?: {\n callbackUrl?: string;\n clientId?: string;\n issuer?: string;\n}): Promise<void> {\n // If clientId and issuer provided, call backend to clear app_user from registry\n if (options?.clientId && options?.issuer) {\n try {\n await fetch(`${options.issuer}/oauth2/logout?client_id=${encodeURIComponent(options.clientId)}`, {\n method: 'POST',\n credentials: 'include', // Include cookies (registry cookie)\n });\n } catch (err) {\n console.warn('[OAuth42] Failed to clear app user from registry:', err);\n // Continue with NextAuth signOut even if this fails\n }\n }\n\n // Import signOut from next-auth/react\n const { signOut } = await import('next-auth/react');\n\n // Clear next-auth session (app-level only)\n await signOut({\n callbackUrl: options?.callbackUrl || '/',\n redirect: true\n });\n}\n\n/**\n * Logout the current user from ALL apps using OAuth42 (provider-level logout)\n *\n * This redirects to the OAuth42 provider's logout endpoint to clear the\n * oauth42_session cookie, effectively logging the user out of all apps.\n *\n * Use this when testing fresh login flows or when the user explicitly\n * wants to sign out of everything.\n *\n * @param issuer - The OAuth42 issuer URL (e.g., 'https://localhost:8443')\n * @param callbackUrl - URL to redirect to after logout (default: current origin)\n *\n * @example\n * ```tsx\n * import { logoutEverywhere } from '@oauth42/next/client';\n *\n * const handleLogoutEverywhere = () => {\n * const issuer = process.env.NEXT_PUBLIC_OAUTH42_ISSUER || 'https://localhost:8443';\n * logoutEverywhere(issuer, '/auth/signin');\n * };\n * ```\n */\nexport function logoutEverywhere(issuer: string, callbackUrl?: string, cookiePrefix?: string): void {\n const redirectUri = callbackUrl || window.location.origin;\n\n // Clear NextAuth session cookies before redirect\n // This ensures the local app session is cleared even if the redirect happens fast\n // Cookie prefix defaults to common patterns, but can be overridden\n const prefixes = cookiePrefix\n ? [cookiePrefix]\n : ['oauth42-portal', 'oauth42-admin', 'next-auth'];\n\n for (const prefix of prefixes) {\n document.cookie = `${prefix}.session-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;\n document.cookie = `${prefix}.csrf-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;\n document.cookie = `${prefix}.callback-url=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;\n }\n\n // Redirect to OAuth42 provider logout endpoint\n // This clears the oauth42_session cookie and redirects back\n window.location.href = `${issuer}/auth/logout?redirect_uri=${encodeURIComponent(redirectUri)}`;\n}\n"],"mappings":"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","names":["signIn","signOut","useSession","signIn","signOut","signIn","signOut","signOut"]}
1
+ {"version":3,"sources":["../../src/client/index.ts","../../src/client/hooks.ts","../../src/client/components.tsx","../../src/utils/hosted-auth.ts","../../src/client/auth.ts"],"sourcesContent":["// Client-side exports\n\n// Re-export commonly used next-auth/react functions\nexport { signIn, signOut, useSession, SessionProvider } from 'next-auth/react';\nexport type { Session } from 'next-auth';\nexport {\n useOAuth42Session,\n useOAuth42User,\n useOAuth42Tokens,\n useRequireAuth,\n} from './hooks';\n\nexport type {\n OAuth42Session,\n UseOAuth42SessionReturn,\n} from './hooks';\n\nexport {\n SignInButton,\n SignOutButton,\n UserProfile,\n AuthStatus,\n ProtectedComponent,\n} from './components';\n\nexport type {\n SignInButtonProps,\n SignOutButtonProps,\n UserProfileProps,\n AuthStatusProps,\n ProtectedComponentProps,\n} from './components';\n\n// Hosted auth utilities\nexport {\n redirectToHostedAuth,\n verifyState,\n DEFAULT_HOSTED_AUTH_CONFIG,\n} from '../utils/hosted-auth';\n\nexport type {\n HostedAuthOptions,\n HostedAuthConfig,\n} from '../utils/hosted-auth';\n\n// Custom authentication utilities\nexport {\n loginWithPassword,\n initiateAuthenticatorLogin,\n completeAuthenticatorLogin,\n simulateApproval,\n logout,\n logoutEverywhere,\n} from './auth';\n\nexport type {\n LoginWithPasswordOptions,\n LoginWithAuthenticatorOptions,\n AuthError,\n} from './auth';","import { useSession, signIn, signOut } from 'next-auth/react';\nimport { useCallback, useEffect, useState } from 'react';\n\nexport type OAuth42Session<E = {}> = ({\n user?: {\n email?: string | null;\n name?: string | null;\n image?: string | null;\n username?: string;\n emailVerified?: boolean;\n };\n accessToken?: string;\n idToken?: string;\n expires?: string;\n}) & E;\n\nexport interface UseOAuth42SessionReturn<E = {}> {\n session: OAuth42Session<E> | null;\n loading: boolean;\n error: Error | null;\n isAuthenticated: boolean;\n signIn: () => Promise<void>;\n signOut: () => Promise<void>;\n}\n\n/**\n * Hook to manage OAuth42 session with optional extra fields\n */\nexport function useOAuth42Session<E = {}>(): UseOAuth42SessionReturn<E> {\n const { data: session, status } = useSession();\n const [error, setError] = useState<Error | null>(null);\n \n const handleSignIn = useCallback(async () => {\n try {\n setError(null);\n await signIn('oauth42');\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n const handleSignOut = useCallback(async () => {\n try {\n setError(null);\n await signOut();\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n return {\n session: session as unknown as OAuth42Session<E> | null,\n loading: status === 'loading',\n error,\n isAuthenticated: status === 'authenticated',\n signIn: handleSignIn,\n signOut: handleSignOut,\n };\n}\n\n/**\n * Hook to get the current OAuth42 user\n */\nexport function useOAuth42User<E = {}>() {\n const { session, isAuthenticated } = useOAuth42Session<E>();\n \n return {\n user: isAuthenticated ? session?.user : null,\n isAuthenticated,\n };\n}\n\n/**\n * Hook to manage OAuth42 tokens\n */\nexport function useOAuth42Tokens<E = {}>() {\n const { session } = useOAuth42Session<E>();\n const [isExpired, setIsExpired] = useState(false);\n \n useEffect(() => {\n if (session?.expires) {\n const expiryTime = new Date(session.expires).getTime();\n const now = Date.now();\n setIsExpired(now >= expiryTime);\n \n // Set a timer to update expiry status\n const timeUntilExpiry = expiryTime - now;\n if (timeUntilExpiry > 0) {\n const timer = setTimeout(() => {\n setIsExpired(true);\n }, timeUntilExpiry);\n \n return () => clearTimeout(timer);\n }\n }\n }, [session?.expires]);\n \n return {\n accessToken: session?.accessToken,\n idToken: session?.idToken,\n isExpired,\n refreshToken: async () => {\n // Trigger a session refresh\n await signIn('oauth42');\n },\n };\n}\n\n/**\n * Hook for protected routes\n */\nexport function useRequireAuth(redirectTo: string = '/auth/signin') {\n const { isAuthenticated, loading } = useOAuth42Session();\n const [isRedirecting, setIsRedirecting] = useState(false);\n \n useEffect(() => {\n if (!loading && !isAuthenticated && !isRedirecting) {\n setIsRedirecting(true);\n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [isAuthenticated, loading, redirectTo, isRedirecting]);\n \n return {\n isAuthenticated,\n loading: loading || isRedirecting,\n };\n}\n","import React from 'react';\nimport { signIn, signOut } from 'next-auth/react';\nimport { useOAuth42Session, useOAuth42User } from './hooks';\n\nexport interface SignInButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign in button component\n */\nexport function SignInButton({ \n children = 'Sign in with OAuth42', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignInButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signIn('oauth42', { callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface SignOutButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign out button component\n */\nexport function SignOutButton({ \n children = 'Sign out', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignOutButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signOut({ callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface UserProfileProps {\n className?: string;\n showEmail?: boolean;\n showName?: boolean;\n showImage?: boolean;\n loadingComponent?: React.ReactNode;\n notAuthenticatedComponent?: React.ReactNode;\n}\n\n/**\n * User profile display component\n */\nexport function UserProfile({\n className = '',\n showEmail = true,\n showName = true,\n showImage = true,\n loadingComponent = <div>Loading...</div>,\n notAuthenticatedComponent = <div>Not authenticated</div>,\n}: UserProfileProps) {\n const { session, loading, isAuthenticated } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated || !session?.user) {\n return <>{notAuthenticatedComponent}</>;\n }\n \n const { user } = session;\n \n return (\n <div className={className}>\n {showImage && user.image && (\n <img \n src={user.image} \n alt={user.name || 'User'} \n style={{ width: 50, height: 50, borderRadius: '50%' }}\n />\n )}\n {showName && user.name && <div>{user.name}</div>}\n {showEmail && user.email && <div>{user.email}</div>}\n </div>\n );\n}\n\nexport interface AuthStatusProps {\n authenticatedComponent?: React.ReactNode;\n unauthenticatedComponent?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Conditional rendering based on auth status\n */\nexport function AuthStatus({\n authenticatedComponent,\n unauthenticatedComponent,\n loadingComponent = <div>Loading...</div>,\n}: AuthStatusProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n return <>{isAuthenticated ? authenticatedComponent : unauthenticatedComponent}</>;\n}\n\nexport interface ProtectedComponentProps {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Wrapper component for protected content\n */\nexport function ProtectedComponent({\n children,\n fallback = <SignInButton />,\n loadingComponent = <div>Loading...</div>,\n}: ProtectedComponentProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n \n return <>{children}</>;\n}","/**\n * Utilities for OAuth42 Hosted Authentication\n */\n\nexport interface HostedAuthOptions {\n /** OAuth2 client ID */\n clientId: string;\n /** Redirect URI after authentication */\n redirectUri: string;\n /** OAuth2 scopes (space-separated) */\n scope?: string;\n /** OAuth2 state parameter for CSRF protection */\n state?: string;\n /** Base URL for OAuth42 issuer/API (defaults to production) */\n issuer?: string;\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nfunction generateState(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Redirect to OAuth42 hosted authentication pages\n *\n * @example\n * ```ts\n * import { redirectToHostedAuth } from '@oauth42/next/client';\n *\n * function LoginButton() {\n * return (\n * <button onClick={() => redirectToHostedAuth({\n * clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID!,\n * redirectUri: `${window.location.origin}/api/auth/callback`,\n * })}>\n * Sign in with OAuth42\n * </button>\n * );\n * }\n * ```\n */\nexport function redirectToHostedAuth(options: HostedAuthOptions): void {\n const {\n clientId,\n redirectUri,\n scope = 'openid profile email',\n state = generateState(),\n issuer = 'https://api.oauth42.com',\n } = options;\n\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope,\n state,\n });\n\n // Redirect to the backend's authorize endpoint, which handles the OAuth flow\n // and redirects to hosted auth login if user is not authenticated\n const authUrl = `${issuer}/oauth2/authorize?${params.toString()}`;\n\n // Store state in sessionStorage for verification on callback\n if (typeof window !== 'undefined') {\n sessionStorage.setItem('oauth42_state', state);\n window.location.href = authUrl;\n }\n}\n\n/**\n * Verify state parameter on OAuth2 callback\n * Call this in your callback page to verify the state matches\n *\n * @example\n * ```ts\n * import { verifyState } from '@oauth42/next/client';\n *\n * export default function CallbackPage() {\n * const searchParams = useSearchParams();\n * const state = searchParams.get('state');\n *\n * if (!verifyState(state)) {\n * return <div>Invalid state parameter</div>;\n * }\n *\n * // Continue with token exchange...\n * }\n * ```\n */\nexport function verifyState(state: string | null): boolean {\n if (typeof window === 'undefined') return false;\n if (!state) return false;\n\n const storedState = sessionStorage.getItem('oauth42_state');\n sessionStorage.removeItem('oauth42_state');\n\n return storedState === state;\n}\n\n/**\n * Configuration for hosted authentication\n */\nexport interface HostedAuthConfig {\n /** Enable hosted authentication */\n enabled: boolean;\n /** Base URL for hosted auth pages (optional, defaults to production) */\n baseUrl?: string;\n /** Feature flags for hosted auth */\n features?: {\n /** Allow user signup */\n signup?: boolean;\n /** Allow social login */\n socialLogin?: boolean;\n /** Allow password reset */\n passwordReset?: boolean;\n };\n}\n\n/**\n * Default hosted auth configuration\n */\nexport const DEFAULT_HOSTED_AUTH_CONFIG: HostedAuthConfig = {\n enabled: true,\n baseUrl: 'https://auth.oauth42.com',\n features: {\n signup: true,\n socialLogin: false,\n passwordReset: true,\n },\n};\n","/**\n * OAuth42 Custom Authentication Utilities\n *\n * Provides functions for implementing custom login UIs in customer apps\n * while properly handling OAuth2 PKCE flows and next-auth integration.\n */\n\nimport { signIn } from 'next-auth/react';\n\n// PKCE utilities\nfunction base64URLEncode(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '');\n}\n\nasync function generateCodeVerifier(): Promise<string> {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64URLEncode(array.buffer);\n}\n\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return base64URLEncode(hash);\n}\n\nfunction generateState(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64URLEncode(array.buffer);\n}\n\nexport interface LoginWithPasswordOptions {\n email: string;\n password: string;\n mfaCode?: string;\n /**\n * URL to redirect to after successful authentication.\n * If not provided, will redirect to '/'\n */\n callbackUrl?: string;\n}\n\nexport interface LoginWithAuthenticatorOptions {\n email: string;\n sessionId: string;\n /**\n * URL to redirect to after successful authentication.\n * If not provided, will redirect to '/'\n */\n callbackUrl?: string;\n}\n\nexport interface AuthError {\n error: string;\n error_description?: string;\n requires_enrollment?: boolean;\n enrollment_token?: string;\n requires_mfa?: boolean;\n}\n\n/**\n * Authenticate with email/password and complete OAuth PKCE flow\n *\n * This function handles the full authentication flow:\n * 1. Authenticates with the backend using credentials\n * 2. Uses the access token to authorize the OAuth client\n * 3. Exchanges authorization code for tokens via next-auth\n *\n * @example\n * ```tsx\n * const result = await loginWithPassword({\n * email: 'user@example.com',\n * password: 'password123',\n * callbackUrl: '/dashboard'\n * });\n *\n * if (result.success) {\n * // User is authenticated, next-auth session is set\n * } else if (result.requires_mfa) {\n * // Prompt for MFA code and call again with mfaCode\n * }\n * ```\n */\nexport async function loginWithPassword(\n options: LoginWithPasswordOptions\n): Promise<{ success: boolean; access_token?: string } & Partial<AuthError>> {\n try {\n // Step 1: Authenticate with backend to get access token\n const loginResponse = await fetch('/api/auth/login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email: options.email,\n password: options.password,\n ...(options.mfaCode ? { mfa_code: options.mfaCode } : {}),\n }),\n });\n\n if (!loginResponse.ok) {\n const errorData = await loginResponse.json().catch(() => ({}));\n const desc: string = errorData?.error_description || errorData?.message || '';\n\n // Handle special cases\n if (loginResponse.status === 403 && errorData?.requires_enrollment) {\n return {\n success: false,\n error: 'enrollment_required',\n error_description: 'MFA enrollment required',\n requires_enrollment: true,\n enrollment_token: errorData.enrollment_token,\n };\n }\n\n if (loginResponse.status === 403 && /mfa required/i.test(desc)) {\n return {\n success: false,\n error: 'mfa_required',\n error_description: 'MFA code required',\n requires_mfa: true,\n };\n }\n\n if (loginResponse.status === 401 && /invalid mfa code/i.test(desc)) {\n return {\n success: false,\n error: 'invalid_mfa_code',\n error_description: 'Invalid MFA code',\n requires_mfa: true,\n };\n }\n\n return {\n success: false,\n error: 'authentication_failed',\n error_description: desc || 'Invalid credentials',\n };\n }\n\n const { access_token } = await loginResponse.json();\n\n // Return access token for the application to use in OAuth authorize flow\n return { success: true, access_token };\n } catch (error) {\n console.error('Login error:', error);\n return {\n success: false,\n error: 'network_error',\n error_description: 'Failed to connect to authentication server',\n };\n }\n}\n\n/**\n * Initiate authenticator (passwordless) login and return challenge code\n *\n * @example\n * ```tsx\n * const result = await initiateAuthenticatorLogin({\n * email: 'user@example.com',\n * sessionId: crypto.randomUUID()\n * });\n *\n * if (result.success) {\n * // Display result.challengeCode to user\n * // Wait for WebSocket approval or poll for completion\n * }\n * ```\n */\nexport async function initiateAuthenticatorLogin(\n options: LoginWithAuthenticatorOptions\n): Promise<{\n success: boolean;\n challengeCode?: string;\n challengeId?: string;\n} & Partial<AuthError>> {\n try {\n const response = await fetch('/api/auth/passwordless/initiate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email: options.email,\n session_id: options.sessionId,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n return {\n success: false,\n error: 'initiation_failed',\n error_description: errorData.error_description || 'Failed to create challenge',\n };\n }\n\n const data = await response.json();\n\n return {\n success: true,\n challengeCode: data.challenge_code,\n challengeId: data.challenge_id,\n };\n } catch (error) {\n console.error('Authenticator login error:', error);\n return {\n success: false,\n error: 'network_error',\n error_description: 'Failed to connect to authentication server',\n };\n }\n}\n\n/**\n * Complete authenticator login after approval\n *\n * Call this after receiving approval notification via WebSocket\n * or after the user has approved on their mobile device.\n * Returns the access token for the application to use in OAuth authorize flow.\n *\n * @param accessToken - Access token received from approval payload\n */\nexport async function completeAuthenticatorLogin(\n accessToken: string\n): Promise<{ success: boolean; access_token: string }> {\n return { success: true, access_token: accessToken };\n}\n\n/**\n * Simulate approval for testing (development only)\n *\n * @param challengeId - The challenge ID to approve\n * @param selectedCode - The code that was displayed to the user\n */\nexport async function simulateApproval(\n challengeId: string,\n selectedCode: string\n): Promise<{ success: boolean }> {\n try {\n await fetch('/api/auth/challenge/approve', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n challenge_id: challengeId,\n selected_code: selectedCode,\n }),\n });\n\n return { success: true };\n } catch (error) {\n console.error('Simulated approval failed:', error);\n return { success: false };\n }\n}\n\n/**\n * Logout the current user from the app only (app-level logout)\n *\n * This clears the next-auth session for this app AND removes the app-user\n * mapping from the session registry. On next login, the user will see\n * the account picker to choose which account to use.\n *\n * For provider-level logout (sign out of all apps), use logoutEverywhere().\n *\n * @param options.callbackUrl - URL to redirect to after logout (default: '/')\n * @param options.clientId - OAuth client ID to clear from registry\n * @param options.issuer - OAuth42 issuer URL for the logout endpoint\n *\n * @example\n * ```tsx\n * import { logout } from '@oauth42/next/client';\n *\n * const handleLogout = async () => {\n * await logout({\n * callbackUrl: '/login',\n * clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID,\n * issuer: process.env.NEXT_PUBLIC_OAUTH_ISSUER || 'https://localhost:8443'\n * });\n * };\n * ```\n */\nexport async function logout(options?: {\n callbackUrl?: string;\n clientId?: string;\n issuer?: string;\n}): Promise<void> {\n // If clientId and issuer provided, call backend to clear app_user from registry\n if (options?.clientId && options?.issuer) {\n try {\n await fetch(`${options.issuer}/oauth2/logout?client_id=${encodeURIComponent(options.clientId)}`, {\n method: 'POST',\n credentials: 'include', // Include cookies (registry cookie)\n });\n } catch (err) {\n console.warn('[OAuth42] Failed to clear app user from registry:', err);\n // Continue with NextAuth signOut even if this fails\n }\n }\n\n // Import signOut from next-auth/react\n const { signOut } = await import('next-auth/react');\n\n // Clear next-auth session (app-level only)\n await signOut({\n callbackUrl: options?.callbackUrl || '/',\n redirect: true\n });\n}\n\n/**\n * Logout the current user from ALL apps using OAuth42 (provider-level logout)\n *\n * This redirects to the OAuth42 provider's logout endpoint to clear the\n * oauth42_session cookie, effectively logging the user out of all apps.\n *\n * Use this when testing fresh login flows or when the user explicitly\n * wants to sign out of everything.\n *\n * @param issuer - The OAuth42 issuer URL (e.g., 'https://localhost:8443')\n * @param callbackUrl - URL to redirect to after logout (default: current origin)\n *\n * @example\n * ```tsx\n * import { logoutEverywhere } from '@oauth42/next/client';\n *\n * const handleLogoutEverywhere = () => {\n * const issuer = process.env.NEXT_PUBLIC_OAUTH42_ISSUER || 'https://localhost:8443';\n * logoutEverywhere(issuer, '/auth/signin');\n * };\n * ```\n */\nexport async function logoutEverywhere(issuer: string, callbackUrl?: string): Promise<void> {\n const redirectUri = callbackUrl || window.location.origin;\n\n // First, call NextAuth's signOut API to properly clear session cookies\n // This uses NextAuth's built-in cookie management which handles all the\n // cookie attributes (domain, path, secure, httpOnly) correctly\n try {\n // Get CSRF token from NextAuth\n const csrfResponse = await fetch('/api/auth/csrf');\n const { csrfToken } = await csrfResponse.json();\n\n // Call NextAuth signOut endpoint to clear session\n await fetch('/api/auth/signout', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n csrfToken,\n json: 'true',\n }),\n credentials: 'include',\n });\n } catch (err) {\n console.warn('[OAuth42] Failed to call NextAuth signout:', err);\n // Continue with redirect even if this fails\n }\n\n // Then redirect to OAuth42 provider logout endpoint\n // This clears the oauth42_session cookie and redirects back\n window.location.href = `${issuer}/auth/logout?redirect_uri=${encodeURIComponent(redirectUri)}`;\n}\n"],"mappings":"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","names":["signIn","signOut","useSession","signIn","signOut","signIn","signOut","signOut"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oauth42/next",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "description": "Official OAuth42 SDK for Next.js applications",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",