Authentication Flows

Detailed guide to implementing login, registration, social login, OTP, and token refresh.

Core Guides Nuxt 4 / TypeScript

useBearerAuth Composable

The primary interface for client-side authentication is useBearerAuth() (also aliased as useAuth()).
typescript
const {
  user,            // Ref<User | null>
  status,          // Ref<'idle' | 'loading' | 'authenticated' | 'unauthenticated'>
  ready,           // Ref<boolean>
  error,           // Ref<string | null>
  loading,         // ComputedRef<boolean>
  isAuthenticated, // ComputedRef<boolean>
  login,           // (credentials, redirectPath?) => Promise<AuthApiResponse>
  socialLogin,     // (credentials, redirectPath?) => Promise<AuthApiResponse>
  register,        // (payload, redirectPath?) => Promise<AuthApiResponse>
  verifyOtp,       // (payload, redirectPath?) => Promise<AuthApiResponse>
  fetchUser,       // (options?) => Promise<{ data, error }>
  refresh,         // () => Promise<AuthApiResponse>
  logout,          // (destination?) => Promise<void>
  forgotPassword,  // (payload) => Promise<any>
  resetPassword,   // (payload) => Promise<any>
  resendOtp,       // (identifier, payload?) => Promise<any>
} = useBearerAuth<CustomUserType>()

Standard Login Flow

The login() method submits your credentials to Nuxt's local endpoint /api/auth/login. You can pass any standard credential payload naturally — such as { email, password }, { username, password }, { phone, password }, { mobile, password }, or { identifier, password }. Nuxt validates that credentials are provided before forwarding the request to your backend endpoints.login, parses the bearer token, stores the session in Redis, sets an HTTP-only cookie on the client, and updates the reactive auth state.
typescript
const auth = useBearerAuth()

async function submitLogin() {
  try {
    const response = await auth.login({
      email: 'user@example.com', // Accepts email, username, phone, mobile, or identifier
      password: 'mypassword',
    })

    // If your backend requested an action (e.g. OTP verification):
    if (response.nextAction === 'verify_account') {
      await navigateTo('/verify-otp')
    }
  } catch (err) {
    console.error('Login error:', auth.error.value)
  }
}

Social Authentication

When using third-party identity providers (Google, Apple, GitHub), pass the received OAuth credential or token to socialLogin():
typescript
const auth = useBearerAuth()

async function onGoogleCallback(googleCredential: string) {
  await auth.socialLogin({
    provider: 'google',
    jwt: googleCredential,
  })
}

Your backend verifies the JWT with Google, generates a bearer token for the user, and returns it. Nuxt saves the session to Redis and issues the session cookie.

User Registration

Register new users via auth.register(payload):
typescript
async function handleRegister() {
  await auth.register({
    name: 'Enoch Tetteh',
    email: 'enoch@example.com',
    password: 'SecurePassword123!',
    password_confirmation: 'SecurePassword123!',
  })
}
If your backend immediately logs in registered users and returns a token, useBearerAuth completes the authentication flow and sets the session.

OTP Verification & Resend

For multi-factor authentication or account activation via One-Time Passwords:

typescript
const auth = useBearerAuth()

// 1. Verify OTP code
async function verifyCode(code: string) {
  await auth.verifyOtp({
    code,
    identifier: 'user@example.com',
  })
}

// 2. Resend OTP code
async function handleResend() {
  await auth.resendOtp('user@example.com')
}

Silent Token Refresh

If your backend issues short-lived bearer tokens and longer-lived refresh tokens, nuxt-bearer-auth manages this transparently.

Because both the access token and refresh token are stored in Redis on the server, you can trigger a token refresh without exposing the refresh token to the client:

typescript
const auth = useBearerAuth()

// Triggers server-side call to backend endpoints.refresh
// The refresh token is read from Redis — it is never sent to the browser
await auth.refresh()

Logout Flow

The logout flow always destroys the local Redis session regardless of whether the remote backend logout call succeeds. This means even if your backend is temporarily unreachable, the Nuxt session is cleared and the cookie is deleted.

typescript
const auth = useBearerAuth()

async function handleLogout() {
  // Destroys session and navigates to '/' (or custom destination)
  await auth.logout('/login')
}
The server-side sequence on logout: 1. Calls your backend endpoints.logout with the bearer token. 2. If the backend call fails, the error is logged as a warning — the logout continues regardless. 3. Deletes the Redis session key (DEL session:). 4. Removes the session from the user's active sessions set (SREM user_sessions:). 5. Clears the browser cookie. 6. Clears client-side auth state.