Nuxt Auth Sanctum

repository·main·Indexed 43 days ago

https://github.com/manchenkoff/nuxt-auth-sanctum

A Nuxt module for Laravel Sanctum authentication (version 3.1.2) that simplifies cookie-based authentication. It is SSR-ready and automates CSRF and token management, providing composables like useSanctumAuth and useSanctumFetch to interact with Laravel APIs.

Tokens
22.1K
Snippets
87
Records
109
Agent score
51%

What's inside Nuxt Auth Sanctum

  1. Overview of Nuxt Auth Sanctum features

    main

    Nuxt Auth Sanctum is a Nuxt module that provides a simple, SSR-ready way to integrate Laravel Sanctum authentication into your Nuxt application. It automates several authentication tasks, including CSRF token and cookie management, as well as Bearer token header management.

    Key capabilities include:

    • Authentication Access: Use the useSanctumAuth composable to access the current user and authentication methods.
    • Data Fetching: Use useSanctumFetch and useLazySanctumFetch to interact with your API while automatically handling authentication headers.
    • Flexible Modes: Supports both Client-Side Rendering (CSR) and Server-Side Rendering (SSR).
    • Security: Automated management of CSRF tokens and Bearer tokens.
    • Extensibility: Includes custom request and response interceptors, and allows subscribing to sanctum:* hooks.
    • Middleware: Provides pre-configured middleware for protecting routes that require authentication.
    • Type Safety: Full TypeScript support and the ability to cast current user information to custom classes.
  2. Configure server-side and client-side specific settings

    main

    The module allows different configurations for SSR (Server-Side Rendering) and CSR (Client-Side Rendering) using Nuxt's runtimeConfig.

    Configuration Priority:

    • Server: runtimeConfig.sanctum → runtimeConfig.public.sanctum → module defaults
    • Client: runtimeConfig.public.sanctum → module defaults

    Use runtimeConfig.sanctum for server-only settings (like internal Docker URLs) and runtimeConfig.public.sanctum for settings shared with the browser (like public TLDs).

    export default defineNuxtConfig({
        modules: ['nuxt-auth-sanctum'],
    
        // Default values for both server and client
        sanctum: {
            baseUrl: 'http://localhost:80',
            logLevel: 3,
        },
    
        // Server-specific overrides
        runtimeConfig: {
            sanctum: {
                baseUrl: 'http://laravel:80',  // Docker internal URL
                logLevel: 4,
            },
    
            // Client-specific overrides
            public: {
                sanctum: {
                    baseUrl: 'https://myapp.com',  // Public TLD
                    logLevel: 2,
                }
            }
        },
    })
  3. How Cookie Authentication works with Sanctum

    main

    When using cookie mode, the authentication flow follows these steps:

    1. Login: Authenticate the user by calling the login method from useSanctumAuth() with user credentials. Upon success, the user is redirected to the route defined in redirect.onLogin.
    2. CSRF Management: Once authenticated, the module automatically requests a CSRF cookie from the API and attaches it as an XSRF header to all subsequent requests.
    3. Header/Cookie Forwarding: The module handles passing headers and cookies from Client-Side Rendering (CSR) to Server-Side Rendering (SSR) requests.
    4. Extensibility: You can extend the default interceptors to add custom information to headers or cookie collections.
    const { login } = useSanctumAuth()
    
    const credentials = {
        email: "john@doe.com",
        password: "password",
        remember: true,
    }
    
    await login(credentials)
  4. How the Server Proxy URL transformation works

    main

    The serverProxy feature uses a catch-all route that is stripped from the final proxied URL sent to the backend.

    If you set sanctum.serverProxy.route to /api/sanctum and sanctum.serverProxy.baseUrl to http://api.frontend.dev, a request made to the local path /api/sanctum/user/profile will be forwarded to the backend as http://api.frontend.dev/user/profile.

    If your backend requires the prefix to remain, you must adjust the serverProxy.baseUrl accordingly.

    // Request URL = http://frontend.dev/api/sanctum/user/profile
    // Actual URL = http://api.frontend.dev/user/profile
    
    const { data } = await useSanctumFetch('/user/profile')
  5. SSR Streaming support for Cookie Authentication

    main

    The module is compatible with Nuxt's experimental.ssrStreaming mode. Because streaming commits response headers before the page finishes rendering, the module uses the following strategy to maintain authentication:

    1. Early Initialization: It initializes the CSRF cookie during plugin setup while headers are still writable.
    2. Cookie Queuing: Any cookies received after headers are committed are queued; non-HttpOnly cookies are written to the client after hydration.
    3. Session Restoration: HttpOnly session cookies are re-established via an additional credentialed CSRF request to ensure the browser stores them natively.

    Requirement: Ensure your CSRF endpoint does not respond with a session cookie, OR ensure your session cookie is not HttpOnly. If the session cookie is not HttpOnly, the module will forward it after hydration, but an extra CSRF request will be sent on the first load to restore the session.

  6. Use custom interceptors with sanctumClient

    main

    Interceptors allow you to define custom logic that runs during API calls made via useSanctumClient. This is useful for:

    • Adding custom headers (e.g., X-Localization, Accept-Language).
    • Implementing telemetry or logging for requests and responses.
    • Modifying request payloads before they are sent.

    These interceptors are powered by ofetch. If you are unfamiliar with ofetch interceptor patterns, it is recommended to review their documentation first.

  7. How TokenStorage works

    main

    The TokenStorage interface defines how the module interacts with your chosen storage mechanism.

    • get(app: NuxtApp): Called before each API request to load the token. It returns a Promise<string | undefined>.
    • set(app: NuxtApp, token?: string): Called after a successful login to save the token, or during logout (where token is undefined) to reset the storage.

    The NuxtApp instance is passed to these methods to allow for context-aware operations like app.runWithContext().

    /**
     * Handlers to work with authentication token.
     */
    export interface TokenStorage {
      /**
       * Function to load a token from the storage.
       */
      get: (app: NuxtApp) => Promise<string | undefined>;
      /**
       * Function to save a token to the storage.
       */
      set: (app: NuxtApp, token?: string) => Promise<void>;
    }
  8. Implement custom token storage

    main

    By default, the module stores the API token in cookies, which allows the token to be automatically included in both CSR (Client-Side Rendering) and SSR (Server-Side Rendering) requests.

    If cookies are not supported by your environment (for example, in Capacitor, Ionic, or when using LocalStorage), you can implement a custom storage mechanism by defining a custom storage provider in your app.config.ts following the required interface.

  9. Define custom token storage using the `sanctum:storage:token` Hook (Recommended)

    main

    The recommended way to implement custom token storage is via the sanctum:storage:token hook. This approach is compatible with all build modes, including nuxt generate (essential for static builds like Capacitor or Ionic apps) because the functions are preserved in the JavaScript bundle.

    To use this, create a client-side plugin and call useSanctumTokenStorage(storage) inside the hook.

    export default defineNuxtPlugin((nuxtApp) => {
      nuxtApp.hook("sanctum:storage:token", () => {
        const storage = {
          async get(app: NuxtApp) {
            const { Preferences } = await import("@capacitor/preferences");
    
            const result = await Preferences.get({ key: "sanctum.token" });
            return result.value ?? undefined;
          },
          async set(app: NuxtApp, token?: string) {
            const { Preferences } = await import("@capacitor/preferences");
    
            if (token) {
              await Preferences.set({ key: "sanctum.token", value: token });
            } else {
              await Preferences.remove({ key: "sanctum.token" });
            }
          },
        };
    
        useSanctumTokenStorage(storage);
      });
    });
  10. Resolve 'Unable to load user identity' (500/403/Fetch failed)

    main

    If the module fails to fetch the user identity during initialization, verify the following:

    • The URL is reachable and the schema (http vs https) is correct.
    • The API port is explicitly set (e.g., 8000).
    • If using Docker, the container is running.
    • If using artisan serve, ensure it is bound to localhost instead of 127.0.0.1.

    SSL/TLS Issues: If you encounter unable to verify the first certificate while using local HTTPS, you may need to set the environment variable NODE_TLS_REJECT_UNAUTHORIZED=0 in your environment.

  11. Protect pages from authenticated users with sanctum:guest middleware

    main

    The sanctum:guest middleware is used to ensure that only unauthenticated users (guests) can access a specific page. This is useful for pages like Login or Register where an already logged-in user should be redirected away.

    Behavior:

    • If the user is not authenticated, they are allowed to view the page.
    • If the user is authenticated, the middleware will redirect them to the path specified in the redirect.onGuestOnly configuration option.
    • Default Redirect: If no redirect rule is configured, the middleware will throw a 403 error.

    To use it, add sanctum:guest to the middleware array in your page's definePageMeta block.

    <script lang="ts" setup>
    definePageMeta({
        middleware: ['sanctum:guest'],
    });
    </script>
    
    <template>
        <!-- Page for guests only -->
    </template>
  12. Handle cookies with SSR streaming enabled

    main

    When experimental.ssrStreaming is enabled, you may see the warning: [response] response headers are already sent, cookies will be replayed on the client. This occurs because the HTML shell is sent before the full render, preventing the server from attaching cookies to the initial response.

    The module attempts to replay non-HttpOnly cookies after hydration. If your session (HttpOnly) cookie is missing, it means your CSRF endpoint is setting an HttpOnly cookie that JS cannot access.

    Solution: Move the session cookie creation to the login endpoint so it is only set during the authenticated login flow.