chore: toast auth initialization errors (#37037)

* chore: toast auth initialization errors

* Redirect back to sign in if authentication fails

* add debug logs

* remove debug logs

* debug gotrue

* remove gotrue debug
This commit is contained in:
Alaister Young
2025-07-15 17:57:32 +08:00
committed by GitHub
parent 1b04f1a43b
commit 31455448b0
6 changed files with 65 additions and 21 deletions

View File

@@ -6,7 +6,7 @@ import { AppBannerContextProvider } from 'components/interfaces/App/AppBannerWra
export const AuthenticationLayout = ({ children }: PropsWithChildren<{}>) => {
return (
<AppBannerContextProvider>
<div className="flex flex-col h-screen w-screen">
<div className="flex flex-col min-h-screen w-screen">
<AppBannerWrapper />
<div className="flex flex-1 w-full overflow-y-hidden">
<div className="flex-grow h-full overflow-y-auto">{children}</div>

View File

@@ -21,7 +21,7 @@ const ForgotPasswordLayout = ({
const { resolvedTheme } = useTheme()
return (
<div className="flex-1 bg-studio flex flex-col gap-8 lg:gap-16 xl:gap-32">
<div className="min-h-screen flex-1 bg-studio flex flex-col gap-8 lg:gap-16 xl:gap-32">
<div className="sticky top-0 mx-auto w-full max-w-7xl px-8 pt-6 sm:px-6 lg:px-8">
<nav className="relative flex items-center justify-between sm:h-10">
<div className="flex flex-shrink-0 flex-grow items-center lg:flex-grow-0">

View File

@@ -82,7 +82,7 @@ const SignInLayout = ({
return (
<>
<div className="relative flex flex-col bg-alternative h-screen">
<div className="relative flex flex-col bg-alternative min-h-screen">
<div
className={`absolute top-0 w-full px-8 mx-auto sm:px-6 lg:px-8 ${
ongoingIncident ? 'mt-14' : 'mt-6'

View File

@@ -1,27 +1,41 @@
import { useQueryClient } from '@tanstack/react-query'
import { AuthProvider as AuthProviderInternal, clearLocalStorage, gotrueClient } from 'common'
import {
AuthProvider as AuthProviderInternal,
clearLocalStorage,
gotrueClient,
useAuthError,
} from 'common'
import { PropsWithChildren, useCallback, useEffect } from 'react'
import { toast } from 'sonner'
import { GOTRUE_ERRORS, IS_PLATFORM } from './constants'
export const AuthProvider = ({ children }: PropsWithChildren<{}>) => {
// Check for unverified GitHub users after a GitHub sign in
useEffect(() => {
async function handleEmailVerificationError() {
const { error } = await gotrueClient.initialize()
const AuthErrorToaster = ({ children }: PropsWithChildren) => {
const error = useAuthError()
if (error?.message === GOTRUE_ERRORS.UNVERIFIED_GITHUB_USER) {
useEffect(() => {
if (error !== null) {
// Check for unverified GitHub users after a GitHub sign in
if (error.message === GOTRUE_ERRORS.UNVERIFIED_GITHUB_USER) {
toast.error(
'Please verify your email on GitHub first, then reach out to us at support@supabase.io to log into the dashboard'
)
return
}
toast.error(error.message)
}
}, [error])
handleEmailVerificationError()
}, [])
return children
}
return <AuthProviderInternal alwaysLoggedIn={!IS_PLATFORM}>{children}</AuthProviderInternal>
export const AuthProvider = ({ children }: PropsWithChildren) => {
return (
<AuthProviderInternal alwaysLoggedIn={!IS_PLATFORM}>
<AuthErrorToaster>{children}</AuthErrorToaster>
</AuthProviderInternal>
)
}
export { useAuth, useIsLoggedIn, useSession, useUser } from 'common'

View File

@@ -1,3 +1,4 @@
import * as Sentry from '@sentry/nextjs'
import { useQueryClient } from '@tanstack/react-query'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
@@ -65,8 +66,8 @@ const SignInMfaPage: NextPageWithLayout = () => {
await queryClient.resetQueries()
router.push(getReturnToPath())
return
}
if (data.currentLevel !== data.nextLevel) {
} else {
// Show the MFA form
setLoading(false)
return
}
@@ -77,7 +78,13 @@ const SignInMfaPage: NextPageWithLayout = () => {
return
}
})
.catch(() => {}) // catch all errors thrown by auth methods
.catch((error) => {
Sentry.captureException(error)
console.error('Auth initialization error:', error)
toast.error('Failed to initialize authentication. Please try again.')
setLoading(false)
router.push({ pathname: '/sign-in', query: router.query })
})
}, [])
if (loading) {

View File

@@ -1,6 +1,6 @@
'use client'
import type { Session } from '@supabase/supabase-js'
import type { AuthError, Session } from '@supabase/supabase-js'
import {
createContext,
PropsWithChildren,
@@ -10,8 +10,8 @@ import {
useMemo,
useState,
} from 'react'
import { gotrueClient, type User } from './gotrue'
import { clearLocalStorage } from './constants/local-storage'
import { gotrueClient, type User } from './gotrue'
export type { User }
@@ -43,10 +43,12 @@ const DEFAULT_SESSION: any = {
type AuthState =
| {
session: Session | null
error: AuthError | null
isLoading: false
}
| {
session: null
error: AuthError | null
isLoading: true
}
@@ -54,6 +56,7 @@ export type AuthContext = { refreshSession: () => Promise<Session | null> } & Au
export const AuthContext = createContext<AuthContext>({
session: null,
error: null,
isLoading: true,
refreshSession: () => Promise.resolve(null),
})
@@ -66,14 +69,32 @@ export const AuthProvider = ({
alwaysLoggedIn,
children,
}: PropsWithChildren<AuthProviderProps>) => {
const [state, setState] = useState<AuthState>({ session: null, isLoading: true })
const [state, setState] = useState<AuthState>({ session: null, error: null, isLoading: true })
useEffect(() => {
let mounted = true
gotrueClient.initialize().then(({ error }) => {
if (mounted && error !== null) {
setState((prev) => ({ ...prev, error }))
}
})
return () => {
mounted = false
}
}, [])
// Keep the session in sync
useEffect(() => {
const {
data: { subscription },
} = gotrueClient.onAuthStateChange((_event, session) => {
setState({ session, isLoading: false })
setState((prev) => ({
session,
// If there is a session, we clear the error
error: session !== null ? null : prev.error,
isLoading: false,
}))
})
return subscription.unsubscribe
@@ -91,7 +112,7 @@ export const AuthProvider = ({
const value = useMemo(() => {
if (alwaysLoggedIn) {
return { session: DEFAULT_SESSION, isLoading: false, refreshSession } as const
return { session: DEFAULT_SESSION, error: null, isLoading: false, refreshSession } as const
} else {
return { ...state, refreshSession } as const
}
@@ -116,6 +137,8 @@ export const useIsLoggedIn = () => {
return user !== null
}
export const useAuthError = () => useAuth().error
export const useIsMFAEnabled = () => {
const user = useUser()