Security Architecture

Detailed analysis of the threat model, token isolation, CSRF protection, and production hardening.

Core Guides Nuxt 4 / TypeScript

Threat Model & Philosophy

"Authentication should be boring. Security shouldn't be an afterthought."
nuxt-bearer-auth was designed to eliminate the most common vulnerability pattern in modern Vue/Nuxt SPAs: storing sensitive API bearer tokens in localStorage or client-accessible JavaScript memory.

We do not claim this package makes an application "100% unhackable". Security requires defense-in-depth across the entire stack. The guarantees described here are precise — read them carefully.

Why Tokens Stay on the Server

When a bearer token is stored in the browser:
  • Any Cross-Site Scripting (XSS) vulnerability in any third-party script, NPM dependency, or CDN can read localStorage.getItem('token') and exfiltrate it immediately.
  • The stolen token can be used from anywhere in the world until it expires.
  • Refresh tokens in the browser expose long-lived credentials to client compromise.
  • When using nuxt-bearer-auth:
  • The browser only possesses a random UUID session cookie flagged as HttpOnly, Secure, and SameSite=Lax.
  • Browser JavaScript — including malicious XSS payloads — cannot read the session cookie value via document.cookie.
  • Even if an attacker induces a malicious request, they cannot extract the underlying API bearer token from the browser.
  • The precise guarantee is: the bearer token itself is not exposed to browser JavaScript. This is different from claiming XSS has no impact at all — a successful XSS attack can still make authenticated requests on behalf of the user while the browser session is active.

    HTTP-Only Cookies: Strengths & Limits

    | Threat | Protected? | Explanation | | :--- | :---: | :--- | | Token exfiltration via XSS | YES | document.cookie cannot read HttpOnly cookies; the bearer token stays in Redis and is never sent to the browser. | | Local device token theft | YES | No plaintext bearer tokens are written to localStorage, sessionStorage, or any browser-accessible storage. | | Cross-Site Request Forgery (CSRF) | REQUIRES CSRF LAYER | Cookies are sent automatically with same-site requests; mitigated by nuxt-csurf. | | Authenticated XSS requests | PARTIAL | An attacker with XSS can make authenticated HTTP requests using the browser's session, but cannot read or exfiltrate the bearer token itself. |

    SSR Payload Security

    During SSR, the server plugin writes the user's profile and authentication status to the Nuxt hydration payload (nuxtApp.payload.bearerAuth). This payload is embedded in the initial HTML and is visible in the browser's page source. What is included in the payload: the user profile object (session.profile), the authentication status string, and the normalized session abilities array (string[] | null) that powers advisory client checks. What is never included: the bearer token, refresh token, session metadata (IP address, user agent, expiry timestamps). Because the user profile is client-visible, you should design your backend's /me endpoint to return only the data your UI genuinely needs. Avoid including internal secrets, raw credentials, or excessive PII in the profile object.

    CSRF Defense with nuxt-csurf

    Because browser cookies are automatically attached on cross-origin requests, nuxt-bearer-auth integrates with nuxt-csurf by default:
  • On mutating requests (POST, PUT, PATCH, DELETE), a cryptographic CSRF token is verified via the x-csrf-token header.
  • The composable's internal fetcher automatically includes the active CSRF token on all requests.
  • Pre-authentication endpoints (login, register, etc.) have CSRF enforcement disabled — these routes have no session to protect.
  • typescript
    bearerAuth: {
      csrf: {
        enabled: true,
        methodsToProtect: ['POST', 'PUT', 'PATCH', 'DELETE'],
        headerName: 'x-csrf-token',
      }
    }
    

    Redis Security Hardening

    Redis holds live session data including bearer tokens and user profiles. It must be treated as sensitive infrastructure.

    1. Network isolation: Never expose Redis port 6379 to the public internet. Keep Redis inside your private VPC or overlay network. The Nuxt server should be the only host that can reach Redis. 2. TLS in transit: Use rediss:// (Redis over TLS) in staging and production to encrypt all traffic between Nuxt and Redis. 3. Strong authentication: Require a high-entropy password or ACL credentials on your Redis instance. 4. Automatic key expiration: All session keys carry a Redis TTL (maxAge seconds). Sessions expire automatically without manual cleanup. 5. Credential storage: Store REDIS_URL (including any password) only in server-side environment variables — never commit credentials to source control.
    Current encryption posture: Session data is stored as JSON in Redis without application-level encryption. The sessionSecret configuration field is reserved for a future signing or encryption mechanism but is not used in the current release. Transport-level TLS via rediss:// is the recommended approach for protecting data in transit between Nuxt and Redis.

    Backend Authorization Boundary

    nuxt-bearer-auth protects your Nuxt application routes and keeps bearer tokens off the browser. It does not automatically secure your external backend API endpoints.

    This distinction matters:

  • A protected Nuxt page means: unauthenticated users are redirected before they can see your UI.
  • A protected Nuxt server route (using requireBearerAuthSession) means: the Nuxt server verified the request has a valid session before processing it.
  • Neither of these prevents a valid bearer token from being used directly against your backend API outside of Nuxt.
  • Your backend API remains the authoritative security boundary for its own resources. Backend endpoints should enforce their own authentication and authorization — verifying the bearer token, checking scopes or roles, and rejecting unauthorized requests regardless of where the request originates. nuxt-bearer-auth is a frontend session management layer. It works alongside backend authorization, not as a replacement for it.

    Production Deployment Checklist

    Before launching your application to production:

  • [ ] Enforce HTTPS: Cookies must have the Secure attribute enabled (automatic when NODE_ENV=production).
  • [ ] Set SameSite Attribute: Use sameSite: 'lax' (or 'strict' for high-security applications).
  • [ ] Configure Redis TLS: Ensure REDIS_URL uses rediss:// on cloud providers.
  • [ ] Isolate Redis: Redis should not be publicly accessible — restrict access to your Nuxt server only.
  • [ ] Require Redis authentication: Set a strong password or ACL rules on your Redis instance.
  • [ ] Set Strong API_BASE_URL: Point to your secure, authenticated API gateway.
  • [ ] Enable Backend Rate Limiting: Protect your backend /auth/login and /auth/refresh routes from brute-force attacks.
  • [ ] Set Short Backend Token Lifetimes: Configure your API (e.g. Laravel Sanctum or JWT) with short access token lifetimes (e.g. 15–60 minutes) combined with silent refresh.
  • [ ] Review SSR payload content: Ensure your backend /me endpoint returns only the profile data your UI needs — the profile object is included in the SSR hydration payload visible to the browser.