@rebasepro/auth 0.0.1-canary.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.umd.js","sources":["../src/api.ts","../src/hooks/useRebaseAuthController.ts","../src/hooks/useBackendUserManagement.ts","../src/components/RebaseLoginView.tsx","../src/components/AdminViews.tsx"],"sourcesContent":["import { AuthResponse, RefreshResponse, Session, UserInfo } from \"./types\";\n\n/**\n * Default API URL - can be overridden in hook props\n */\nlet baseApiUrl = \"\";\n\n/**\n * Configure the API base URL\n */\nexport function setApiUrl(url: string): void {\n baseApiUrl = url;\n}\n\n/**\n * Get the current API URL\n */\nexport function getApiUrl(): string {\n return baseApiUrl;\n}\n\nclass AuthApiError extends Error {\n code: string;\n\n constructor(message: string, code: string) {\n super(message);\n this.code = code;\n this.name = \"AuthApiError\";\n }\n}\n\nasync function handleResponse<T>(response: Response): Promise<T> {\n let data: Record<string, unknown>;\n try {\n data = await response.json();\n } catch (parseError) {\n // Response wasn't JSON - could be network error or server issue\n throw new AuthApiError(\n `Server returned non-JSON response (status: ${response.status})`,\n \"PARSE_ERROR\"\n );\n }\n\n if (!response.ok) {\n throw new AuthApiError(\n (data as Record<string, Record<string, string>>).error?.message || \"Request failed\",\n (data as Record<string, Record<string, string>>).error?.code || \"UNKNOWN_ERROR\"\n );\n }\n\n return data as T;\n}\n\n/**\n * Wrapper for fetch that catches generic network failures (like server down)\n * and translates them to an AuthApiError.\n */\nasync function fetchWithHandling(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n try {\n return await fetch(input, init);\n } catch (error: unknown) {\n if (error instanceof TypeError && error.message.includes(\"Failed to fetch\")) {\n throw new AuthApiError(\n \"Failed to connect to the backend server. The backend might be down or failed to initialize (e.g., database connection timeout).\",\n \"NETWORK_ERROR\"\n );\n }\n throw new AuthApiError(\"Network error: \" + (error instanceof Error ? error.message : String(error)), \"NETWORK_ERROR\");\n }\n}\n\n/**\n * Register a new user with email/password\n */\nexport async function register(\n email: string,\n password: string,\n displayName?: string\n): Promise<AuthResponse> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/register`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, password, displayName })\n });\n\n return handleResponse<AuthResponse>(response);\n}\n\n/**\n * Login with email/password\n */\nexport async function login(email: string, password: string): Promise<AuthResponse> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/login`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, password })\n });\n\n return handleResponse<AuthResponse>(response);\n}\n\n/**\n * Login with Google ID token\n */\nexport async function googleLogin(idToken: string): Promise<AuthResponse> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/google`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ idToken })\n });\n\n return handleResponse<AuthResponse>(response);\n}\n\n/**\n * Refresh access token using refresh token\n */\nexport async function refreshAccessToken(refreshToken: string): Promise<RefreshResponse> {\n console.log(\"[AUTH-API] Calling refresh endpoint...\");\n\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/refresh`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken })\n });\n\n console.log(\"[AUTH-API] Refresh response status:\", response.status);\n return handleResponse<RefreshResponse>(response);\n}\n\n/**\n * Logout and invalidate refresh token\n */\nexport async function logout(refreshToken?: string): Promise<void> {\n await fetchWithHandling(`${baseApiUrl}/api/auth/logout`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken })\n });\n}\n\n/**\n * Get current user info\n */\nexport async function getCurrentUser(accessToken: string): Promise<{ user: UserInfo }> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/me`, {\n method: \"GET\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${accessToken}`\n }\n });\n\n return handleResponse<{ user: UserInfo }>(response);\n}\n\n/**\n * Request password reset email\n */\nexport async function forgotPassword(email: string): Promise<{ success: boolean; message: string }> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/forgot-password`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email })\n });\n\n return handleResponse<{ success: boolean; message: string }>(response);\n}\n\n/**\n * Reset password using token from email\n */\nexport async function resetPassword(token: string, password: string): Promise<{ success: boolean; message: string }> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/reset-password`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token, password })\n });\n\n return handleResponse<{ success: boolean; message: string }>(response);\n}\n\n/**\n * Change password for authenticated user\n */\nexport async function changePassword(\n accessToken: string,\n oldPassword: string,\n newPassword: string\n): Promise<{ success: boolean; message: string }> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/change-password`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${accessToken}`\n },\n body: JSON.stringify({ oldPassword, newPassword })\n });\n\n return handleResponse<{ success: boolean; message: string }>(response);\n}\n\n/**\n * Send email verification link\n */\nexport async function sendVerificationEmail(accessToken: string): Promise<{ success: boolean; message: string }> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/send-verification`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${accessToken}`\n }\n });\n\n return handleResponse<{ success: boolean; message: string }>(response);\n}\n\n/**\n * Verify email address using token\n */\nexport async function verifyEmail(token: string): Promise<{ success: boolean; message: string }> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/verify-email?token=${encodeURIComponent(token)}`, {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" }\n });\n\n return handleResponse<{ success: boolean; message: string }>(response);\n}\n\n/**\n * Update current user profile\n */\nexport async function updateProfile(\n accessToken: string,\n displayName?: string,\n photoURL?: string\n): Promise<{ user: UserInfo }> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/me`, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${accessToken}`\n },\n body: JSON.stringify({ displayName, photoURL })\n });\n\n return handleResponse<{ user: UserInfo }>(response);\n}\n\n/**\n * Fetch active sessions for current user\n */\nexport async function fetchSessions(accessToken: string, currentRefreshToken?: string): Promise<{ sessions: Session[] }> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${accessToken}`\n };\n if (currentRefreshToken) {\n headers[\"X-Refresh-Token\"] = currentRefreshToken;\n }\n\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/sessions`, {\n method: \"GET\",\n headers\n });\n\n return handleResponse<{ sessions: Session[] }>(response);\n}\n\n/**\n * Revoke a specific session\n */\nexport async function revokeSession(accessToken: string, sessionId: string): Promise<{ success: boolean; message: string }> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/sessions/${sessionId}`, {\n method: \"DELETE\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${accessToken}`\n }\n });\n\n return handleResponse<{ success: boolean; message: string }>(response);\n}\n\n/**\n * Revoke all sessions for current user\n */\nexport async function revokeAllSessions(accessToken: string): Promise<{ success: boolean; message: string }> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/sessions`, {\n method: \"DELETE\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${accessToken}`\n }\n });\n\n return handleResponse<{ success: boolean; message: string }>(response);\n}\n\n/**\n * Auth config response from the backend\n */\nexport interface AuthConfigResponse {\n /** True when there are no users in the system and first user setup is needed */\n needsSetup: boolean;\n /** Whether new user registration is enabled */\n registrationEnabled: boolean;\n /** Whether Google OAuth is configured */\n googleEnabled: boolean;\n /** Whether email service is configured */\n emailServiceEnabled: boolean;\n}\n\n/**\n * Fetch auth configuration / status from the backend\n * This is an unauthenticated endpoint used to detect bootstrap mode\n */\nexport async function fetchAuthConfig(): Promise<AuthConfigResponse> {\n const response = await fetchWithHandling(`${baseApiUrl}/api/auth/config`, {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" }\n });\n\n return handleResponse<AuthConfigResponse>(response);\n}\n\nexport { AuthApiError };\n\n","import { useCallback, useEffect, useState, useRef } from \"react\";\nimport { User } from \"@rebasepro/core\";\nimport * as authApi from \"../api\";\nimport { AuthConfigResponse } from \"../api\";\nimport {\n RebaseAuthController,\n RebaseAuthControllerProps,\n AuthTokens,\n UserInfo\n} from \"../types\";\n\nconst STORAGE_KEY = \"rebase_auth\";\n\n// Buffer time before expiry to trigger refresh (2 minutes)\nconst TOKEN_REFRESH_BUFFER_MS = 2 * 60 * 1000;\n\n/**\n * Convert UserInfo from API to Rebase User type\n */\nfunction convertToUser(userInfo: UserInfo): User {\n return {\n uid: userInfo.uid,\n email: userInfo.email,\n displayName: userInfo.displayName || null,\n photoURL: userInfo.photoURL || null,\n providerId: \"custom\",\n isAnonymous: false,\n roles: userInfo.roles || []\n };\n}\n\n/**\n * Storage data structure\n */\ninterface StoredAuthData {\n tokens: AuthTokens;\n user: UserInfo;\n}\n\n/**\n * Save auth data to localStorage\n */\nfunction saveAuthToStorage(tokens: AuthTokens, user: UserInfo): void {\n try {\n const data: StoredAuthData = { tokens, user };\n localStorage.setItem(STORAGE_KEY, JSON.stringify(data));\n const expiryDate = new Date(tokens.accessTokenExpiresAt);\n const expiryStr = Number.isFinite(tokens.accessTokenExpiresAt) ? expiryDate.toISOString() : \"invalid\";\n } catch (e) {\n }\n}\n\n/**\n * Load auth data from localStorage\n */\nfunction loadAuthFromStorage(): StoredAuthData | null {\n try {\n const data = localStorage.getItem(STORAGE_KEY);\n if (data) {\n const parsed = JSON.parse(data);\n return parsed;\n }\n } catch (e) {\n console.warn(\"Failed to load auth from storage:\", e);\n }\n return null;\n}\n\n/**\n * Clear auth data from localStorage\n */\nfunction clearAuthFromStorage(): void {\n try {\n localStorage.removeItem(STORAGE_KEY);\n } catch (e) {\n console.warn(\"Failed to clear auth from storage:\", e);\n }\n}\n\n/**\n * Check if token is expired or about to expire\n */\nfunction isTokenExpiredOrNearExpiry(expiresAt: number, bufferMs: number = TOKEN_REFRESH_BUFFER_MS): boolean {\n return Date.now() + bufferMs >= expiresAt;\n}\n\n/**\n * Auth controller hook for JWT-based authentication\n * with @rebasepro/backend\n * \n * @param props Configuration options\n * @returns RebaseAuthController instance\n */\nexport function useRebaseAuthController(\n props: RebaseAuthControllerProps = {}\n): RebaseAuthController {\n const { apiUrl, onSignOut, defineRolesFor } = props;\n\n const [user, setUser] = useState<User | null>(null);\n const [authLoading, setAuthLoading] = useState(false);\n const [initialLoading, setInitialLoading] = useState(true);\n const [authError, setAuthError] = useState<Error | null>(null);\n const [authProviderError, setAuthProviderError] = useState<Error | null>(null);\n const [loginSkipped, setLoginSkipped] = useState(false);\n const [extra, setExtra] = useState<unknown>(null);\n const [authConfig, setAuthConfig] = useState<AuthConfigResponse | null>(null);\n\n // Store tokens in ref for quick access, but also persist to localStorage\n const tokensRef = useRef<AuthTokens | null>(null);\n const refreshTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n // Track if a refresh is currently in progress to avoid concurrent refreshes\n const refreshPromiseRef = useRef<Promise<AuthTokens | null> | null>(null);\n // Track if component is mounted\n const isMountedRef = useRef(true);\n\n // Configure API URL on mount\n useEffect(() => {\n if (apiUrl) {\n authApi.setApiUrl(apiUrl);\n }\n }, [apiUrl]);\n\n // Clear session and sign out\n const clearSessionAndSignOut = useCallback(() => {\n tokensRef.current = null;\n clearAuthFromStorage();\n if (refreshTimeoutRef.current) {\n clearTimeout(refreshTimeoutRef.current);\n refreshTimeoutRef.current = null;\n }\n setUser(null);\n setLoginSkipped(false);\n onSignOut?.();\n }, [onSignOut]);\n\n /**\n * Refresh the access token using the stored refresh token.\n * Returns the new tokens or null if refresh failed.\n */\n const refreshAccessToken = useCallback(async (): Promise<AuthTokens | null> => {\n // Prevent concurrent refreshes\n if (refreshPromiseRef.current) {\n // Wait for the current refresh to complete\n return refreshPromiseRef.current;\n }\n\n const executeRefresh = async (): Promise<AuthTokens | null> => {\n // Check if another tab has already refreshed the token\n const storedData = loadAuthFromStorage();\n if (storedData?.tokens?.accessTokenExpiresAt) {\n const storedTokens = storedData.tokens;\n // If stored token is newer and not expired\n if (!isTokenExpiredOrNearExpiry(storedTokens.accessTokenExpiresAt) && storedTokens.accessToken !== tokensRef.current?.accessToken) {\n tokensRef.current = storedTokens;\n return storedTokens;\n }\n }\n\n const currentTokens = tokensRef.current;\n if (!currentTokens?.refreshToken) {\n return null;\n }\n\n\n try {\n const response = await authApi.refreshAccessToken(currentTokens.refreshToken);\n const newTokens = response.tokens;\n\n // Update tokens immediately\n tokensRef.current = newTokens;\n\n // Persist to storage\n const latestStoredData = loadAuthFromStorage();\n if (latestStoredData) {\n saveAuthToStorage(newTokens, latestStoredData.user);\n }\n\n const newExpiryStr = Number.isFinite(newTokens.accessTokenExpiresAt) ? new Date(newTokens.accessTokenExpiresAt).toISOString() : \"invalid\";\n return newTokens;\n } catch (error: unknown) {\n\n // If it's a network error (e.g., backend restarting), we throw so callers can retry\n // instead of immediately assuming the refresh token is invalid and signing out.\n if (error instanceof Error && (error as { code?: string }).code === \"NETWORK_ERROR\") {\n throw error;\n }\n return null;\n } finally {\n refreshPromiseRef.current = null;\n }\n };\n\n refreshPromiseRef.current = executeRefresh();\n return refreshPromiseRef.current;\n }, []);\n\n // Schedule token refresh before expiry\n const scheduleTokenRefresh = useCallback((tokens: AuthTokens) => {\n if (refreshTimeoutRef.current) {\n clearTimeout(refreshTimeoutRef.current);\n }\n\n // Calculate when to refresh (2 minutes before expiry)\n const expiresAt = tokens.accessTokenExpiresAt;\n const refreshAt = expiresAt - TOKEN_REFRESH_BUFFER_MS;\n const timeUntilRefresh = refreshAt - Date.now();\n\n if (timeUntilRefresh <= 0) {\n // Token already expired or about to expire - refresh now\n refreshAccessToken().then(newTokens => {\n if (newTokens && isMountedRef.current) {\n scheduleTokenRefresh(newTokens);\n } else if (!newTokens && isMountedRef.current) {\n clearSessionAndSignOut();\n }\n });\n return;\n }\n\n\n refreshTimeoutRef.current = setTimeout(async () => {\n if (!isMountedRef.current) return;\n\n try {\n const newTokens = await refreshAccessToken();\n\n if (newTokens && isMountedRef.current) {\n scheduleTokenRefresh(newTokens);\n } else if (!newTokens && isMountedRef.current) {\n clearSessionAndSignOut();\n }\n } catch (error) {\n // Network error - try again shortly instead of logging out\n if (isMountedRef.current) {\n refreshTimeoutRef.current = setTimeout(() => {\n scheduleTokenRefresh(tokens);\n }, 10000);\n }\n }\n }, timeUntilRefresh);\n }, [refreshAccessToken, clearSessionAndSignOut]);\n\n // Get auth token for API requests (with automatic refresh if needed)\n const getAuthToken = useCallback(async (): Promise<string> => {\n // If still loading, throw - the UI should show a spinner\n if (initialLoading) {\n throw new Error(\"Auth is still loading\");\n }\n\n const currentTokens = tokensRef.current;\n if (!currentTokens) {\n throw new Error(\"User is not logged in\");\n }\n\n // Check if token is expired or about to expire\n if (isTokenExpiredOrNearExpiry(currentTokens.accessTokenExpiresAt)) {\n try {\n const newTokens = await refreshAccessToken();\n if (!newTokens) {\n clearSessionAndSignOut();\n throw new Error(\"Session expired. Please login again.\");\n }\n return newTokens.accessToken;\n } catch (error: unknown) {\n // If the error was a network error during refresh, just re-throw it \n // so the user isn't logged out locally and the network request fails naturally.\n if (error instanceof Error && (error as { code?: string }).code === \"NETWORK_ERROR\") {\n throw error;\n }\n clearSessionAndSignOut();\n throw error;\n }\n }\n\n return currentTokens.accessToken;\n }, [initialLoading, refreshAccessToken, clearSessionAndSignOut]);\n\n // Handle successful authentication\n const handleAuthSuccess = useCallback(async (userInfo: UserInfo, tokens: AuthTokens) => {\n tokensRef.current = tokens;\n let convertedUser = convertToUser(userInfo);\n\n // Apply custom roles if defineRolesFor provided\n if (defineRolesFor) {\n const customRoles = await defineRolesFor(convertedUser);\n if (customRoles) {\n convertedUser = { ...convertedUser, roles: customRoles.map(r => r.id) };\n }\n }\n\n // Save to localStorage for persistence\n saveAuthToStorage(tokens, userInfo);\n\n setUser(convertedUser);\n setAuthError(null);\n setAuthProviderError(null);\n setLoginSkipped(false);\n scheduleTokenRefresh(tokens);\n }, [scheduleTokenRefresh, defineRolesFor]);\n\n // Email/password login\n const emailPasswordLogin = useCallback(async (email: string, password: string) => {\n setAuthLoading(true);\n setAuthProviderError(null);\n\n try {\n const response = await authApi.login(email, password);\n await handleAuthSuccess(response.user, response.tokens);\n } catch (error: unknown) {\n setAuthProviderError(error as Error);\n throw error;\n } finally {\n setAuthLoading(false);\n }\n }, [handleAuthSuccess]);\n\n // Register new user\n const register = useCallback(async (email: string, password: string, displayName?: string) => {\n setAuthLoading(true);\n setAuthProviderError(null);\n\n try {\n const response = await authApi.register(email, password, displayName);\n await handleAuthSuccess(response.user, response.tokens);\n } catch (error: unknown) {\n setAuthProviderError(error as Error);\n throw error;\n } finally {\n setAuthLoading(false);\n }\n }, [handleAuthSuccess]);\n\n // Google login with ID token\n const googleLogin = useCallback(async (idToken: string) => {\n setAuthLoading(true);\n setAuthProviderError(null);\n\n try {\n const response = await authApi.googleLogin(idToken);\n await handleAuthSuccess(response.user, response.tokens);\n } catch (error: unknown) {\n setAuthProviderError(error as Error);\n throw error;\n } finally {\n setAuthLoading(false);\n }\n }, [handleAuthSuccess]);\n\n // Sign out\n const signOut = useCallback(async () => {\n try {\n if (tokensRef.current) {\n await authApi.logout(tokensRef.current.refreshToken);\n }\n } catch (error) {\n console.error(\"Logout error:\", error);\n } finally {\n clearSessionAndSignOut();\n }\n }, [clearSessionAndSignOut]);\n\n // Skip login\n const skipLogin = useCallback(() => {\n setLoginSkipped(true);\n setUser(null);\n }, []);\n\n // Forgot password - request reset email\n const forgotPassword = useCallback(async (email: string) => {\n setAuthLoading(true);\n setAuthProviderError(null);\n\n try {\n await authApi.forgotPassword(email);\n } catch (error: unknown) {\n setAuthProviderError(error as Error);\n throw error;\n } finally {\n setAuthLoading(false);\n }\n }, []);\n\n // Reset password using token\n const resetPassword = useCallback(async (token: string, password: string) => {\n setAuthLoading(true);\n setAuthProviderError(null);\n\n try {\n await authApi.resetPassword(token, password);\n } catch (error: unknown) {\n setAuthProviderError(error as Error);\n throw error;\n } finally {\n setAuthLoading(false);\n }\n }, []);\n\n // Change password for authenticated user\n const changePassword = useCallback(async (oldPassword: string, newPassword: string) => {\n setAuthLoading(true);\n setAuthProviderError(null);\n\n try {\n if (!tokensRef.current) {\n throw new Error(\"User is not logged in\");\n }\n await authApi.changePassword(tokensRef.current.accessToken, oldPassword, newPassword);\n // After password change, user needs to log in again (all sessions invalidated)\n clearSessionAndSignOut();\n } catch (error: unknown) {\n setAuthProviderError(error as Error);\n throw error;\n } finally {\n setAuthLoading(false);\n }\n }, [clearSessionAndSignOut]);\n\n // Update user profile\n const updateProfile = useCallback(async (displayName?: string, photoURL?: string) => {\n setAuthLoading(true);\n setAuthProviderError(null);\n\n try {\n if (!tokensRef.current) {\n throw new Error(\"User is not logged in\");\n }\n const response = await authApi.updateProfile(tokensRef.current.accessToken, displayName, photoURL);\n\n // Update local user state\n let convertedUser = convertToUser(response.user);\n if (defineRolesFor) {\n const customRoles = await defineRolesFor(convertedUser);\n if (customRoles) {\n convertedUser = { ...convertedUser, roles: customRoles.map(r => r.id) };\n }\n }\n\n // Update storage\n const storedData = loadAuthFromStorage();\n if (storedData) {\n saveAuthToStorage(storedData.tokens, response.user);\n }\n\n setUser(convertedUser);\n return convertedUser;\n } catch (error: unknown) {\n setAuthProviderError(error as Error);\n throw error;\n } finally {\n setAuthLoading(false);\n }\n }, [defineRolesFor]);\n\n // Fetch active sessions\n const fetchSessions = useCallback(async () => {\n try {\n if (!tokensRef.current) {\n throw new Error(\"User is not logged in\");\n }\n const response = await authApi.fetchSessions(tokensRef.current.accessToken, tokensRef.current.refreshToken);\n return response.sessions;\n } catch (error: unknown) {\n setAuthProviderError(error as Error);\n throw error;\n }\n }, []);\n\n // Revoke a session\n const revokeSession = useCallback(async (sessionId: string) => {\n try {\n if (!tokensRef.current) {\n throw new Error(\"User is not logged in\");\n }\n await authApi.revokeSession(tokensRef.current.accessToken, sessionId);\n // If the revoked session is the current one, the next API request will fail with 401\n // and trigger an auto-logout. Otherwise, it just removes it from the DB.\n } catch (error: unknown) {\n setAuthProviderError(error as Error);\n throw error;\n }\n }, []);\n\n // Restore auth state from localStorage on mount\n useEffect(() => {\n isMountedRef.current = true;\n\n const restoreAuth = async () => {\n\n // Fetch auth config (needsSetup, registrationEnabled, etc.)\n try {\n const config = await authApi.fetchAuthConfig();\n if (isMountedRef.current) {\n setAuthConfig(config);\n }\n } catch (e) {\n }\n\n const stored = loadAuthFromStorage();\n\n if (!stored) {\n setInitialLoading(false);\n return;\n }\n\n if (!stored.tokens?.refreshToken) {\n clearAuthFromStorage();\n setInitialLoading(false);\n return;\n }\n\n\n // Validate accessTokenExpiresAt is a valid number\n const expiresAt = stored.tokens.accessTokenExpiresAt;\n if (typeof expiresAt !== \"number\" || !Number.isFinite(expiresAt)) {\n clearAuthFromStorage();\n setInitialLoading(false);\n return;\n }\n\n\n // Check if access token is still valid\n if (!isTokenExpiredOrNearExpiry(stored.tokens.accessTokenExpiresAt)) {\n // Token is still valid - use it directly\n tokensRef.current = stored.tokens;\n\n let userToSet = convertToUser(stored.user);\n if (defineRolesFor) {\n const customRoles = await defineRolesFor(userToSet);\n if (customRoles) {\n userToSet = { ...userToSet, roles: customRoles.map(r => r.id) };\n }\n }\n\n setUser(userToSet);\n scheduleTokenRefresh(stored.tokens);\n setInitialLoading(false);\n return;\n }\n\n // Token is expired or near expiry - refresh it\n tokensRef.current = stored.tokens; // Set so refreshAccessToken can use it\n\n try {\n const newTokens = await refreshAccessToken();\n\n if (!newTokens) {\n clearAuthFromStorage();\n tokensRef.current = null;\n setInitialLoading(false);\n return;\n }\n\n if (!isMountedRef.current) return;\n\n // Fetch fresh user data from the server\n let userToSet: User;\n try {\n const meResponse = await authApi.getCurrentUser(newTokens.accessToken);\n\n if (!isMountedRef.current) return;\n\n const freshUserInfo = meResponse.user;\n\n // Update stored data with fresh user info\n saveAuthToStorage(newTokens, freshUserInfo);\n\n userToSet = convertToUser(freshUserInfo);\n\n if (defineRolesFor) {\n const customRoles = await defineRolesFor(userToSet);\n if (!isMountedRef.current) return;\n if (customRoles) {\n userToSet = { ...userToSet, roles: customRoles.map(r => r.id) };\n }\n }\n } catch (meError: unknown) {\n if (!isMountedRef.current) return;\n userToSet = convertToUser(stored.user);\n }\n\n if (!isMountedRef.current) return;\n\n setUser(userToSet);\n scheduleTokenRefresh(newTokens);\n } catch (error: unknown) {\n if (!isMountedRef.current) return;\n\n // Do not clear the session entirely if it's just a temporary network outage\n if (!(error instanceof Error && (error as { code?: string }).code === \"NETWORK_ERROR\")) {\n clearAuthFromStorage();\n tokensRef.current = null;\n }\n } finally {\n if (isMountedRef.current) {\n setInitialLoading(false);\n }\n }\n };\n\n restoreAuth();\n\n return () => {\n isMountedRef.current = false;\n };\n }, [scheduleTokenRefresh, defineRolesFor, refreshAccessToken]);\n\n // Handle visibility change - refresh token when user returns to tab\n useEffect(() => {\n const handleVisibilityChange = async () => {\n if (initialLoading) return;\n\n if (document.visibilityState === \"visible\" && tokensRef.current) {\n // Check if token needs refreshing\n if (isTokenExpiredOrNearExpiry(tokensRef.current.accessTokenExpiresAt)) {\n try {\n const newTokens = await refreshAccessToken();\n\n if (newTokens && isMountedRef.current) {\n scheduleTokenRefresh(newTokens);\n } else if (!newTokens && isMountedRef.current) {\n clearSessionAndSignOut();\n }\n } catch (error) {\n }\n }\n }\n };\n\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n return () => {\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n };\n }, [initialLoading, refreshAccessToken, scheduleTokenRefresh, clearSessionAndSignOut]);\n\n\n // Get currently configured API URL\n const getApiUrl = useCallback(() => {\n return authApi.getApiUrl();\n }, []);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n isMountedRef.current = false;\n if (refreshTimeoutRef.current) {\n clearTimeout(refreshTimeoutRef.current);\n }\n };\n }, []);\n\n // Revoke all sessions\n const revokeAllSessions = useCallback(async () => {\n try {\n if (!tokensRef.current) {\n throw new Error(\"User is not logged in\");\n }\n await authApi.revokeAllSessions(tokensRef.current.accessToken);\n clearSessionAndSignOut();\n } catch (error: unknown) {\n setAuthProviderError(error as Error);\n throw error;\n }\n }, [clearSessionAndSignOut]);\n\n return {\n user,\n authLoading,\n initialLoading,\n authError,\n authProviderError,\n loginSkipped,\n needsSetup: authConfig?.needsSetup ?? false,\n registrationEnabled: authConfig?.registrationEnabled ?? false,\n getAuthToken,\n getApiUrl,\n signOut,\n emailPasswordLogin,\n register,\n googleLogin,\n skipLogin,\n forgotPassword,\n resetPassword,\n changePassword,\n updateProfile,\n fetchSessions,\n revokeSession,\n revokeAllSessions,\n extra,\n setExtra\n };\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport { Role, User } from \"@rebasepro/core\";\n\n/**\n * UserManagement interface - compatible with @rebasepro/user_management\n * Defined inline to avoid dependency on that package\n */\nexport interface UserManagement<USER extends User = User> {\n loading: boolean;\n\n users: USER[];\n saveUser: (user: USER) => Promise<USER>;\n deleteUser: (user: USER) => Promise<void>;\n\n roles: Role[];\n saveRole: (role: Role) => Promise<void>;\n deleteRole: (role: Role) => Promise<void>;\n\n isAdmin?: boolean;\n allowDefaultRolesCreation?: boolean;\n includeCollectionConfigPermissions?: boolean;\n defineRolesFor: (user: User) => Promise<Role[] | undefined> | Role[] | undefined;\n getUser: (uid: string) => User | null;\n\n usersError?: Error;\n rolesError?: Error;\n bootstrapAdmin?: () => Promise<void>;\n}\n\nexport interface BackendUserManagementConfig {\n /**\n * Base API URL for the backend\n */\n apiUrl: string;\n\n /**\n * Function to get the current auth token\n */\n getAuthToken: () => Promise<string>;\n\n /**\n * Current logged-in user\n */\n currentUser?: User | null;\n}\n\ninterface ApiUser {\n uid: string;\n email: string;\n displayName?: string | null;\n photoURL?: string | null;\n roles: string[];\n createdAt?: string;\n updatedAt?: string;\n}\n\ninterface ApiRole {\n id: string;\n name: string;\n isAdmin?: boolean;\n config?: Record<string, any>;\n}\n\n/**\n * Convert API user to Rebase User\n * @param apiUser - The API user object\n * @param availableRoles - Optional array of available roles to look up names\n */\nfunction convertUser(apiUser: ApiUser): User {\n return {\n uid: apiUser.uid,\n email: apiUser.email,\n displayName: apiUser.displayName || null,\n photoURL: apiUser.photoURL || null,\n providerId: \"custom\",\n isAnonymous: false,\n roles: apiUser.roles\n };\n}\n\n/**\n * Convert API role to Rebase Role\n */\nfunction convertRole(apiRole: ApiRole): Role {\n return {\n id: apiRole.id,\n name: apiRole.name,\n isAdmin: apiRole.isAdmin ?? false,\n config: apiRole.config ?? undefined\n };\n}\n\n/**\n * Hook to manage users and roles via backend API\n * Compatible with Rebase UserManagement interface\n */\nexport function useBackendUserManagement(config: BackendUserManagementConfig): UserManagement {\n const { apiUrl, getAuthToken, currentUser } = config;\n\n const [users, setUsers] = useState<User[]>([]);\n const [roles, setRoles] = useState<Role[]>([]);\n const [loading, setLoading] = useState(true);\n const [usersError, setUsersError] = useState<Error | undefined>();\n const [rolesError, setRolesError] = useState<Error | undefined>();\n\n /**\n * Make authenticated API request\n */\n const apiRequest = useCallback(async (\n endpoint: string,\n method: string = \"GET\",\n body?: Record<string, unknown>,\n retryCount: number = 6,\n signal?: AbortSignal\n ): Promise<any> => {\n let lastError: Error | null = null;\n for (let attempt = 0; attempt < retryCount; attempt++) {\n if (signal?.aborted) {\n const error = new Error(\"Request aborted\");\n error.name = \"AbortError\";\n throw error;\n }\n\n try {\n const token = await getAuthToken();\n // Use /api/admin prefix for admin endpoints\n const response = await fetch(`${apiUrl}/api/admin${endpoint}`, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${token}`\n },\n body: body ? JSON.stringify(body) : undefined,\n signal\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let errorMessage = \"API request failed\";\n try {\n const errorJson = JSON.parse(errorText);\n errorMessage = errorJson.error?.message || errorMessage;\n } catch (e) {\n errorMessage = errorText || `HTTP error ${response.status}`;\n }\n\n const error = Object.assign(new Error(errorMessage), { status: response.status });\n throw error;\n }\n\n return await response.json();\n } catch (error: unknown) {\n if (error instanceof Error && error.name === \"AbortError\" || signal?.aborted) {\n throw error;\n }\n\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Retry conditions: Network errors (TypeError) OR 5xx Server Errors (Backend rebooting)\n const isNetworkError = error instanceof TypeError;\n const isServerError = typeof (error as { status?: number }).status === \"number\" && (error as { status: number }).status >= 500 && (error as { status: number }).status < 600;\n\n if (attempt < retryCount - 1 && (isNetworkError || isServerError)) {\n const delay = Math.min(1000 * Math.pow(2, attempt), 5000); // 1s, 2s, 4s...\n console.warn(`Admin API request to ${endpoint} failed, retrying in ${delay}ms...`);\n \n // Wait for delay or abort\n await new Promise<void>((resolve, reject) => {\n if (signal?.aborted) return reject(new Error(\"AbortError\"));\n const timer = setTimeout(resolve, delay);\n if (signal) {\n signal.addEventListener(\"abort\", () => {\n clearTimeout(timer);\n reject(new Error(\"AbortError\"));\n }, { once: true });\n }\n }).catch(() => {}); // Catch AbortError from wait\n \n if (signal?.aborted) {\n const abortError = new Error(\"Request aborted\");\n abortError.name = \"AbortError\";\n throw abortError;\n }\n continue;\n }\n\n console.error(\"Admin API error after retries:\", error);\n throw error;\n }\n }\n throw lastError;\n }, [apiUrl, getAuthToken]);\n\n /**\n * Load users from API\n * @param availableRoles - Optional roles array to resolve role names\n */\n const loadUsers = useCallback(async (signal?: AbortSignal) => {\n try {\n const data = await apiRequest(\"/users\", \"GET\", undefined, 6, signal);\n setUsers(data.users.map((u: ApiUser) => convertUser(u)));\n setUsersError(undefined);\n } catch (error: unknown) {\n if (error instanceof Error && error.name === \"AbortError\") return;\n console.error(\"Failed to load users:\", error);\n setUsersError(error instanceof Error ? error : new Error(String(error)));\n }\n }, [apiRequest]);\n\n /**\n * Load roles from API\n */\n const loadRoles = useCallback(async (signal?: AbortSignal) => {\n try {\n const data = await apiRequest(\"/roles\", \"GET\", undefined, 6, signal);\n setRoles(data.roles.map(convertRole));\n setRolesError(undefined);\n } catch (error: unknown) {\n if (error instanceof Error && error.name === \"AbortError\") return;\n console.error(\"Failed to load roles:\", error);\n setRolesError(error instanceof Error ? error : new Error(String(error)));\n }\n }, [apiRequest]);\n\n /**\n * Initial data load - only when user is logged in\n * Load roles first, then users (so role names can be resolved)\n */\n useEffect(() => {\n // Don't load if no user is logged in\n if (!currentUser) {\n setLoading(false);\n return;\n }\n\n const abortController = new AbortController();\n\n const load = async () => {\n setLoading(true);\n // Load roles first\n let loadedRoles: Role[] = [];\n try {\n const data = await apiRequest(\"/roles\", \"GET\", undefined, 6, abortController.signal);\n loadedRoles = data.roles.map(convertRole);\n setRoles(loadedRoles);\n setRolesError(undefined);\n } catch (error: unknown) {\n if (error instanceof Error && error.name !== \"AbortError\") {\n console.error(\"Failed to load roles:\", error);\n setRolesError(error);\n }\n }\n // Then load users if not aborted\n if (!abortController.signal.aborted) {\n await loadUsers(abortController.signal);\n }\n \n if (!abortController.signal.aborted) {\n setLoading(false);\n }\n };\n load();\n \n return () => {\n abortController.abort();\n };\n }, [currentUser, apiRequest, loadUsers]);\n\n /**\n * Save user (create or update)\n */\n const saveUser = useCallback(async (user: User): Promise<User> => {\n const roleIds = user.roles ?? [];\n\n // Check if user exists\n const existingUser = users.find(u => u.uid === user.uid);\n\n if (existingUser) {\n // Update\n const data = await apiRequest(`/users/${user.uid}`, \"PUT\", {\n email: user.email,\n displayName: user.displayName,\n roles: roleIds\n });\n const updated = convertUser(data.user);\n setUsers(prev => prev.map(u => u.uid === updated.uid ? updated : u));\n return updated;\n } else {\n // Create\n const data = await apiRequest(\"/users\", \"POST\", {\n email: user.email,\n displayName: user.displayName,\n roles: roleIds\n });\n const created = convertUser(data.user);\n setUsers(prev => [...prev, created]);\n return created;\n }\n }, [apiRequest, users, roles]);\n\n /**\n * Delete user\n */\n const deleteUser = useCallback(async (user: User): Promise<void> => {\n await apiRequest(`/users/${user.uid}`, \"DELETE\");\n setUsers(prev => prev.filter(u => u.uid !== user.uid));\n }, [apiRequest]);\n\n /**\n * Save role (create or update)\n */\n const saveRole = useCallback(async (role: Role): Promise<void> => {\n // Check if role exists\n const existingRole = roles.find(r => r.id === role.id);\n\n if (existingRole) {\n // Update\n const data = await apiRequest(`/roles/${role.id}`, \"PUT\", {\n name: role.name,\n isAdmin: role.isAdmin,\n config: role.config\n });\n const updated = convertRole(data.role);\n setRoles(prev => prev.map(r => r.id === updated.id ? updated : r));\n } else {\n // Create\n const data = await apiRequest(\"/roles\", \"POST\", {\n id: role.id,\n name: role.name,\n isAdmin: role.isAdmin ?? false,\n config: role.config\n });\n const created = convertRole(data.role);\n setRoles(prev => [...prev, created]);\n }\n }, [apiRequest, roles]);\n\n /**\n * Delete role\n */\n const deleteRole = useCallback(async (role: Role): Promise<void> => {\n await apiRequest(`/roles/${role.id}`, \"DELETE\");\n setRoles(prev => prev.filter(r => r.id !== role.id));\n }, [apiRequest]);\n\n /**\n * Get user by uid\n */\n const getUser = useCallback((uid: string): User | null => {\n return users.find(u => u.uid === uid) ?? null;\n }, [users]);\n\n /**\n * Define roles for a given user (for authController)\n */\n const defineRolesFor = useCallback(async (user: User): Promise<Role[] | undefined> => {\n // Find the user in our list\n const existingUser = users.find(u => u.uid === user.uid || u.email === user.email);\n if (!existingUser) return undefined;\n\n // Return roles from our cached role data (string IDs → full Role objects)\n const userRoleIds = existingUser.roles ?? [];\n return roles.filter(r => userRoleIds.includes(r.id));\n }, [users, roles]);\n\n /**\n * Check if current user is admin\n */\n const isAdmin = currentUser?.roles?.includes(\"admin\") ?? false;\n\n\n\n /**\n * Bootstrap default admin\n */\n const bootstrapAdmin = useCallback(async (): Promise<void> => {\n try {\n await apiRequest(\"/bootstrap\", \"POST\");\n // Reload users and roles after successful bootstrap\n const data = await apiRequest(\"/roles\");\n const loadedRoles = data.roles.map(convertRole);\n setRoles(loadedRoles);\n await loadUsers();\n } catch (error) {\n console.error(\"Failed to bootstrap admin:\", error);\n throw error;\n }\n }, [apiRequest, loadUsers]);\n\n return {\n loading,\n users,\n saveUser,\n deleteUser,\n roles,\n saveRole,\n deleteRole,\n isAdmin,\n allowDefaultRolesCreation: true,\n includeCollectionConfigPermissions: true,\n defineRolesFor,\n getUser,\n usersError,\n rolesError,\n bootstrapAdmin\n };\n}\n","import React, { ReactNode, useEffect, useRef, useState } from \"react\";\n\nimport { ArrowBackIcon, Button, CircularProgress, IconButton, MailIcon, TextField, Typography } from \"@rebasepro/ui\";\nimport { ErrorView, RebaseLogo, useModeController } from \"@rebasepro/core\";\n\nimport { RebaseAuthController } from \"../types\";\n\n/**\n * Props for RebaseLoginView\n */\nexport interface RebaseLoginViewProps {\n /**\n * Auth controller from useRebaseAuthController\n */\n authController: RebaseAuthController;\n\n /**\n * Path to the logo displayed in the login screen\n */\n logo?: string;\n\n /**\n * Enable the skip login button\n */\n allowSkipLogin?: boolean;\n\n /**\n * Disable the login buttons\n */\n disabled?: boolean;\n\n /**\n * Prevent users from creating new accounts\n */\n disableSignupScreen?: boolean;\n\n /**\n * Display this component when no user is found\n */\n noUserComponent?: ReactNode;\n\n /**\n * Display this component below the sign-in buttons\n */\n additionalComponent?: ReactNode;\n\n /**\n * Error message when user is not allowed access\n */\n notAllowedError?: string | Error;\n\n /**\n * Enable Google login button (requires googleClientId in hook)\n */\n googleEnabled?: boolean;\n\n /**\n * Google client ID for OAuth\n */\n googleClientId?: string;\n}\n\n/**\n * Login view component for custom JWT authentication\n * Based on MongoLoginView pattern from @rebasepro/mongodb\n */\nexport function RebaseLoginView({\n logo,\n authController,\n noUserComponent,\n disableSignupScreen = false,\n disabled = false,\n notAllowedError,\n googleEnabled = false,\n googleClientId\n}: RebaseLoginViewProps) {\n\n const modeState = useModeController();\n\n const [registrationSelected, setRegistrationSelected] = useState(false);\n const [passwordLoginSelected, setPasswordLoginSelected] = useState(false);\n const [forgotPasswordSelected, setForgotPasswordSelected] = useState(false);\n\n // Auto-show setup form when no users exist (bootstrap mode)\n const isBootstrapMode = authController.needsSetup;\n\n function buildErrorView() {\n if (!authController.authProviderError) return null;\n if (authController.user != null) return null;\n return <ErrorView error={authController.authProviderError.message ?? authController.authProviderError} />;\n }\n\n let logoComponent;\n if (logo) {\n logoComponent = <img src={logo}\n style={{\n height: \"100%\",\n width: \"100%\",\n objectFit: \"cover\"\n }}\n alt={\"Logo\"} />;\n } else {\n logoComponent = <RebaseLogo />;\n }\n\n let notAllowedMessage: string | undefined;\n if (notAllowedError) {\n if (typeof notAllowedError === \"string\") {\n notAllowedMessage = notAllowedError;\n } else if (notAllowedError instanceof Error) {\n notAllowedMessage = notAllowedError.message;\n } else {\n notAllowedMessage = \"It looks like you don't have access to the CMS, based on the specified Authenticator configuration\";\n }\n }\n\n return (\n <div className=\"flex flex-col justify-center items-center min-h-screen min-w-full p-2\">\n <div className=\"flex flex-col items-center w-full max-w-md\">\n <div className={`m-4 p-4 w-64 h-64`}>\n {logoComponent}\n </div>\n {notAllowedMessage &&\n <div className=\"p-4\">\n <ErrorView error={notAllowedMessage} />\n </div>\n }\n {!forgotPasswordSelected && buildErrorView()}\n\n {/* Bootstrap mode: show setup form directly */}\n {isBootstrapMode && !authController.user && (\n <LoginForm\n authController={authController}\n registrationMode={true}\n onClose={() => {}}\n onForgotPassword={() => {}}\n mode={modeState.mode}\n noUserComponent={noUserComponent}\n disableSignupScreen={false}\n bootstrapMode={true}\n />\n )}\n\n {/* Normal mode: show login/register buttons */}\n {!isBootstrapMode && !passwordLoginSelected && !registrationSelected && !forgotPasswordSelected && (\n <>\n <LoginButton\n disabled={disabled}\n text={\"Email/password\"}\n icon={<MailIcon />}\n onClick={() => {\n setRegistrationSelected(false);\n setPasswordLoginSelected(true);\n setForgotPasswordSelected(false);\n }}\n />\n {googleEnabled && googleClientId && (\n <GoogleLoginButton\n disabled={disabled}\n googleClientId={googleClientId}\n authController={authController}\n />\n )}\n {!disableSignupScreen && authController.registrationEnabled && (\n <LoginButton\n disabled={disabled}\n text={\"Create account\"}\n icon={<MailIcon />}\n onClick={() => {\n setRegistrationSelected(true);\n setPasswordLoginSelected(false);\n setForgotPasswordSelected(false);\n }}\n />\n )}\n </>\n )}\n {!isBootstrapMode && (passwordLoginSelected || registrationSelected) && !forgotPasswordSelected && (\n <LoginForm\n authController={authController}\n registrationMode={registrationSelected}\n onClose={() => {\n setRegistrationSelected(false);\n setPasswordLoginSelected(false);\n }}\n onForgotPassword={() => {\n setForgotPasswordSelected(true);\n setPasswordLoginSelected(false);\n }}\n mode={modeState.mode}\n noUserComponent={noUserComponent}\n disableSignupScreen={disableSignupScreen}\n />\n )}\n {forgotPasswordSelected && (\n <ForgotPasswordForm\n authController={authController}\n onClose={() => {\n setForgotPasswordSelected(false);\n setPasswordLoginSelected(true);\n }}\n />\n )}\n </div>\n </div>\n );\n}\n\nfunction LoginButton({\n icon,\n onClick,\n text,\n disabled\n}: { icon: React.ReactNode, onClick: () => void, text: string, disabled?: boolean }) {\n return (\n <div className=\"m-2 w-full\">\n <Button\n disabled={disabled}\n className={`w-full`}\n onClick={onClick}>\n <div className=\"flex items-center justify-center p-2 w-full h-8\">\n <div className=\"flex flex-col items-center justify-center w-8\">\n {icon}\n </div>\n <div className=\"grow pl-2 text-center\">\n {text}\n </div>\n </div>\n </Button>\n </div>\n )\n}\n\nfunction GoogleLoginButton({\n disabled,\n googleClientId,\n authController\n}: {\n disabled?: boolean,\n googleClientId: string,\n authController: RebaseAuthController\n}) {\n const handleGoogleLogin = async () => {\n try {\n const google = (window as unknown as { google?: { accounts: { id: { initialize: (config: { client_id: string; callback: (response: { credential: string }) => void }) => void; prompt: () => void } } } }).google;\n if (!google) {\n console.error(\"Google Sign-In not loaded\");\n return;\n }\n\n google.accounts.id.initialize({\n client_id: googleClientId,\n callback: async (response: { credential: string }) => {\n try {\n await authController.googleLogin(response.credential);\n } catch (err: unknown) {\n console.error(\"Google login error:\", err);\n }\n }\n });\n\n google.accounts.id.prompt();\n } catch (err: unknown) {\n console.error(\"Google login error:\", err);\n }\n };\n\n return (\n <div className=\"m-2 w-full\">\n <Button\n disabled={disabled}\n className={`w-full`}\n onClick={handleGoogleLogin}>\n <div className=\"flex items-center justify-center p-2 w-full h-8\">\n <div className=\"flex flex-col items-center justify-center w-8\">\n <svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\">\n <path fill=\"currentColor\"\n d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z\" />\n <path fill=\"currentColor\"\n d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" />\n <path fill=\"currentColor\"\n d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\" />\n <path fill=\"currentColor\"\n d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" />\n </svg>\n </div>\n <div className=\"grow pl-2 text-center\">\n Continue with Google\n </div>\n </div>\n </Button>\n </div>\n )\n}\n\nfunction LoginForm({\n onClose,\n onForgotPassword,\n authController,\n mode,\n registrationMode,\n noUserComponent,\n disableSignupScreen,\n bootstrapMode = false\n}: {\n onClose: () => void,\n onForgotPassword: () => void,\n authController: RebaseAuthController,\n mode: \"light\" | \"dark\",\n registrationMode: boolean,\n noUserComponent?: ReactNode,\n disableSignupScreen: boolean,\n bootstrapMode?: boolean\n}) {\n\n const passwordRef = useRef<HTMLInputElement | null>(null);\n\n const [email, setEmail] = useState<string>();\n const [password, setPassword] = useState<string>();\n const [displayName, setDisplayName] = useState<string>();\n\n const loginMode = !registrationMode;\n\n useEffect(() => {\n if (!document) return;\n const escFunction = (event: KeyboardEvent) => {\n if (event.keyCode === 27) {\n onClose();\n }\n };\n document.addEventListener(\"keydown\", escFunction, false);\n return () => {\n document.removeEventListener(\"keydown\", escFunction, false);\n };\n }, [onClose]);\n\n function handleEnterPassword() {\n if (email && password) {\n authController.emailPasswordLogin(email, password);\n }\n }\n\n function handleRegistration() {\n if (email && password) {\n authController.register(email, password, displayName);\n }\n }\n\n const onBackPressed = () => {\n onClose();\n }\n\n const handleSubmit = (event: React.FormEvent) => {\n event.preventDefault();\n if (registrationMode)\n handleRegistration();\n else\n handleEnterPassword();\n }\n\n const label = bootstrapMode\n ? \"Welcome! Create your admin account\"\n : registrationMode\n ? \"Create a new account\"\n : \"Enter your email and password\";\n\n const button = registrationMode ? \"Create account\" : \"Login\";\n\n return (\n <form onSubmit={handleSubmit} className=\"flex flex-col items-center w-full max-w-[500px] gap-2\">\n {!bootstrapMode && (\n <div className=\"w-full\">\n <IconButton onClick={onBackPressed}>\n <ArrowBackIcon />\n </IconButton>\n </div>\n )}\n\n <div className=\"flex justify-center w-full py-2\">\n <Typography align={\"center\"} variant={bootstrapMode ? \"subtitle1\" : \"subtitle2\"}>{label}</Typography>\n </div>\n\n {bootstrapMode && (\n <Typography variant=\"body2\" className=\"text-gray-500 text-center mb-2\">\n No users found. Create the first account to get started. This account will have admin privileges.\n </Typography>\n )}\n\n {registrationMode && (\n <div className=\"w-full\">\n <TextField placeholder=\"Display Name (optional)\"\n className={\"w-full\"}\n value={displayName ?? \"\"}\n disabled={authController.initialLoading}\n type=\"text\"\n onChange={(event) => setDisplayName(event.target.value)} />\n </div>\n )}\n\n <div className=\"w-full\">\n <TextField placeholder=\"Email\"\n className={\"w-full\"}\n autoFocus\n value={email ?? \"\"}\n disabled={authController.initialLoading}\n type=\"email\"\n onChange={(event) => setEmail(event.target.value)} />\n </div>\n\n <div className=\"w-full\">\n {registrationMode && noUserComponent}\n </div>\n\n <div className=\"w-full\">\n <TextField placeholder=\"Password\"\n className={\"w-full\"}\n value={password ?? \"\"}\n disabled={authController.initialLoading}\n inputRef={passwordRef}\n type=\"password\"\n onChange={(event) => setPassword(event.target.value)} />\n </div>\n\n {registrationMode && (\n <Typography variant=\"caption\" className=\"text-gray-500 text-sm\">\n Password: 8+ chars, uppercase, lowercase, number\n </Typography>\n )}\n\n {!registrationMode && (\n <div className=\"w-full text-right\">\n <button\n type=\"button\"\n className=\"text-sm text-blue-600 hover:text-blue-800 hover:underline\"\n onClick={onForgotPassword}\n >\n Forgot password?\n </button>\n </div>\n )}\n\n <div className=\"flex justify-end items-center w-full gap-2\">\n {authController.authLoading && (\n <CircularProgress />\n )}\n <Button type=\"submit\" disabled={authController.authLoading}>\n {button}\n </Button>\n </div>\n </form>\n );\n}\n\nfunction ForgotPasswordForm({\n onClose,\n authController\n}: {\n onClose: () => void,\n authController: RebaseAuthController\n}) {\n const [email, setEmail] = useState<string>(\"\");\n const [submitted, setSubmitted] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (!document) return;\n const escFunction = (event: KeyboardEvent) => {\n if (event.keyCode === 27) {\n onClose();\n }\n };\n document.addEventListener(\"keydown\", escFunction, false);\n return () => {\n document.removeEventListener(\"keydown\", escFunction, false);\n };\n }, [onClose]);\n\n const handleSubmit = async (event: React.FormEvent) => {\n event.preventDefault();\n setError(null);\n\n if (!email) {\n setError(\"Please enter your email address\");\n return;\n }\n\n try {\n await authController.forgotPassword(email);\n setSubmitted(true);\n } catch (err: unknown) {\n // Check for EMAIL_NOT_CONFIGURED error\n if (err instanceof Error && (err as { code?: string }).code === \"EMAIL_NOT_CONFIGURED\") {\n setError(\"Password reset is not available. Please contact your administrator.\");\n } else {\n // Still show success (security: don't reveal if email exists)\n setSubmitted(true);\n }\n }\n };\n\n if (submitted) {\n return (\n <div className=\"flex flex-col items-center w-full max-w-[500px] gap-4 p-4\">\n <div className=\"w-full\">\n <IconButton onClick={onClose}>\n <ArrowBackIcon />\n </IconButton>\n </div>\n\n <div className=\"text-center\">\n <Typography variant=\"subtitle1\" className=\"mb-4\">\n Check your email\n </Typography>\n <Typography variant=\"body2\" className=\"text-gray-600\">\n If an account exists for <strong>{email}</strong>, you'll receive a password reset link shortly.\n </Typography>\n </div>\n\n <Button onClick={onClose} className=\"mt-4\">\n Back to login\n </Button>\n </div>\n );\n }\n\n return (\n <form onSubmit={handleSubmit} className=\"flex flex-col items-center w-full max-w-[500px] gap-2\">\n <div className=\"w-full\">\n <IconButton onClick={onClose}>\n <ArrowBackIcon />\n </IconButton>\n </div>\n\n <div className=\"flex justify-center w-full py-2\">\n <Typography align={\"center\"} variant={\"subtitle2\"}>Reset your password</Typography>\n </div>\n\n <Typography variant=\"body2\" className=\"text-gray-600 text-center mb-2\">\n Enter your email address and we'll send you a link to reset your password.\n </Typography>\n\n {error && (\n <div className=\"w-full\">\n <ErrorView error={error} />\n </div>\n )}\n\n <div className=\"w-full\">\n <TextField\n placeholder=\"Email\"\n className={\"w-full\"}\n autoFocus\n value={email}\n type=\"email\"\n onChange={(event) => setEmail(event.target.value)}\n />\n </div>\n\n <div className=\"flex justify-end items-center w-full gap-2 mt-2\">\n {authController.authLoading && (\n <CircularProgress />\n )}\n <Button type=\"submit\" disabled={authController.authLoading || !email}>\n Send reset link\n </Button>\n </div>\n </form>\n );\n}\n\n","import React, { useCallback, useMemo, useState } from \"react\";\nimport { CMSView, EntityCollection, FieldCaption, Role, SecurityRule, User, useSnackbarController, ConfirmationDialog, useAuthController, useCollectionRegistryController } from \"@rebasepro/core\";\n\nimport {\n AddIcon,\n Button,\n Chip,\n Container,\n DeleteIcon,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n IconButton,\n Paper,\n Table,\n TableBody,\n TableCell,\n TableHeader,\n TableRow,\n TextField,\n Typography,\n CircularProgress,\n CenteredView,\n Tooltip,\n Checkbox,\n MultiSelect,\n MultiSelectItem,\n LoadingButton,\n getColorSchemeForSeed,\n ChipColorScheme,\n ChipColorKey\n} from \"@rebasepro/ui\";\nimport { UserManagement } from \"../hooks/useBackendUserManagement\";\n\ninterface AdminViewsProps {\n userManagement: UserManagement;\n apiUrl: string;\n getAuthToken: () => Promise<string>;\n}\n\n/**\n * Create admin views for user and role management\n */\nexport function createUserManagementAdminViews({ userManagement, apiUrl, getAuthToken }: AdminViewsProps): CMSView[] {\n return [\n {\n slug: \"dev/users\",\n name: \"CMS Users\",\n group: \"Admin\",\n icon: \"face\",\n view: <UsersView userManagement={userManagement} apiUrl={apiUrl} getAuthToken={getAuthToken} />\n },\n {\n slug: \"dev/roles\",\n name: \"Roles\",\n group: \"Admin\",\n icon: \"gpp_good\",\n view: <RolesView userManagement={userManagement} />\n }\n ];\n}\n\n// ============================================\n// RoleChip Component (matches original)\n// ============================================\nfunction RoleChip({ role }: { role: Role }) {\n let colorScheme: ChipColorScheme | ChipColorKey;\n if (role.isAdmin) {\n colorScheme = \"blueDarker\";\n } else if (role.id === \"editor\") {\n colorScheme = \"yellowLight\";\n } else if (role.id === \"viewer\") {\n colorScheme = \"grayLight\";\n } else {\n colorScheme = getColorSchemeForSeed(role.id);\n }\n\n return (\n <Chip colorScheme={colorScheme} key={role.id}>\n {role.name}\n </Chip>\n );\n}\n\n// ============================================\n// UsersView Component\n// ============================================\nexport function UsersView({ userManagement, apiUrl, getAuthToken }: {\n userManagement: UserManagement;\n apiUrl: string;\n getAuthToken: () => Promise<string>;\n}) {\n const { users, roles, saveUser, deleteUser, loading } = userManagement;\n const snackbarController = useSnackbarController();\n const { user: loggedInUser } = useAuthController();\n\n const [dialogOpen, setDialogOpen] = useState(false);\n const [selectedUser, setSelectedUser] = useState<User | undefined>();\n const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);\n const [userToDelete, setUserToDelete] = useState<User | undefined>();\n const [deleteInProgress, setDeleteInProgress] = useState(false);\n const [formKey, setFormKey] = useState(0);\n const [bootstrapping, setBootstrapping] = useState(false);\n\n // Check if any admin exists\n const hasAdmin = users.some(u => u.roles?.includes(\"admin\"));\n\n const handleBootstrap = async () => {\n setBootstrapping(true);\n try {\n const token = await getAuthToken();\n const response = await fetch(`${apiUrl}/api/admin/bootstrap`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${token}`\n }\n });\n const data = await response.json();\n if (!response.ok) {\n throw new Error(data.error?.message || \"Bootstrap failed\");\n }\n snackbarController.open({ type: \"success\", message: \"You are now an admin! Refreshing...\" });\n // Reload to get new roles\n window.location.reload();\n } catch (error: unknown) {\n snackbarController.open({ type: \"error\", message: error instanceof Error ? error.message : \"Failed to bootstrap admin\" });\n } finally {\n setBootstrapping(false);\n }\n };\n\n const handleAddUser = () => {\n setSelectedUser(undefined);\n setFormKey(k => k + 1);\n setDialogOpen(true);\n };\n\n const handleEditUser = (user: User) => {\n setSelectedUser(user);\n setDialogOpen(true);\n };\n\n const handleClose = () => {\n setDialogOpen(false);\n setSelectedUser(undefined);\n };\n\n const handleDelete = async () => {\n if (!userToDelete) return;\n setDeleteInProgress(true);\n try {\n await deleteUser(userToDelete);\n snackbarController.open({ type: \"success\", message: \"User deleted successfully\" });\n setDeleteConfirmOpen(false);\n setUserToDelete(undefined);\n } catch (error: unknown) {\n snackbarController.open({ type: \"error\", message: error instanceof Error ? error.message : \"Error deleting user\" });\n } finally {\n setDeleteInProgress(false);\n }\n };\n\n if (loading) {\n return <CenteredView><CircularProgress /></CenteredView>;\n }\n\n return (\n <Container className=\"w-full flex flex-col py-4 gap-4\" maxWidth={\"6xl\"}>\n {/* Bootstrap warning when no admins */}\n {!hasAdmin && loggedInUser && (\n <div className=\"bg-yellow-100 dark:bg-yellow-900 border border-yellow-400 dark:border-yellow-700 rounded p-4 flex items-center justify-between\">\n <div>\n <Typography variant=\"label\" className=\"text-yellow-800 dark:text-yellow-200\">\n No admin users exist. You can make yourself an admin.\n </Typography>\n </div>\n <Button\n onClick={handleBootstrap}\n disabled={bootstrapping}\n >\n {bootstrapping ? <CircularProgress size=\"small\" /> : \"Make me admin\"}\n </Button>\n </div>\n )}\n\n <div className=\"flex items-center mt-12\">\n <Typography gutterBottom variant=\"h4\" className=\"grow\" component=\"h4\">\n Users\n </Typography>\n <Button startIcon={<AddIcon />} onClick={handleAddUser}>\n Add user\n </Button>\n </div>\n\n <div className=\"overflow-auto\">\n <Table className=\"w-full\">\n <TableHeader>\n <TableCell header className=\"truncate w-16\"></TableCell>\n <TableCell header>Email</TableCell>\n <TableCell header>Name</TableCell>\n <TableCell header>Roles</TableCell>\n </TableHeader>\n <TableBody>\n {users.map(user => (\n <TableRow key={user.uid} onClick={() => handleEditUser(user)}>\n <TableCell style={{ width: \"64px\" }}>\n <Tooltip asChild title=\"Delete this user\">\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n setUserToDelete(user);\n setDeleteConfirmOpen(true);\n }}>\n <DeleteIcon />\n </IconButton>\n </Tooltip>\n </TableCell>\n <TableCell>{user.email}</TableCell>\n <TableCell className=\"font-medium\">{user.displayName}</TableCell>\n <TableCell>\n <div className=\"flex flex-wrap gap-2\">\n {user.roles?.map((roleId: string) => {\n const role = roles.find(r => r.id === roleId);\n return role ? <RoleChip key={roleId} role={role} /> : <span key={roleId}>{roleId}</span>;\n })}\n </div>\n </TableCell>\n </TableRow>\n ))}\n\n {users.length === 0 && (\n <TableRow>\n <TableCell colspan={4}>\n <CenteredView className=\"flex flex-col gap-4 my-8 items-center\">\n <Typography variant=\"label\">\n There are no users yet\n </Typography>\n </CenteredView>\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </div>\n\n {/* User Edit Dialog */}\n <UserDetailsForm\n key={selectedUser?.uid ?? `new-${formKey}`}\n open={dialogOpen}\n user={selectedUser}\n roles={roles}\n saveUser={saveUser}\n handleClose={handleClose}\n />\n\n {/* Delete Confirmation */}\n <ConfirmationDialog\n open={deleteConfirmOpen}\n loading={deleteInProgress}\n onAccept={handleDelete}\n onCancel={() => { setDeleteConfirmOpen(false); setUserToDelete(undefined); }}\n title={<>Delete?</>}\n body={<>Are you sure you want to delete this user?</>}\n />\n </Container>\n );\n}\n\n// ============================================\n// UserDetailsForm Component (matches original)\n// ============================================\nfunction UserDetailsForm({\n open,\n user: userProp,\n roles,\n saveUser,\n handleClose\n}: {\n open: boolean;\n user?: User;\n roles: Role[];\n saveUser: (user: User) => Promise<User>;\n handleClose: () => void;\n}) {\n const snackbarController = useSnackbarController();\n const isNewUser = !userProp;\n\n const [displayName, setDisplayName] = useState(userProp?.displayName || \"\");\n const [email, setEmail] = useState(userProp?.email || \"\");\n const [selectedRoleIds, setSelectedRoleIds] = useState<string[]>(\n userProp?.roles || [\"editor\"]\n );\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [errors, setErrors] = useState<{ displayName?: string; email?: string; roles?: string }>({});\n const [submitCount, setSubmitCount] = useState(0);\n\n const validate = () => {\n const newErrors: typeof errors = {};\n if (!displayName) newErrors.displayName = \"Required\";\n if (!email) newErrors.email = \"Required\";\n else if (!/\\S+@\\S+\\.\\S+/.test(email)) newErrors.email = \"Invalid email\";\n if (selectedRoleIds.length === 0) newErrors.roles = \"At least one role is required\";\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setSubmitCount(c => c + 1);\n\n if (!validate()) return;\n\n setIsSubmitting(true);\n try {\n const userToSave: User = {\n uid: userProp?.uid || crypto.randomUUID(),\n email,\n displayName: displayName || null,\n photoURL: userProp?.photoURL || null,\n providerId: \"custom\",\n isAnonymous: false,\n roles: selectedRoleIds\n };\n await saveUser(userToSave);\n handleClose();\n } catch (error: unknown) {\n snackbarController.open({ type: \"error\", message: error instanceof Error ? error.message : \"Failed to save user\" });\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const dirty = isNewUser ||\n displayName !== (userProp?.displayName || \"\") ||\n email !== (userProp?.email || \"\") ||\n JSON.stringify(selectedRoleIds.sort()) !== JSON.stringify((userProp?.roles || []).sort());\n\n return (\n <Dialog open={open} onOpenChange={(open) => !open ? handleClose() : undefined} maxWidth=\"4xl\">\n <form onSubmit={handleSubmit} autoComplete=\"off\" noValidate\n style={{ display: \"flex\", flexDirection: \"column\", position: \"relative\", height: \"100%\" }}>\n\n <DialogTitle variant=\"h4\" gutterBottom={false}>\n User\n </DialogTitle>\n\n <DialogContent className=\"h-full grow\">\n <div className=\"grid grid-cols-12 gap-4\">\n <div className=\"col-span-12\">\n <TextField\n name=\"displayName\"\n required\n error={submitCount > 0 && Boolean(errors.displayName)}\n value={displayName}\n onChange={(e) => setDisplayName(e.target.value)}\n label=\"Name\"\n />\n <FieldCaption>\n {submitCount > 0 && errors.displayName ? errors.displayName : \"Name of this user\"}\n </FieldCaption>\n </div>\n\n <div className=\"col-span-12\">\n <TextField\n required\n error={submitCount > 0 && Boolean(errors.email)}\n name=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n label=\"Email\"\n disabled={!isNewUser}\n />\n <FieldCaption>\n {submitCount > 0 && errors.email ? errors.email : \"Email of this user\"}\n </FieldCaption>\n </div>\n\n <div className=\"col-span-12\">\n <MultiSelect\n className=\"w-full\"\n label=\"Roles\"\n value={selectedRoleIds}\n onValueChange={(value: string[]) => setSelectedRoleIds(value)}\n >\n {roles.map(role => (\n <MultiSelectItem key={role.id} value={role.id}>\n <RoleChip role={role} />\n </MultiSelectItem>\n ))}\n </MultiSelect>\n </div>\n </div>\n </DialogContent>\n\n <DialogActions>\n <Button variant=\"text\" onClick={handleClose}>\n Cancel\n </Button>\n <LoadingButton\n variant=\"filled\"\n type=\"submit\"\n disabled={!dirty}\n loading={isSubmitting}\n >\n {isNewUser ? \"Create user\" : \"Update\"}\n </LoadingButton>\n </DialogActions>\n </form>\n </Dialog>\n );\n}\n\n// ============================================\n// RolesView Component\n// ============================================\nexport function RolesView({ userManagement }: { userManagement: UserManagement }) {\n const { roles, saveRole, deleteRole, loading, allowDefaultRolesCreation } = userManagement;\n const snackbarController = useSnackbarController();\n\n const [dialogOpen, setDialogOpen] = useState(false);\n const [selectedRole, setSelectedRole] = useState<Role | undefined>();\n const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);\n const [roleToDelete, setRoleToDelete] = useState<Role | undefined>();\n const [deleteInProgress, setDeleteInProgress] = useState(false);\n\n const handleAddRole = () => {\n setSelectedRole(undefined);\n setDialogOpen(true);\n };\n\n const handleEditRole = (role: Role) => {\n setSelectedRole(role);\n setDialogOpen(true);\n };\n\n const handleClose = () => {\n setDialogOpen(false);\n setSelectedRole(undefined);\n };\n\n const handleDelete = async () => {\n if (!roleToDelete) return;\n setDeleteInProgress(true);\n try {\n await deleteRole(roleToDelete);\n snackbarController.open({ type: \"success\", message: \"Role deleted successfully\" });\n setDeleteConfirmOpen(false);\n setRoleToDelete(undefined);\n } catch (error: unknown) {\n snackbarController.open({ type: \"error\", message: error instanceof Error ? error.message : \"Error deleting role\" });\n } finally {\n setDeleteInProgress(false);\n }\n };\n\n const createDefaultRoles = () => {\n const defaultRoles: Role[] = [\n { id: \"admin\", name: \"Admin\", isAdmin: true },\n { id: \"editor\", name: \"Editor\", isAdmin: false },\n { id: \"viewer\", name: \"Viewer\", isAdmin: false }\n ];\n defaultRoles.forEach(role => saveRole(role));\n };\n\n if (loading) {\n return <CenteredView><CircularProgress /></CenteredView>;\n }\n\n return (\n <Container className=\"w-full flex flex-col py-4 gap-4\" maxWidth={\"6xl\"}>\n <div className=\"flex items-center mt-12\">\n <Typography gutterBottom variant=\"h4\" className=\"grow\" component=\"h4\">\n Roles\n </Typography>\n <Button startIcon={<AddIcon />} onClick={handleAddRole}>\n Add role\n </Button>\n </div>\n\n <div className=\"w-full overflow-auto\">\n <Table className=\"w-full\">\n <TableHeader>\n <TableCell header className=\"w-16\"></TableCell>\n <TableCell header>Role</TableCell>\n <TableCell header className=\"items-center\">Is Admin</TableCell>\n </TableHeader>\n <TableBody>\n {roles.map(role => {\n return (\n <TableRow key={role.id} onClick={() => handleEditRole(role)}>\n <TableCell style={{ width: \"64px\" }}>\n {!role.isAdmin && (\n <Tooltip asChild title=\"Delete this role\">\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n setRoleToDelete(role);\n setDeleteConfirmOpen(true);\n }}>\n <DeleteIcon />\n </IconButton>\n </Tooltip>\n )}\n </TableCell>\n <TableCell>\n <RoleChip role={role} />\n </TableCell>\n <TableCell className=\"items-center\">\n <Checkbox checked={role.isAdmin ?? false} />\n </TableCell>\n </TableRow>\n );\n })}\n\n {roles.length === 0 && (\n <TableRow>\n <TableCell colspan={4}>\n <CenteredView className=\"flex flex-col gap-4 my-8 items-center\">\n <Typography variant=\"label\">\n You don't have any roles yet.\n </Typography>\n {allowDefaultRolesCreation && (\n <Button onClick={createDefaultRoles}>\n Create default roles\n </Button>\n )}\n </CenteredView>\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </div>\n\n {/* Role Edit Dialog */}\n <RoleDetailsForm\n key={selectedRole?.id ?? \"new\"}\n open={dialogOpen}\n role={selectedRole}\n saveRole={saveRole}\n handleClose={handleClose}\n />\n\n {/* Delete Confirmation */}\n <ConfirmationDialog\n open={deleteConfirmOpen}\n loading={deleteInProgress}\n onAccept={handleDelete}\n onCancel={() => { setDeleteConfirmOpen(false); setRoleToDelete(undefined); }}\n title={<>Delete?</>}\n body={<>Are you sure you want to delete this role?</>}\n />\n </Container>\n );\n}\n\n// ============================================\n// RoleDetailsForm Component\n// ============================================\nfunction RoleDetailsForm({\n open,\n role: roleProp,\n saveRole,\n handleClose\n}: {\n open: boolean;\n role?: Role;\n saveRole: (role: Role) => Promise<void>;\n handleClose: () => void;\n}) {\n const snackbarController = useSnackbarController();\n const isNewRole = !roleProp;\n\n const [roleId, setRoleId] = useState(roleProp?.id || \"\");\n const [roleName, setRoleName] = useState(roleProp?.name || \"\");\n const [isAdmin, setIsAdmin] = useState(roleProp?.isAdmin ?? false);\n\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [errors, setErrors] = useState<{ id?: string; name?: string }>({});\n const [submitCount, setSubmitCount] = useState(0);\n\n const validate = () => {\n const newErrors: typeof errors = {};\n if (!roleId) newErrors.id = \"Required\";\n if (!roleName) newErrors.name = \"Required\";\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setSubmitCount(c => c + 1);\n\n if (!validate()) return;\n\n setIsSubmitting(true);\n try {\n await saveRole({\n id: roleId,\n name: roleName,\n isAdmin\n });\n handleClose();\n } catch (error: unknown) {\n snackbarController.open({ type: \"error\", message: error instanceof Error ? error.message : \"Failed to save role\" });\n } finally {\n setIsSubmitting(false);\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={(open) => !open ? handleClose() : undefined} maxWidth=\"6xl\">\n <form onSubmit={handleSubmit} autoComplete=\"off\" noValidate\n style={{ display: \"flex\", flexDirection: \"column\", position: \"relative\", height: \"100%\" }}>\n\n <DialogTitle variant=\"h4\" gutterBottom={false}>\n Role\n </DialogTitle>\n\n <DialogContent className=\"h-full grow overflow-y-auto\">\n <div className=\"grid grid-cols-12 gap-4\">\n <div className=\"col-span-12 sm:col-span-4\">\n <TextField\n name=\"id\"\n required\n error={submitCount > 0 && Boolean(errors.id)}\n value={roleId}\n onChange={(e) => setRoleId(e.target.value)}\n label=\"Role ID\"\n disabled={!isNewRole}\n />\n <FieldCaption>\n {submitCount > 0 && errors.id ? errors.id : \"Unique identifier for this role\"}\n </FieldCaption>\n </div>\n\n <div className=\"col-span-12 sm:col-span-4\">\n <TextField\n name=\"name\"\n required\n error={submitCount > 0 && Boolean(errors.name)}\n value={roleName}\n onChange={(e) => setRoleName(e.target.value)}\n label=\"Role Name\"\n />\n <FieldCaption>\n {submitCount > 0 && errors.name ? errors.name : \"Display name for this role\"}\n </FieldCaption>\n </div>\n\n <div className=\"col-span-12 sm:col-span-4 flex items-start pt-2\">\n <label className=\"flex items-center gap-2 cursor-pointer mt-3\">\n <Checkbox\n checked={isAdmin}\n onCheckedChange={(checked) => setIsAdmin(Boolean(checked))}\n />\n <span className=\"font-medium\">Is Admin</span>\n </label>\n </div>\n\n {/* Permissions matrix */}\n <div className=\"col-span-12\">\n <CollectionPermissionsMatrix roleId={roleId} isAdmin={isAdmin} />\n </div>\n </div>\n </DialogContent>\n\n <DialogActions>\n <Button variant=\"text\" onClick={handleClose}>\n Cancel\n </Button>\n <LoadingButton\n variant=\"filled\"\n type=\"submit\"\n loading={isSubmitting}\n >\n {isNewRole ? \"Create role\" : \"Update\"}\n </LoadingButton>\n </DialogActions>\n </form>\n </Dialog>\n );\n}\n\n// ============================================\n// CollectionPermissionsMatrix Component\n// ============================================\nconst CRUD_OPS = [\n { op: \"select\" as const, label: \"Read\" },\n { op: \"insert\" as const, label: \"Create\" },\n { op: \"update\" as const, label: \"Edit\" },\n { op: \"delete\" as const, label: \"Delete\" },\n];\n\n/** Inline check: does roleId have access for this operation on these securityRules? */\nfunction hasRoleAccess(\n rules: SecurityRule[] | undefined,\n roleId: string,\n op: \"select\" | \"insert\" | \"update\" | \"delete\"\n): boolean {\n if (!rules || rules.length === 0) return true; // no rules = unrestricted\n const applicable = rules.filter(r =>\n r.operation === op || r.operation === \"all\" ||\n r.operations?.includes(op) || r.operations?.includes(\"all\")\n );\n if (applicable.length === 0) return false;\n const forRole = applicable.filter(r =>\n !r.roles || r.roles.length === 0 || r.roles.includes(roleId) || r.roles.includes(\"public\")\n );\n if (forRole.length === 0) return false;\n // Restrictive rules: any failing one denies immediately\n for (const r of forRole) {\n if ((r.mode ?? \"permissive\") === \"restrictive\") return false;\n }\n return forRole.some(r => (r.mode ?? \"permissive\") === \"permissive\");\n}\n\nfunction PermCell({ granted }: { granted: boolean }) {\n return (\n <span className={granted\n ? \"text-green-500 dark:text-green-400 text-base select-none\"\n : \"text-surface-300 dark:text-surface-600 text-base select-none\"}\n >\n {granted ? \"✓\" : \"✗\"}\n </span>\n );\n}\n\nfunction CollectionPermissionsMatrix({ roleId, isAdmin }: { roleId: string; isAdmin: boolean }) {\n const { collections } = useCollectionRegistryController();\n\n if (!collections || collections.length === 0) {\n return (\n <div className=\"mt-4\">\n <Typography variant=\"label\" className=\"text-surface-400\">No collections configured</Typography>\n </div>\n );\n }\n\n const topLevel = collections.filter(c => !c.collectionGroup);\n\n return (\n <div className=\"mt-6\">\n <Typography variant=\"label\" className=\"mb-2 block text-surface-600 dark:text-surface-400 uppercase tracking-wide text-xs\">\n Collection permissions\n </Typography>\n <div className=\"rounded-lg border border-surface-200 dark:border-surface-700 overflow-hidden\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableCell header>Collection</TableCell>\n {CRUD_OPS.map(({ op, label }) => (\n <TableCell key={op} header className=\"text-center w-24\">{label}</TableCell>\n ))}\n </TableRow>\n </TableHeader>\n <TableBody>\n {topLevel.map((collection) => {\n const noRules = !collection.securityRules || collection.securityRules.length === 0;\n return (\n <TableRow key={collection.slug}>\n <TableCell>\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium\">{collection.name}</span>\n {noRules && !isAdmin && (\n <Tooltip title=\"No security rules — unrestricted\">\n <Chip className=\"text-xs\" colorScheme=\"yellowLight\">No rules</Chip>\n </Tooltip>\n )}\n </div>\n <span className=\"text-xs text-surface-400 font-mono\">{collection.slug}</span>\n </TableCell>\n {CRUD_OPS.map(({ op }) => (\n <TableCell key={op} className=\"text-center\">\n <PermCell granted={isAdmin || hasRoleAccess(collection.securityRules, roleId, op)} />\n </TableCell>\n ))}\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n {!roleId && (\n <Typography variant=\"caption\" className=\"mt-2 text-surface-400 italic\">\n Enter a role ID above to preview permissions\n </Typography>\n )}\n </div>\n );\n}\n"],"names":["useState","useRef","useEffect","authApi.setApiUrl","useCallback","refreshAccessToken","authApi.refreshAccessToken","authApi.login","register","authApi.register","googleLogin","authApi.googleLogin","authApi.logout","forgotPassword","authApi.forgotPassword","resetPassword","authApi.resetPassword","changePassword","authApi.changePassword","updateProfile","authApi.updateProfile","fetchSessions","authApi.fetchSessions","revokeSession","authApi.revokeSession","authApi.fetchAuthConfig","authApi.getCurrentUser","getApiUrl","authApi.getApiUrl","revokeAllSessions","authApi.revokeAllSessions","useModeController","ErrorView","jsx","RebaseLogo","jsxs","Fragment","MailIcon","Button","IconButton","ArrowBackIcon","Typography","TextField","CircularProgress","getColorSchemeForSeed","Chip","useSnackbarController","useAuthController","CenteredView","Container","AddIcon","Table","TableHeader","TableCell","TableBody","TableRow","Tooltip","DeleteIcon","ConfirmationDialog","Dialog","open","DialogTitle","DialogContent","FieldCaption","MultiSelect","MultiSelectItem","DialogActions","LoadingButton","Checkbox","useCollectionRegistryController"],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ import { AuthController, Role, User } from "@rebasepro/core";
2
+ /**
3
+ * Auth controller that extends the base AuthController
4
+ * with additional methods for email/password and Google login
5
+ */
6
+ export type RebaseAuthController = AuthController & {
7
+ /** Login with Google ID token from frontend Google Sign-In */
8
+ googleLogin: (idToken: string) => Promise<void>;
9
+ /** Login with email and password */
10
+ emailPasswordLogin: (email: string, password: string) => Promise<void>;
11
+ /** Register a new user */
12
+ register: (email: string, password: string, displayName?: string) => Promise<void>;
13
+ /** Skip login (for anonymous access if enabled) */
14
+ skipLogin: () => void;
15
+ /** Whether login was skipped */
16
+ loginSkipped: boolean;
17
+ /** Error from auth provider (login failure details) */
18
+ authProviderError: Error | null;
19
+ /** True when there are no users in the system — first-user bootstrap mode */
20
+ needsSetup: boolean;
21
+ /** Whether new user registration is enabled (always true during setup) */
22
+ registrationEnabled: boolean;
23
+ /** Request password reset email */
24
+ forgotPassword: (email: string) => Promise<void>;
25
+ /** Reset password using token from email */
26
+ resetPassword: (token: string, password: string) => Promise<void>;
27
+ /** Change password for authenticated user */
28
+ changePassword: (oldPassword: string, newPassword: string) => Promise<void>;
29
+ /** Update user profile */
30
+ updateProfile: (displayName?: string, photoURL?: string) => Promise<User>;
31
+ /** Fetch active sessions */
32
+ fetchSessions: () => Promise<Session[]>;
33
+ /** Revoke a session */
34
+ revokeSession: (sessionId: string) => Promise<void>;
35
+ /** Revoke all active sessions */
36
+ revokeAllSessions: () => Promise<void>;
37
+ /** Get internal API URL */
38
+ getApiUrl?: () => string | undefined;
39
+ };
40
+ /**
41
+ * Props for useRebaseAuthController hook
42
+ */
43
+ export interface RebaseAuthControllerProps {
44
+ /** Base URL of the backend API */
45
+ apiUrl?: string;
46
+ /** Google OAuth client ID (optional, enables Google login) */
47
+ googleClientId?: string;
48
+ /** Callback when user signs out */
49
+ onSignOut?: () => void;
50
+ /** Define roles for a user after login */
51
+ defineRolesFor?: (user: User) => Promise<Role[] | undefined> | Role[] | undefined;
52
+ }
53
+ /**
54
+ * Auth tokens returned from the backend
55
+ */
56
+ export interface AuthTokens {
57
+ accessToken: string;
58
+ refreshToken: string;
59
+ /** Unix timestamp (ms) when the access token expires */
60
+ accessTokenExpiresAt: number;
61
+ }
62
+ /**
63
+ * User info from the backend
64
+ */
65
+ export interface UserInfo {
66
+ uid: string;
67
+ email: string;
68
+ displayName?: string | null;
69
+ photoURL?: string | null;
70
+ emailVerified?: boolean;
71
+ roles?: string[];
72
+ }
73
+ /**
74
+ * Auth response from backend login/register endpoints
75
+ */
76
+ export interface AuthResponse {
77
+ user: UserInfo;
78
+ tokens: AuthTokens;
79
+ }
80
+ /**
81
+ * Response from token refresh endpoint
82
+ */
83
+ export interface RefreshResponse {
84
+ tokens: AuthTokens;
85
+ }
86
+ /**
87
+ * Session info from the backend
88
+ */
89
+ export interface Session {
90
+ id: string;
91
+ userAgent?: string;
92
+ ipAddress?: string;
93
+ createdAt: string;
94
+ isCurrentSession?: boolean;
95
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@rebasepro/auth",
3
+ "type": "module",
4
+ "version": "0.0.1-canary.0",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "description": "Custom JWT authentication for Rebase with PostgreSQL backend",
9
+ "main": "./dist/index.umd.js",
10
+ "module": "./dist/index.es.js",
11
+ "types": "./dist/index.d.ts",
12
+ "source": "src/index.ts",
13
+ "dependencies": {
14
+ "@rebasepro/core": "0.0.1-canary.0",
15
+ "@rebasepro/types": "0.0.1-canary.0",
16
+ "@rebasepro/ui": "0.0.1-canary.0"
17
+ },
18
+ "peerDependencies": {
19
+ "react": ">=19.0.0",
20
+ "react-dom": ">=19.0.0"
21
+ },
22
+ "exports": {
23
+ ".": {
24
+ "development": "./src/index.ts",
25
+ "import": "./dist/index.es.js",
26
+ "require": "./dist/index.umd.js",
27
+ "types": "./dist/index.d.ts"
28
+ },
29
+ "./package.json": "./package.json"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^20.19.17",
33
+ "@types/react": "^19.0.8",
34
+ "@types/react-dom": "^19.0.3",
35
+ "typescript": "^5.9.3",
36
+ "vite": "^7.2.4"
37
+ },
38
+ "scripts": {
39
+ "dev": "vite",
40
+ "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
41
+ "clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f"
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "src"
46
+ ],
47
+ "gitHead": "646afae9c387c3ce02c699d98cd8c7272bdd1929"
48
+ }