Authentication Guide
Learn how to implement secure authentication in your NuxtFast application using Supabase Auth

Lorenzo
Author
NuxtFast comes with built-in authentication powered by Supabase Auth, providing a secure and scalable solution for user management. This guide will walk you through setting up and customizing authentication in your application.
Overview
Supabase Auth provides:
- Multiple authentication methods (email/password, magic links, OAuth)
- Built-in security features (rate limiting, email verification, password policies)
- User management (profiles, roles, metadata)
- Session management (automatic token refresh, secure storage)
Quick Setup
1. Environment Configuration
First, ensure your Supabase credentials are configured in your .env file:
SUPABASE_URL=your_supabase_url
SUPABASE_ANON_KEY=your_supabase_anon_key
Enable Google OAuth
Social authentication with providers like Google, GitHub, etc.
The most important one is definitely the Google provider. Follow the official documentation to setup the Google OAuth providers.
If your product is focused to developers, you can also setup the GitHub provider.
2. Basic Authentication Flow
The most common authentication pattern in NuxtFast:
<template>
<div>
<!-- Login Form -->
<form v-if="!user" @submit.prevent="signIn">
<div class="form-control">
<label class="label">Email</label>
<input v-model="email" type="email" class="input input-bordered" required />
</div>
<div class="form-control">
<label class="label">Password</label>
<input v-model="password" type="password" class="input input-bordered" required />
</div>
<button type="submit" class="btn btn-primary">Sign In</button>
</form>
<!-- Authenticated Content -->
<div v-else>
<p>Welcome, {{ user.email }}!</p>
<button @click="signOut" class="btn btn-ghost">Sign Out</button>
</div>
</div>
</template>
<script setup>
const supabase = useSupabaseClient()
const user = useSupabaseUser()
const email = ref('')
const password = ref('')
const signIn = async () => {
const { error } = await supabase.auth.signInWithPassword({
email: email.value,
password: password.value,
})
if (error) {
console.error('Error signing in:', error)
}
}
const signOut = async () => {
const { error } = await supabase.auth.signOut()
if (error) {
console.error('Error signing out:', error)
}
}
</script>
Authentication Methods
Email & Password
The traditional email and password authentication:
// Sign up
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'secure-password',
})
// Sign in
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'secure-password',
})
Magic Links
Passwordless authentication via email:
const { data, error } = await supabase.auth.signInWithOtp({
email: 'user@example.com',
options: {
emailRedirectTo: 'https://your-app.com/auth/callback',
},
})
OAuth Providers
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: 'https://your-app.com/auth/callback',
},
})
User Management
Accessing User Data
Always use the useSupabaseUser() composable to access user data:
<script setup>
const user = useSupabaseUser()
// Reactive user data
watchEffect(() => {
if (user.value) {
console.log('User ID:', user.value.id)
console.log('Email:', user.value.email)
console.log('Metadata:', user.value.user_metadata)
}
})
</script>
User Profiles
Create and manage user profiles with additional data:
// Create profile after sign up
const { data, error } = await supabase.from('profiles').insert({
user_id: user.value.id,
username: 'johndoe',
full_name: 'John Doe',
avatar_url: 'https://example.com/avatar.jpg',
})
// Update profile
const { data, error } = await supabase
.from('profiles')
.update({ full_name: 'John Smith' })
.eq('user_id', user.value.id)
Route Protection
Middleware Protection
Protect pages using middleware:
// middleware/auth.js
export default defineNuxtRouteMiddleware((to, from) => {
const user = useSupabaseUser()
if (!user.value) {
return navigateTo('/login')
}
})
Use in pages:
<script setup>
definePageMeta({
middleware: 'auth',
})
</script>
Component Protection
Protect specific components:
<template>
<div>
<div v-if="user">
<!-- Protected content -->
</div>
<div v-else>
<p>Please sign in to access this content.</p>
<NuxtLink to="/login" class="btn btn-primary">Sign In</NuxtLink>
</div>
</div>
</template>
<script setup>
const user = useSupabaseUser()
</script>
Row Level Security (RLS)
Supabase's Row Level Security ensures data access is properly controlled:
-- Enable RLS on tables
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
-- Policy: Users can only see their own profile
CREATE POLICY "Users can view own profile" ON profiles
FOR SELECT USING (auth.uid() = user_id);
-- Policy: Users can update their own profile
CREATE POLICY "Users can update own profile" ON profiles
FOR UPDATE USING (auth.uid() = user_id);
Advanced Features
Custom Claims
Add custom claims to user tokens:
const { data, error } = await supabase.auth.updateUser({
data: {
role: 'admin',
subscription_tier: 'premium',
},
})
Session Management
Handle session events:
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'SIGNED_IN') {
console.log('User signed in:', session.user)
} else if (event === 'SIGNED_OUT') {
console.log('User signed out')
}
})
Password Reset
Implement password reset functionality:
// Send reset email
const { data, error } = await supabase.auth.resetPasswordForEmail('user@example.com', {
redirectTo: 'https://your-app.com/reset-password',
})
// Update password
const { data, error } = await supabase.auth.updateUser({
password: 'new-secure-password',
})
Best Practices
1. Always Validate on Server
Never trust client-side validation alone. Implement server-side validation:
// server/api/protected-action.js
export default defineEventHandler(async event => {
const user = await serverSupabaseUser(event)
if (!user) {
throw createError({
statusCode: 401,
statusMessage: 'Unauthorized',
})
}
// Proceed with protected action
})
2. Handle Loading States
Provide good UX during authentication:
<template>
<div>
<div v-if="pending" class="loading loading-spinner"></div>
<div v-else-if="user">
<!-- Authenticated content -->
</div>
<div v-else>
<!-- Sign in form -->
</div>
</div>
</template>
<script setup>
const user = useSupabaseUser()
const pending = computed(() => user.value === undefined)
</script>
3. Secure Sensitive Operations
For sensitive operations, require re-authentication:
const deleteAccount = async () => {
// Require recent authentication
const { data, error } = await supabase.auth.reauthenticate()
if (!error) {
// Proceed with account deletion
await supabase.rpc('delete_user_account')
}
}
Troubleshooting
Common Issues
User is null after page refresh
- Solution: The user data loads asynchronously. Use loading states.
Authentication redirects not working
- Solution: Check your Site URL in Supabase dashboard settings.
CORS errors during development
- Solution: Add your development URL to allowed origins in Supabase.