Getting Started with Nuxt Bearer Auth
Learn how to install, configure, and secure your Nuxt application with server-side bearer authentication.
Overview & Getting Started Nuxt 4 / TypeScript
Introduction
nuxt-bearer-auth provides a server-side authentication layer between your Nuxt application and any external bearer-token API (e.g. Laravel Sanctum, FastAPI, Django REST Framework, Node.js, Go). Instead of storing bearer tokens in the browser'slocalStorage or cookies where they can be intercepted or exposed to client-side scripts, nuxt-bearer-auth holds the bearer token securely on the Nuxt server inside a Redis store and hands the browser an HTTP-only session cookie.
typescript
┌───────────────┐ HTTP-only Session Cookie ┌────────────────┐ Bearer Token ┌────────────────┐
│ Browser │ ◄──────────────────────────────► │ Nuxt Server │ ◄──────────────────► │ External API │
└───────────────┘ └───────┬────────┘ └────────────────┘
│
▼
┌────────────────┐
│ Redis Store │
│ (Server Session)│
└────────────────┘
Requirements
Before installing the module, ensure your environment meets the following:
^3.12.0 || ^4.0.0 (the module's declared peer dependency range)Compatibility & Verification
The module declares the Nuxt peer range^3.12.0 || ^4.0.0. Compatibility checks cover representative releases, not every version in that range. The module repository's consumer compatibility workflow builds and packs the current module, installs that package into an isolated consumer fixture, and runs Nuxt preparation and a production build.The automated matrix targets Nuxt 3.12.0, 3.21.11, and 4.6.1 on Node.js 24.19.0 with npm 10.0.0. Nuxt 3.21.11 and 4.6.0 have been manually verified with consumer preparation and production builds. Nuxt 3.12.0 and 4.6.1 remain unverified until their CI jobs succeed. Nuxt 5 compatibility has not been established. Check the workflow for current results; its configuration alone does not mean a run has passed.
Installation
Installnuxt-bearer-auth, redis client, and nuxt-csurf (for CSRF protection) using your package manager:
bash
# Using npm
npm install nuxt-bearer-auth redis nuxt-csurf
# Using pnpm
pnpm add nuxt-bearer-auth redis nuxt-csurf
# Using yarn
yarn add nuxt-bearer-auth redis nuxt-csurf
Nuxt Configuration
Addnuxt-bearer-auth to your nuxt.config.ts modules array and configure the API base URL and Redis connection:
typescript
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-bearer-auth'],
bearerAuth: {
apiBaseUrl: process.env.API_BASE_URL,
redisUrl: process.env.REDIS_URL || 'redis://127.0.0.1:6379',
redirects: {
login: '/login',
authenticated: '/dashboard',
logout: '/',
unauthorized: '/auth/not-allowed',
},
routes: {
public: ['/', '/login', '/forgot-password', '/reset-password'],
authPages: ['/login', '/forgot-password', '/reset-password'],
},
},
})
Environment Variables
Create or update your.env file with your backend and Redis credentials:
bash
# .env
API_BASE_URL=https://api.example.com
REDIS_URL=redis://127.0.0.1:6379
APP_ENV=local
Security Tip: In production, ensure
REDIS_URL uses password authentication or TLS (rediss://...) and is isolated within your private VPC network. Redis holds live session data including bearer tokens — treat it as sensitive infrastructure.Your First Login
In your Vue component or page (e.g.pages/login.vue), import and use the useBearerAuth() composable:
vue
<script setup lang="ts">
const auth = useBearerAuth()
const email = ref('')
const password = ref('')
const errorMessage = ref('')
const isSubmitting = ref(false)
async function handleLogin() {
isSubmitting.value = true
errorMessage.value = ''
try {
// Calls /api/auth/login -> authenticates with backend -> stores token in Redis -> sets session cookie
await auth.login({
email: email.value, // accepts email, username, phone, mobile, or identifier
password: password.value,
})
// Upon success, user is automatically redirected to redirects.authenticated ('/dashboard')
} catch (err: any) {
errorMessage.value = err?.data?.message || auth.error.value || 'Invalid credentials'
} finally {
isSubmitting.value = false
}
}
</script>
<template>
<div class="login-container">
<h1>Sign In</h1>
<form @submit.prevent="handleLogin">
<div v-if="errorMessage" class="error-banner">{{ errorMessage }}</div>
<label>Email Address</label>
<input v-model="email" type="email" required autocomplete="email" />
<label>Password</label>
<input v-model="password" type="password" required autocomplete="current-password" />
<button type="submit" :disabled="isSubmitting || auth.loading.value">
{{ isSubmitting ? 'Signing in...' : 'Sign In' }}
</button>
</form>
</div>
</template>
Protecting Routes
By default,nuxt-bearer-auth installs a global client and server route middleware.
1. Any route in routes.public is accessible to unauthenticated visitors.
2. Any route not listed in routes.public automatically redirects unauthenticated visitors to redirects.login (e.g. /login?redirect=/dashboard).
3. If an authenticated user visits an auth page (listed in routes.authPages such as /login), they are redirected to redirects.authenticated.
typescript
// Customizing route protection rules
bearerAuth: {
routes: {
public: ['/', '/about', '/pricing', '/login'],
authPages: ['/login', '/register'],
}
}