docs: Update architecture for supabase
test: Add tests for auth workflow
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
import { checkAuthRedirect } from '~/utils/auth'
|
||||
|
||||
export default defineNuxtRouteMiddleware((to) => {
|
||||
const user = useSupabaseUser()
|
||||
|
||||
const publicRoutes = ['/', '/login', '/signup', '/auth/callback']
|
||||
if (publicRoutes.includes(to.path)) return
|
||||
|
||||
if (!user.value) {
|
||||
return navigateTo('/')
|
||||
}
|
||||
const redirect = checkAuthRedirect(user.value, to.path)
|
||||
if (redirect) return navigateTo(redirect)
|
||||
})
|
||||
|
||||
@@ -1,13 +1,59 @@
|
||||
<template>
|
||||
<IonPage>
|
||||
<IonContent class="ion-padding ion-text-center">
|
||||
<p>Signing you in...</p>
|
||||
<div class="callback-state">
|
||||
<IonSpinner v-if="!errorMessage" name="crescent" class="callback-spinner" />
|
||||
<IonIcon v-else :icon="alertCircleOutline" class="callback-error-icon" color="danger" />
|
||||
<p>{{ errorMessage || 'Signing you in...' }}</p>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { IonPage, IonContent } from '@ionic/vue'
|
||||
import { IonPage, IonContent, IonSpinner, IonIcon } from '@ionic/vue'
|
||||
import { alertCircleOutline } from 'ionicons/icons'
|
||||
|
||||
definePageMeta({ layout: false })
|
||||
|
||||
const supabase = useSupabaseClient()
|
||||
const user = useSupabaseUser()
|
||||
const route = useRoute()
|
||||
const errorMessage = ref('')
|
||||
|
||||
onMounted(async () => {
|
||||
const code = route.query.code as string | undefined
|
||||
if (code) {
|
||||
const { error } = await supabase.auth.exchangeCodeForSession(code)
|
||||
if (error) {
|
||||
errorMessage.value = 'Sign-in failed. Please try again.'
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Navigate home once session is established (handles both hash-based and PKCE flows)
|
||||
watch(user, (value) => {
|
||||
if (value) navigateTo('/')
|
||||
}, { immediate: true })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.callback-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.callback-spinner {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.callback-error-icon {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
14
app/utils/auth.ts
Normal file
14
app/utils/auth.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export const PUBLIC_ROUTES = ['/', '/login', '/signup', '/auth/callback'] as const
|
||||
|
||||
/**
|
||||
* Pure auth decision logic — no Nuxt/Supabase dependencies.
|
||||
* Returns the path to redirect to, or null if no redirect is needed.
|
||||
*/
|
||||
export function checkAuthRedirect(
|
||||
userValue: { id: string } | null,
|
||||
path: string,
|
||||
): string | null {
|
||||
if ((PUBLIC_ROUTES as readonly string[]).includes(path)) return null
|
||||
if (!userValue) return '/'
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user