Skip to content

Commit 3145544

Browse files
authored
chore: toast auth initialization errors (supabase#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
1 parent 1b04f1a commit 3145544

File tree

6 files changed

+65
-21
lines changed

6 files changed

+65
-21
lines changed

apps/studio/components/layouts/AuthenticationLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { AppBannerContextProvider } from 'components/interfaces/App/AppBannerWra
66
export const AuthenticationLayout = ({ children }: PropsWithChildren<{}>) => {
77
return (
88
<AppBannerContextProvider>
9-
<div className="flex flex-col h-screen w-screen">
9+
<div className="flex flex-col min-h-screen w-screen">
1010
<AppBannerWrapper />
1111
<div className="flex flex-1 w-full overflow-y-hidden">
1212
<div className="flex-grow h-full overflow-y-auto">{children}</div>

apps/studio/components/layouts/SignInLayout/ForgotPasswordLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const ForgotPasswordLayout = ({
2121
const { resolvedTheme } = useTheme()
2222

2323
return (
24-
<div className="flex-1 bg-studio flex flex-col gap-8 lg:gap-16 xl:gap-32">
24+
<div className="min-h-screen flex-1 bg-studio flex flex-col gap-8 lg:gap-16 xl:gap-32">
2525
<div className="sticky top-0 mx-auto w-full max-w-7xl px-8 pt-6 sm:px-6 lg:px-8">
2626
<nav className="relative flex items-center justify-between sm:h-10">
2727
<div className="flex flex-shrink-0 flex-grow items-center lg:flex-grow-0">

apps/studio/components/layouts/SignInLayout/SignInLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ const SignInLayout = ({
8282

8383
return (
8484
<>
85-
<div className="relative flex flex-col bg-alternative h-screen">
85+
<div className="relative flex flex-col bg-alternative min-h-screen">
8686
<div
8787
className={`absolute top-0 w-full px-8 mx-auto sm:px-6 lg:px-8 ${
8888
ongoingIncident ? 'mt-14' : 'mt-6'

apps/studio/lib/auth.tsx

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
11
import { useQueryClient } from '@tanstack/react-query'
2-
import { AuthProvider as AuthProviderInternal, clearLocalStorage, gotrueClient } from 'common'
2+
import {
3+
AuthProvider as AuthProviderInternal,
4+
clearLocalStorage,
5+
gotrueClient,
6+
useAuthError,
7+
} from 'common'
38
import { PropsWithChildren, useCallback, useEffect } from 'react'
49
import { toast } from 'sonner'
510

611
import { GOTRUE_ERRORS, IS_PLATFORM } from './constants'
712

8-
export const AuthProvider = ({ children }: PropsWithChildren<{}>) => {
9-
// Check for unverified GitHub users after a GitHub sign in
10-
useEffect(() => {
11-
async function handleEmailVerificationError() {
12-
const { error } = await gotrueClient.initialize()
13+
const AuthErrorToaster = ({ children }: PropsWithChildren) => {
14+
const error = useAuthError()
1315

14-
if (error?.message === GOTRUE_ERRORS.UNVERIFIED_GITHUB_USER) {
16+
useEffect(() => {
17+
if (error !== null) {
18+
// Check for unverified GitHub users after a GitHub sign in
19+
if (error.message === GOTRUE_ERRORS.UNVERIFIED_GITHUB_USER) {
1520
toast.error(
1621
'Please verify your email on GitHub first, then reach out to us at [email protected] to log into the dashboard'
1722
)
23+
return
1824
}
25+
26+
toast.error(error.message)
1927
}
28+
}, [error])
2029

21-
handleEmailVerificationError()
22-
}, [])
30+
return children
31+
}
2332

24-
return <AuthProviderInternal alwaysLoggedIn={!IS_PLATFORM}>{children}</AuthProviderInternal>
33+
export const AuthProvider = ({ children }: PropsWithChildren) => {
34+
return (
35+
<AuthProviderInternal alwaysLoggedIn={!IS_PLATFORM}>
36+
<AuthErrorToaster>{children}</AuthErrorToaster>
37+
</AuthProviderInternal>
38+
)
2539
}
2640

2741
export { useAuth, useIsLoggedIn, useSession, useUser } from 'common'

apps/studio/pages/sign-in-mfa.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as Sentry from '@sentry/nextjs'
12
import { useQueryClient } from '@tanstack/react-query'
23
import { useRouter } from 'next/router'
34
import { useEffect, useState } from 'react'
@@ -65,8 +66,8 @@ const SignInMfaPage: NextPageWithLayout = () => {
6566
await queryClient.resetQueries()
6667
router.push(getReturnToPath())
6768
return
68-
}
69-
if (data.currentLevel !== data.nextLevel) {
69+
} else {
70+
// Show the MFA form
7071
setLoading(false)
7172
return
7273
}
@@ -77,7 +78,13 @@ const SignInMfaPage: NextPageWithLayout = () => {
7778
return
7879
}
7980
})
80-
.catch(() => {}) // catch all errors thrown by auth methods
81+
.catch((error) => {
82+
Sentry.captureException(error)
83+
console.error('Auth initialization error:', error)
84+
toast.error('Failed to initialize authentication. Please try again.')
85+
setLoading(false)
86+
router.push({ pathname: '/sign-in', query: router.query })
87+
})
8188
}, [])
8289

8390
if (loading) {

packages/common/auth.tsx

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import type { Session } from '@supabase/supabase-js'
3+
import type { AuthError, Session } from '@supabase/supabase-js'
44
import {
55
createContext,
66
PropsWithChildren,
@@ -10,8 +10,8 @@ import {
1010
useMemo,
1111
useState,
1212
} from 'react'
13-
import { gotrueClient, type User } from './gotrue'
1413
import { clearLocalStorage } from './constants/local-storage'
14+
import { gotrueClient, type User } from './gotrue'
1515

1616
export type { User }
1717

@@ -43,17 +43,20 @@ const DEFAULT_SESSION: any = {
4343
type AuthState =
4444
| {
4545
session: Session | null
46+
error: AuthError | null
4647
isLoading: false
4748
}
4849
| {
4950
session: null
51+
error: AuthError | null
5052
isLoading: true
5153
}
5254

5355
export type AuthContext = { refreshSession: () => Promise<Session | null> } & AuthState
5456

5557
export const AuthContext = createContext<AuthContext>({
5658
session: null,
59+
error: null,
5760
isLoading: true,
5861
refreshSession: () => Promise.resolve(null),
5962
})
@@ -66,14 +69,32 @@ export const AuthProvider = ({
6669
alwaysLoggedIn,
6770
children,
6871
}: PropsWithChildren<AuthProviderProps>) => {
69-
const [state, setState] = useState<AuthState>({ session: null, isLoading: true })
72+
const [state, setState] = useState<AuthState>({ session: null, error: null, isLoading: true })
73+
74+
useEffect(() => {
75+
let mounted = true
76+
gotrueClient.initialize().then(({ error }) => {
77+
if (mounted && error !== null) {
78+
setState((prev) => ({ ...prev, error }))
79+
}
80+
})
81+
82+
return () => {
83+
mounted = false
84+
}
85+
}, [])
7086

7187
// Keep the session in sync
7288
useEffect(() => {
7389
const {
7490
data: { subscription },
7591
} = gotrueClient.onAuthStateChange((_event, session) => {
76-
setState({ session, isLoading: false })
92+
setState((prev) => ({
93+
session,
94+
// If there is a session, we clear the error
95+
error: session !== null ? null : prev.error,
96+
isLoading: false,
97+
}))
7798
})
7899

79100
return subscription.unsubscribe
@@ -91,7 +112,7 @@ export const AuthProvider = ({
91112

92113
const value = useMemo(() => {
93114
if (alwaysLoggedIn) {
94-
return { session: DEFAULT_SESSION, isLoading: false, refreshSession } as const
115+
return { session: DEFAULT_SESSION, error: null, isLoading: false, refreshSession } as const
95116
} else {
96117
return { ...state, refreshSession } as const
97118
}
@@ -116,6 +137,8 @@ export const useIsLoggedIn = () => {
116137
return user !== null
117138
}
118139

140+
export const useAuthError = () => useAuth().error
141+
119142
export const useIsMFAEnabled = () => {
120143
const user = useUser()
121144

0 commit comments

Comments
 (0)