useAccAuth
Composable for all authentication actions and session state management.
Overview
useAccAuth() provides all authentication actions for your application. Each action communicates directly with the Accelerator auth endpoints at /acc/auth. After a successful action, the composable automatically syncs the global auth state so the rest of your app stays up to date.
Reactive access to the current auth state is available through three companion composables: useAccAuthSessions(), useAccAuthActiveSessionId(), and useAccAuthUser(). These return Nuxt useState refs that are kept in sync automatically.
Setup
Auth is disabled by default. Enable it in your nuxt.config.ts with enabled: true, a cookieSecret, and optionally a loginPage path for automatic redirects on 401/403 — see Auth error handling for how that works.
// nuxt.config.ts
export default defineNuxtConfig({
...,
levarneAcc: {
auth: {
enabled: true,
cookieSecret: process.env.COOKIE_SECRET, // required
loginPage: '/login', // optional — redirect on 401/403
},
}
})Usage
const {
signUp,
signInWithPassword,
verify,
confirmInvite,
signOut,
signOutAll,
switchSession,
refreshActiveSession,
syncAuthState,
getCurrentUser,
requestPasswordReset,
confirmPasswordReset,
changePassword,
setup2FA,
verify2FA,
signInWith2FA,
signInWithMicrosoft,
linkMicrosoft,
} = useAccAuth()State composables
Import these separately to read the current auth state reactively anywhere in your app. They are plain useState refs — read them in templates or watch them in composables.
const sessions = useAccAuthSessions() // Ref<AccSession[]>
const activeSessionId = useAccAuthActiveSessionId() // Ref<string | null>
const authUser = useAccAuthUser() // Ref<Record<string, unknown> | null>signUp
Registers a new user. The backend always sends an email — either a verification email for new/unconfirmed users, or a password reset email for existing confirmed users (to avoid revealing account existence). Supports optional reCAPTCHA — pass captchaAction and the $accApi plugin resolves the token automatically. See Email flows for details.
const result = await signUp({
username: 'user@example.com',
password: 'secret',
captchaAction: 'sign_up', // triggers automatic reCAPTCHA
baseUrl: 'https://your-origin.com', // optional — used to build links in emails
frontendPath: 'auth/confirm', // optional — path segment in verification/reset email links
})
// result: { success: boolean, errorCode?: AuthErrorCode }signInWithPassword
Authenticates a user with username and password. On success, syncs the auth state automatically. If the user is UNCONFIRMED, the backend sends a verification email and returns USER_NOT_CONFIRMED — see Email flows. If the user has 2FA enabled, the response includes errorCode: 'MFA_REQUIRED' and a challengeToken — use signInWith2FA to complete the login.
const result = await signInWithPassword({
username: 'user@example.com',
password: 'secret',
sessionDuration: 'long', // 'short' | 'long' — defaults to 'short'
baseUrl: 'https://your-origin.com', // optional
frontendPath: 'auth/confirm', // optional — used if user is UNCONFIRMED and a verification email is sent
})
// result: { success: boolean, sessionId?: string, errorCode?: AuthErrorCode }The session TTLs and access token lifetime can be overridden in your nuxt.config.ts. Access tokens are refreshed automatically by the $accApi proxy on every request when expired:
// nuxt.config.ts — override session and token TTLs
levarneAcc: {
auth: {
sessionTtlShortSeconds: 60 * 60, // default: 1 hour
sessionTtlLongSeconds: 60 * 60 * 24 * 30, // default: 30 days
accessTokenTtlSeconds: 60 * 15, // default: 15 minutes
},
}verify
Confirms a user's email using the token from a verification email. On success, creates a new session and syncs the auth state automatically. If the token is expired, the backend re-sends a fresh verification email and returns TOKEN_EXPIRED — see Email flows.
const result = await verify({
token: 'verification-token-from-email',
sessionDuration: 'long', // 'short' | 'long' — defaults to 'short'
baseUrl: 'https://your-origin.com', // optional — used if token is expired and a new email is sent
frontendPath: 'auth/confirm', // optional — path segment in the re-sent verification email link
})
// result: { success: boolean, sessionId?: string, errorCode?: AuthErrorCode }confirmInvite
Accepts an invitation using the token from the email link and sets the account's password. On success, creates and activates a session and syncs the auth state automatically. Authentication tokens stay in server-side session storage; the result contains only the session ID. This uses the invite-confirmation endpoint, so use confirmInvite for invite tokens.
const { confirmInvite } = useAccAuth()
const result = await confirmInvite({
token: 'invite-token-from-email',
newPassword: 'your-new-password',
sessionDuration: 'short', // 'short' | 'long' — defaults to 'short'
frontendPath: 'auth/accept-invite', // optional — used in replacement emails
})
if (result.success) {
// The session is active and useAccAuthUser() is already updated.
// result.sessionId identifies the session; authentication tokens stay on the server.
} An expired token triggers a replacement invitation email and returns TOKEN_EXPIRED. Other invite errors include TOKEN_ALREADY_USED, NOT_FOUND, and ALREADY_VERIFIED. Failed confirmations do not create a session. See Email flows for replacement link settings.
signOut / signOutAll
signOut() signs out the active session, or a specific session if sessionId is provided. signOutAll() terminates every session for the current user. Both sync the auth state on success.
// Sign out the active session
await signOut()
// Sign out a specific session by ID
await signOut({ sessionId: 'session_abc123' })
// Sign out all sessions for the current user
await signOutAll()switchSession
Switches the active session to a different one. Useful when a user has multiple sessions (e.g. multiple accounts). Syncs auth state on success.
// Switch to a different session (e.g. a second logged-in account)
const result = await switchSession('session_abc123')
// result: { success: boolean, activeSessionId?: string, errorCode?: AuthErrorCode }refreshActiveSession
Refreshes the active session's tokens. Call this proactively before a token expires to keep the session alive. Syncs auth state on success.
const result = await refreshActiveSession()
// result: { success: boolean, errorCode?: AuthErrorCode }syncAuthState
Fetches the current auth state from the server and updates the global state refs. Useful on app startup or after an external auth event.
// Fetch and apply the current auth state from the server
const authState = await syncAuthState()
// authState: { activeSessionId: string | null, sessions: AccSession[], activeUser: ... | null }getCurrentUser
Fetches the current user object from the server and updates useAccAuthUser(). Use this when you need a fresh copy of the user without doing a full state sync.
// Fetch just the user object and update useAccAuthUser()
const user = await getCurrentUser()Password reset
Two-step password reset flow. First call requestPasswordReset() to send the reset email. Then call confirmPasswordReset() with the token from that email. If the token is expired, confirmPasswordReset re-sends a fresh email automatically and returns TOKEN_EXPIRED. See Email flows for details.
// Step 1 — request a reset email
await requestPasswordReset({
username: 'user@example.com',
captchaAction: 'forgot_password', // triggers automatic reCAPTCHA
baseUrl: 'https://your-origin.com', // optional — used to build the reset link in the email
frontendPath: 'auth/reset-password', // optional — path segment in the reset email link
})
// Step 2 — confirm with the token from the email
await confirmPasswordReset({
token: 'reset-token-from-email',
newPassword: 'new-secret',
baseUrl: 'https://your-origin.com', // optional — used if token is expired and a new email is sent
frontendPath: 'auth/reset-password', // optional — used if token is expired and a new email is sent
})changePassword
Changes the password for the currently authenticated user. Requires the current password.
const result = await changePassword({
currentPassword: 'old-secret',
newPassword: 'new-secret',
})
// result: { success: boolean, errorCode?: AuthErrorCode }2FA
Two-factor authentication using TOTP (time-based one-time passwords). The flow is: setup2FA to generate a secret, verify2FA to confirm it, and signInWith2FA to complete login when signInWithPassword returns MFA_REQUIRED.
setup2FA
Generates a TOTP secret and otpauth:// URI for the authenticated user. Display the URI as a QR code so the user can scan it with their authenticator app, or show the secret for manual entry. Requires an active session.
const { secret, uri } = await setup2FA()
// secret: 'TO3Y45KKXTZR4RYQ' — 16-char base32 key for manual entry
// uri: 'otpauth://totp/user?secret=…&algorithm=SHA1&digits=6&period=30' — render as QR codeverify2FA
Confirms 2FA setup by submitting the 6-digit TOTP code from the user's authenticator app. Must be called after setup2FA. On success, syncs the auth state automatically.
const result = await verify2FA({
totp: '123456', // 6-digit code from the user's authenticator app
})
// result: { success: boolean, sessionId?: string, errorCode?: AuthErrorCode }signInWith2FA
Completes a 2FA login. When signInWithPassword returns MFA_REQUIRED with a challengeToken, call this function with the challenge token and the 6-digit TOTP code to authenticate. On success, syncs the auth state automatically.
// Step 1: normal sign in — returns MFA_REQUIRED when 2FA is enabled
const { success, errorCode, challengeToken } = await signInWithPassword({
username: 'user@example.com',
password: 'secret',
})
if (errorCode === 'MFA_REQUIRED' && challengeToken) {
// Step 2: complete login with TOTP code
const result = await signInWith2FA({
challengeToken,
totp: '123456', // 6-digit code from authenticator app
})
}SSO
Single sign-on with external providers (currently Microsoft). The flow is backend-owned: call signInWithMicrosoft or linkMicrosoft; each helper redirects the browser to the provider, and the backend callback route (/acc/auth/sso/callback) automatically exchanges the code, creates or links the session, and redirects the user back. Errors during the callback are propagated via the ?sso-error= query param.
signInWithMicrosoft
Sign in with a Microsoft account via SSO. The composable requests the provider authorize URL and redirects the browser automatically. The provider handles login, then the backend redirects to the fixed server route /acc/auth/sso/callback which automatically exchanges the code, creates a session, and redirects the user back to where they were (captured via returnTo from the current route).
// Requests the Microsoft authorize URL and redirects automatically
const result = await signInWithMicrosoft()
if (!result.success) {
console.error(result.errorCode)
}
// After Microsoft logs the user in, the backend redirects to
// /acc/auth/sso/callback, exchanges the code, creates a session,
// and redirects back to the page where the user started. If the backend rejects the request (e.g. provider misconfigured), the composable returns { success: false, errorCode } — see SSO error handling.
linkMicrosoft
Link a Microsoft identity to the currently authenticated user. Requires an active session. The callback route handles the exchange and redirects back automatically.
// Links Microsoft to the currently authenticated user and redirects automatically
const result = await linkMicrosoft()
if (!result.success) {
console.error(result.errorCode)
}
// The callback route exchanges the code, links the external identity,
// and redirects back to the page where the user initiated the link. On success, the composable redirects automatically. If the provider is already linked to this user, the backend returns EXTERNAL_PROVIDER_ALREADY_LINKED — see SSO error handling.
SSO error handling
SSO errors can occur at two points:
- Authorize request — the
signInWithMicrosoft/linkMicrosoftcall itself fails (e.g. provider misconfigured, user not authenticated for a link). The error is returned synchronously inresult.errorCode— handle it immediately before redirecting. - Callback return — the external provider redirect succeeds but the backend exchange fails (e.g. the external identity is already linked to another user). The callback route redirects back to the page where the user started, appending
?sso-error=ERROR_CODEto the URL.
For both sign-in and link, errors redirect back to the valid returnTo path where the flow started, preserving any existing query parameters and hash. If no valid returnTo is available, the configured loginPage is used with ?sso-error=ERROR_CODE.
// Handle the sso-error query param on the page where users land after SSO
const route = useRoute()
const ssoError = computed(() => route.query['sso-error'] as string | undefined)
// Example: show a banner on the login page
watch(ssoError, (code) => {
if (!code) return
switch (code) {
case 'EXTERNAL_PROVIDER_ALREADY_LINKED':
errorMessage.value = 'This provider is already linked to your account'
break
case 'PASSWORD_ACCOUNT_EXISTS':
errorMessage.value = 'An account with this email already exists. Please sign in with your password.'
break
case 'UNAUTHORIZED':
errorMessage.value = 'You must be logged in to link an account'
break
default:
errorMessage.value = 'Sign-in failed, please try again'
}
}, { immediate: true })Email flows
Several auth functions can trigger the backend to send an email. The frontendPath and baseUrl parameters control the link in that email. The final link format is {baseUrl}/{frontendPath}?token={tokenValue}.
frontendPath must be a relative path with no leading slash (e.g. auth/confirm). If omitted, the backend falls back to a hardcoded default (verify, resetpassword, etc. depending on the email type).
signUp
Always sends an email, but the type depends on user state:
- New user → sends a verification email.
- Existing
UNCONFIRMEDuser with expired token → re-sends the verification email.
signInWithPassword
Sends a verification email only when the user is UNCONFIRMED. Issues a new verification token, sends the email, then returns USER_NOT_CONFIRMED.
verify
Sends a fresh verification email only when the submitted token is expired. Re-issues a new token, sends the email, then returns TOKEN_EXPIRED.
confirmInvite
Sends a fresh invitation email when the submitted invite token is expired, then returns TOKEN_EXPIRED. Pass frontendPath to point the replacement link at your invite-acceptance page. It must be a relative path with no leading slash; the backend defaults to resetpassword. baseUrl defaults to the current request origin.
requestPasswordReset
Sends a password reset email when the user exists, is CONFIRMED, and has no active reset token.
confirmPasswordReset
Sends a fresh password reset email only when the submitted token is expired or already used. Re-issues a new token, sends the email, then returns TOKEN_EXPIRED.
Return type
Most actions return a GenericAuthResult. Sign-in and sign-up have their own shapes.
Session storage
Sessions are stored server-side using a configurable storage driver. The driver is set via auth.storageDriver in your module config. The default is memory.
memory — the default. Stores sessions in the Nitro server process memory. No setup required, but sessions are lost on every server restart and not shared across multiple server instances. Only suitable for development or single-instance deployments where persistence is not needed.
// nuxt.config.ts — default, no extra config needed
levarneAcc: {
auth: {
enabled: true,
storageDriver: { type: 'memory' },
},
}fs-lite — not a built-in driver option, but Nitro's built-in devStorage supports it. In development you can mount a file-system storage under the acc-sessions key in your nuxt.config.ts. The module checks whether acc-sessions is already mounted before registering its own driver, so this takes precedence automatically. Sessions are written to .data/.acc-sessions and survive server restarts. This is the recommended setup for local development.
// nuxt.config.ts
// Use Nitro's devStorage to mount a file-system store under the acc-sessions key.
// The module skips registering its own driver when this mount already exists,
// so this takes precedence automatically. Sessions are written to .data/.acc-sessions.
export default defineNuxtConfig({
levarneAcc: {
auth: {
enabled: true,
cookieSecret: process.env.COOKIE_SECRET,
// no storageDriver needed — the devStorage mount below takes over
},
},
nitro: {
devStorage: {
'acc-sessions': {
driver: 'fs-lite',
base: '.data/.acc-sessions',
},
},
},
})dynamodb — persistent, scalable storage backed by an AWS DynamoDB table. Suitable for production. Sessions are wrapped with an in-process memory cache to reduce read latency. Requires the table to exist with pk (string) as the partition key. Set a TTL attribute named sessionTtl on the table to have DynamoDB expire sessions automatically.
// nuxt.config.ts
levarneAcc: {
auth: {
enabled: true,
cookieSecret: process.env.COOKIE_SECRET,
storageDriver: {
type: 'dynamodb',
region: 'eu-west-1',
table: 'sessions',
namespace: 'my-app',
endpoint: 'http://localhost:8000', // optional — for local DynamoDB
},
},
}postgresql — persistent storage backed by a PostgreSQL table. Suitable for production. The table is auto-created on first use. Sessions are wrapped with an in-process memory cache to reduce read latency. Requires the pg package (and @types/pg for TypeScript) in the consumer app. Expired rows are filtered out on read but not deleted automatically — schedule a periodic clean-up to remove them.
// nuxt.config.ts
levarneAcc: {
auth: {
enabled: true,
cookieSecret: process.env.COOKIE_SECRET,
storageDriver: {
type: 'postgresql',
url: process.env.DATABASE_URL,
namespace: 'my-app',
// tableName: 'acc_sessions', // optional — defaults to 'acc_sessions'
},
},
}cosmosdb — persistent session storage backed by an Azure Cosmos DB for NoSQL container. Sessions are wrapped with an in-process memory cache to reduce read latency. The container must use /pk as its partition key and have TTL enabled; item TTL is set from the session lifetime. Requires Node.js 22 or later and the optional @azure/cosmos package.
// nuxt.config.ts
levarneAcc: {
auth: {
enabled: true,
cookieSecret: process.env.COOKIE_SECRET,
storageDriver: {
type: 'cosmosdb',
endpoint: process.env.COSMOS_DB_ENDPOINT,
key: process.env.COSMOS_DB_KEY,
database: 'my-app',
container: 'sessions',
namespace: 'my-app',
},
},
}