Authentication
nuxt-directus-sdk uses Directus's session-based authentication, which is ideal for Nuxt applications. Session mode provides better security (httpOnly cookies) and works seamlessly across domains with proper configuration.
Quick Start
Basic Login
const { login, logout, user, loggedIn } = useDirectusAuth()
// Login with email/password
await login('user@example.com', 'password')
// Check if logged in
if (loggedIn.value) {
console.log('User:', user.value)
}
// Logout
await logout()The module automatically:
- Sets the session cookie
- Fetches the user data
- Redirects after login/logout
- Handles token refresh
SSO / OAuth Login
One-line integration with SSO providers:
const { loginWithProvider } = useDirectusAuth()
// Redirect to Google OAuth
await loginWithProvider('google')
// Other providers
await loginWithProvider('github')
await loginWithProvider('microsoft')
await loginWithProvider('facebook')The flow:
- Redirects to Directus SSO endpoint
- User authenticates with provider
- Directus sets session cookie
- Redirects back to your app
- User is automatically logged in
Directus Configuration Required
For SSO to work with external redirects, you must add your frontend URL to the Directus redirect allow list:
# Directus .env
AUTH_<PROVIDER>_REDIRECT_ALLOW_LIST=https://yourapp.com,http://localhost:3000For example:
AUTH_GOOGLE_REDIRECT_ALLOW_LIST=https://yourapp.com,http://localhost:3000
AUTH_GITHUB_REDIRECT_ALLOW_LIST=https://yourapp.com,http://localhost:3000This is a security setting in Directus to prevent open redirect vulnerabilities.
User Management
Get Current User
const { user, readMe } = useDirectusAuth()
// User is auto-fetched on app load
console.log(user.value)
// Manually refresh user data
await readMe()Update Current User
const { updateMe } = useDirectusAuth()
await updateMe({
first_name: 'John',
last_name: 'Doe',
})User Registration
const { register } = useDirectusAuth()
const newUser = await register({
email: 'newuser@example.com',
password: 'secure-password',
first_name: 'John',
last_name: 'Doe',
})Password Reset
const { passwordRequest, passwordReset } = useDirectusAuth()
// Request password reset
await passwordRequest('user@example.com', 'https://yourapp.com/reset-password')
// Reset password with token
await passwordReset('reset-token', 'new-password')User Invites
const { inviteUser, acceptUserInvite } = useDirectusAuth()
// Invite a user
await inviteUser('newuser@example.com', 'role-id', 'https://yourapp.com/accept-invite')
// Accept invite
await acceptUserInvite('invite-token', 'password')Directus Configuration Required
For invite URLs to work with external domains, you must add them to the Directus redirect allow list:
# Directus .env
USER_INVITE_URL_ALLOW_LIST=https://yourapp.com/accept-invite,http://localhost:3000/accept-inviteThis is a security setting in Directus to prevent open redirect vulnerabilities when users accept invitations.
Protected Routes
Page-Level Protection
Protect individual pages with the auth middleware:
<script setup>
definePageMeta({
middleware: 'auth'
})
</script>
<template>
<div>
<p>This page requires authentication</p>
</div>
</template>Global Protection
Protect all routes by default:
// nuxt.config.ts
export default defineNuxtConfig({
directus: {
auth: {
enableGlobalAuthMiddleware: true,
},
},
})Then allow public pages using the guest middleware:
<script setup>
definePageMeta({
middleware: 'guest'
})
</script>
<template>
<div>
<p>This page is public</p>
</div>
</template>Custom Redirects
Configure where users are redirected:
export default defineNuxtConfig({
directus: {
auth: {
redirect: {
login: '/account/login', // Where to go when not logged in
home: '/dashboard', // Where to go after login
logout: '/', // Where to go after logout
},
},
},
})Server-Side Authentication
In Server Routes
// server/api/profile.ts
export default defineEventHandler(async (event) => {
const directus = useServerDirectus(event)
// This request is automatically authenticated with the user's session
const user = await directus.request(readMe())
return { user }
})Admin Operations
// server/api/admin/users.ts
export default defineEventHandler(async () => {
const directus = useAdminDirectus()
// Uses admin token for privileged operations
const users = await directus.request(readUsers())
return { users }
})Configuration
Frontend Configuration
// nuxt.config.ts
export default defineNuxtConfig({
directus: {
auth: {
enabled: true, // default
autoRefresh: true, // auto-refresh tokens
credentials: 'include', // required for cross-domain
realtimeAuthMode: 'public', // 'public', 'handshake', or 'strict'
enableGlobalAuthMiddleware: false, // protect all routes
readMeFields: ['*'], // fields to fetch for current user
redirect: {
home: '/',
login: '/account/login',
logout: '/',
},
},
},
})Backend Configuration
Same Domain Setup
If your Nuxt app and Directus are on the same domain (e.g., localhost in dev):
# Directus .env
AUTH_LOCAL_MODE=session
SESSION_COOKIE_SECURE=false # true in production
SESSION_COOKIE_SAME_SITE=Lax
CORS_ENABLED=true
CORS_ORIGIN=http://localhost:3000
CORS_CREDENTIALS=trueCross-Domain Setup
For production with separate domains (e.g., app.example.com and api.example.com):
# Directus .env
AUTH_LOCAL_MODE=session
SESSION_COOKIE_DOMAIN=.example.com # Shared parent domain
SESSION_COOKIE_SECURE=true
SESSION_COOKIE_SAME_SITE=None
CORS_ENABLED=true
CORS_ORIGIN=https://app.example.com
CORS_CREDENTIALS=trueComposable API Reference
useDirectusAuth()
Returns an object with auth methods and state:
const {
user, // Ref<DirectusUser | null>
loggedIn, // ComputedRef<boolean>
readMe, // () => Promise<DirectusUser>
updateMe, // (data) => Promise<DirectusUser>
login, // (email, password, options?) => Promise<DirectusUser>
loginWithProvider, // (provider, redirect?) => Promise<void>
logout, // (redirect?) => Promise<void>
register, // (data) => Promise<DirectusUser>
createUser, // (data) => Promise<DirectusUser>
inviteUser, // (email, role, inviteUrl?) => Promise<void>
acceptUserInvite, // (token, password) => Promise<void>
passwordRequest, // (email, resetUrl?) => Promise<void>
passwordReset, // (token, password) => Promise<void>
} = useDirectusAuth()useDirectusUser()
Direct access to the user state:
const user = useDirectusUser()
// Ref<DirectusUser | null>Advanced Topics
Custom Login Logic
const { login } = useDirectusAuth()
// Login without redirect
await login('user@example.com', 'password', {
redirect: false
})
// Login with custom redirect
await login('user@example.com', 'password', {
redirect: '/custom-page'
})
// Login and handle manually
const user = await login('user@example.com', 'password', {
redirect: false
})
if (user.role === 'admin') {
await navigateTo('/admin')
} else {
await navigateTo('/dashboard')
}Listen to Login Events
// plugins/auth-listener.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('directus:loggedIn', (user) => {
console.log('User logged in:', user)
// Track login event, update analytics, etc.
})
})Configurable User Fields
Control which fields are fetched for the current user:
export default defineNuxtConfig({
directus: {
auth: {
readMeFields: ['id', 'email', 'first_name', 'last_name', 'avatar'],
},
},
})Troubleshooting
Cookies Not Being Set
- ✅ Check
CORS_CREDENTIALS=truein Directus - ✅ Verify
SESSION_COOKIE_DOMAINis set correctly - ✅ Ensure both sites use HTTPS in production (required for
SameSite=None) - ✅ Check
credentials: 'include'is set in module config
Session Not Persisting
- ✅ Make sure cookies aren't being blocked by browser
- ✅ Check browser dev tools → Application → Cookies
- ✅ Verify
directus_session_tokencookie exists - ✅ Ensure cookie domain matches your setup
SSR Issues
- ✅ Use
useServerDirectus(event)in server routes (notuseDirectus()) - ✅ Check cookies are being forwarded on SSR (automatic with this module)
- ✅ Verify server-side requests include session cookie