fix: handle auth email callbacks explicitly

This commit is contained in:
2026-05-31 15:38:38 +08:00
parent 08aafeffcb
commit 0e2f24288f
3 changed files with 87 additions and 18 deletions
+6 -1
View File
@@ -7,4 +7,9 @@ if (!supabaseUrl || !supabaseKey) {
throw new Error('Missing Supabase environment variables') throw new Error('Missing Supabase environment variables')
} }
export const supabase = createClient(supabaseUrl, supabaseKey) export const supabase = createClient(supabaseUrl, supabaseKey, {
auth: {
// Email confirmation and password recovery are handled by route components.
detectSessionInUrl: false,
},
})
+19 -4
View File
@@ -22,33 +22,48 @@ function startCountdown() {
onMounted(async () => { onMounted(async () => {
try { try {
const search = new URLSearchParams(window.location.search)
const hash = window.location.hash.slice(1) const hash = window.location.hash.slice(1)
const params = new URLSearchParams(hash) const params = new URLSearchParams(hash)
const code = search.get('code')
const accessToken = params.get('access_token') const accessToken = params.get('access_token')
const refreshToken = params.get('refresh_token') const refreshToken = params.get('refresh_token')
const type = params.get('type') const type = params.get('type')
const error = params.get('error') const error = search.get('error') ?? params.get('error')
if (error) { if (error) {
state.value = 'failed' state.value = 'failed'
return return
} }
if (type === 'signup' && accessToken && refreshToken) { if (code || (type === 'signup' && accessToken && refreshToken)) {
const confirmClient = createClient( const confirmClient = createClient(
import.meta.env.VITE_SUPABASE_URL, import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY, import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY,
{
auth: {
detectSessionInUrl: false,
},
},
) )
if (code) {
const { error: exchangeError } = await confirmClient.auth.exchangeCodeForSession(code)
if (exchangeError) {
state.value = 'failed'
return
}
} else {
const { error: setSessionError } = await confirmClient.auth.setSession({ const { error: setSessionError } = await confirmClient.auth.setSession({
access_token: accessToken, access_token: accessToken!,
refresh_token: refreshToken, refresh_token: refreshToken!,
}) })
if (setSessionError) { if (setSessionError) {
state.value = 'failed' state.value = 'failed'
return return
} }
}
await confirmClient.auth.signOut() await confirmClient.auth.signOut()
+57 -8
View File
@@ -23,6 +23,20 @@ function showInvalidRecoveryLink() {
state.value = 'invalid' state.value = 'invalid'
} }
function getRecoveryParams() {
const search = new URLSearchParams(window.location.search)
const hash = new URLSearchParams(window.location.hash.slice(1))
return {
code: search.get('code'),
accessToken: hash.get('access_token'),
refreshToken: hash.get('refresh_token'),
type: hash.get('type'),
error: search.get('error') ?? hash.get('error'),
errorDescription: search.get('error_description') ?? hash.get('error_description'),
}
}
function getResetErrorMessage(value: unknown) { function getResetErrorMessage(value: unknown) {
const message = value instanceof Error ? value.message : '' const message = value instanceof Error ? value.message : ''
if ( if (
@@ -47,6 +61,47 @@ function startCountdown() {
}, 1000) }, 1000)
} }
async function initializeRecoverySession() {
const {
code,
accessToken,
refreshToken,
type,
error: recoveryError,
errorDescription,
} = getRecoveryParams()
if (recoveryError || errorDescription) {
showInvalidRecoveryLink()
return
}
try {
if (code) {
const { error } = await supabase.auth.exchangeCodeForSession(code)
if (error) throw error
} else if (type === 'recovery' && accessToken && refreshToken) {
const { error } = await supabase.auth.setSession({
access_token: accessToken,
refresh_token: refreshToken,
})
if (error) throw error
}
const { data: { session }, error } = await supabase.auth.getSession()
if (error || !session?.user) {
showInvalidRecoveryLink()
return
}
window.history.replaceState(null, '', window.location.pathname)
state.value = 'ready'
} catch (e) {
error.value = getResetErrorMessage(e)
state.value = 'invalid'
}
}
async function handleResetPassword() { async function handleResetPassword() {
error.value = '' error.value = ''
@@ -85,14 +140,8 @@ async function handleResetPassword() {
} }
} }
onMounted(async () => { onMounted(() => {
const { data: { session }, error: sessionError } = await supabase.auth.getSession() void initializeRecoverySession()
if (sessionError || !session?.user) {
showInvalidRecoveryLink()
return
}
state.value = 'ready'
}) })
onUnmounted(() => { onUnmounted(() => {