# Introduction This module provides a simple way to use Laravel Sanctum with Nuxt. SSR-ready! ## Key Features This module includes a range of features designed to streamline authentication: - `useSanctumAuth` composable for easy access to the current user and authentication methods - `useSanctumFetch` and `useLazySanctumFetch` to load data from your API - Automated `CSRF` token header and cookie management - Automated `Bearer` token header management - Both `CSR` and `SSR` modes support - Pre-configured middleware for pages that require authentication - Cast current user information to any class you want - Custom `request` and `response` interceptors - Subscribe to `sanctum:*` hooks to react as you want - Compatible with default Nuxt `ofetch` client - TypeScript support - ... and more, check the docs! ::warning --- target: _blank to: https://laravel.com/docs/10.x/sanctum#spa-authentication --- **Note**: Before using this module, please ensure you have configured Laravel Sanctum on your backend. You can find more information about Laravel Sanctum here. :: We recommend looking at our [breeze-nuxt](https://github.com/manchenkoff/breeze-nuxt){rel=""nofollow""} template that works flawlessly with [breeze-api](https://github.com/manchenkoff/breeze-api){rel=""nofollow""} Laravel application with preconfigured Sanctum and Echo modules. ## Ecosystem This project is a part of Nuxt Laravel modules ecosystem which you may find useful: ::card-group :::card --- icon: i-lucide-lock target: _blank title: Sanctum to: https://github.com/manchenkoff/nuxt-auth-sanctum --- Module for Sanctum authentication ::: :::card --- icon: i-lucide-radio target: _blank title: Echo to: https://github.com/manchenkoff/nuxt-laravel-echo --- Module for Echo broadcasting ::: :::card --- icon: i-lucide-badge-check target: _blank title: Precognition to: https://github.com/manchenkoff/nuxt-sanctum-precognition --- Module for Precognition form validation and Nuxt UI support, based on Sanctum ::: :::card --- icon: i-simple-icons-nuxt target: _blank title: Breeze Nuxt to: https://github.com/manchenkoff/breeze-nuxt --- Nuxt application starter with configured modules for Laravel ::: :::card --- icon: i-simple-icons-laravel target: _blank title: Breeze API to: https://github.com/manchenkoff/breeze-api --- Laravel API application starter with preconfigured Sanctum, Echo and Precognition ::: :: ## Support If you like this module, please support the project to help me maintain and improve it! [![Buy Me A Coffee](https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png){style="height: 60px !important;width: 217px !important;"}](https://www.buymeacoffee.com/manchenkoff) # Installation ## Quick Start You can use the following command to install the module and automatically register it in your `nuxt.config.ts` modules section ```bash [Terminal] npx nuxi@latest module add nuxt-auth-sanctum ``` or manually install a dependency via: ```bash [Terminal] pnpm add nuxt-auth-sanctum ``` and register the module in your `nuxt.config.ts`: ```typescript [nuxt.config.ts] export default defineNuxtConfig({ modules: [ // other modules 'nuxt-auth-sanctum' ], sanctum: {}, }) ``` ## Configuration Once you have the module installed and registered, provide the configuration in `nuxt.config.ts` according to your setup. ```typescript [nuxt.config.ts] export default defineNuxtConfig({ //... other parts of the config // nuxt-auth-sanctum options (also configurable via environment variables) sanctum: { baseUrl: 'http://localhost:80', // Laravel API } }) ``` That's it! You can now use Nuxt Auth Sanctum in your Nuxt app ✨ # Configuration ## Initial setup The only required configuration option is `baseUrl` which will be used for API calls to your Laravel API, so you can start using the module with the following definition: ```typescript [nuxt.config.ts] export default defineNuxtConfig({ modules: ['nuxt-auth-sanctum'], sanctum: { baseUrl: 'http://localhost:80', // Laravel API }, }) ``` ## Available options For any additional configurations, you can adjust the next list of available parameters: | Parameter | Description | Default | | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | | `baseUrl` | The base URL of the Laravel API | `undefined` | | `mode` | Authentication mode to work with Laravel API. Supported values - `cookie`, `token`. | `cookie` | | `origin` | The URL of the current application to use in Referrer header | `useRequestUrl().origin` | | `userStateKey` | The key to use to store the user identity in the `useState` variable. | `sanctum.user.identity` | | `redirectIfAuthenticated` | Determine whether to redirect the user if it is already authenticated on a login attempt. | `false` | | `redirectIfUnauthenticated` | Determine whether to redirect when the user got unauthenticated on any API request. | `false` | | `endpoints.csrf` | The endpoint to request a new CSRF token | `/sanctum/csrf-cookie` | | `endpoints.login` | The endpoint to send user credentials to authenticate | `/login` | | `endpoints.logout` | The endpoint to destroy current user session | `/logout` | | `endpoints.user` | The endpoint to fetch current user data | `/api/user` | | `csrf.cookie` | Name of the CSRF cookie to extract from server response | `XSRF-TOKEN` | | `csrf.header` | Name of the CSRF header to pass from client to server | `X-XSRF-TOKEN` | | `client.retry` | The number of times to retry a request when it fails | `false` | | `client.initialRequest` | Determines whether to request the user identity on plugin initialization | `true` | | `redirect.keepRequestedRoute` | Determines whether to keep the requested route when redirecting after login | `false` | | `redirect.keepRouteOnUnauthenticated` | Determines whether to pass the current route as a `redirect` query parameter when redirecting on 401 responses | `false` | | `redirect.onLogin` | Route to redirect to when user is authenticated. If set to false, do nothing | `/` | | `redirect.onLogout` | Route to redirect to when user is not authenticated. If set to false, do nothing | `/` | | `redirect.onAuthOnly` | Route to redirect to when user has to be authenticated. If set to false, do nothing | `/login` | | `redirect.onGuestOnly` | Route to redirect to when user has to be a guest. If set to false, do nothing | `/` | | `globalMiddleware.enabled` | Determines whether the global middleware is enabled | `false` | | `globalMiddleware.prepend` | Determines whether the global middleware is prepended to the list of middlewares | `false` | | `globalMiddleware.allow404WithoutAuth` | Determines whether to allow 404 page without authentication | `true` | | `logLevel` | The level to use for the logger. More details [here](https://sanctum.manchenkoff.me/advanced/logging). | `3` | | `appendPlugin` | Determines whether to append the plugin to the Nuxt application. More details [here](https://nuxt.com/docs/api/kit/plugins#options){rel=""nofollow""}. | `false` | | `serverProxy.enabled` | Determines whether the server side proxy is enabled. Available on server-side only. | `false` | | `serverProxy.route` | Nuxt server route to catch all requests. This route will receive any nested path as well. Available on server-side only. | `/api/sanctum` | | `serverProxy.baseUrl` | The base URL of the Laravel API. Available on server-side only. | `http://localhost:80` | For more details, please check the source code - [options.ts](https://github.com/manchenkoff/nuxt-auth-sanctum/blob/main/src/runtime/types/options.ts){rel=""nofollow""}. ## Overrides You can override any of these options in the `nuxt.config.ts` file: ```typescript [nuxt.config.ts] export default defineNuxtConfig({ modules: ['nuxt-auth-sanctum'], sanctum: { baseUrl: 'http://localhost:80', // Your Laravel API redirect: { onLogin: '/dashboard', // Custom route after successful login }, }, }) ``` ## RuntimeConfig Module configuration is exposed to `runtimeConfig` property of your Nuxt app, so you can override either in sanctum module config or `runtimeConfig.public.sanctum` property. ```typescript [nuxt.config.ts] export default defineNuxtConfig({ modules: ['nuxt-auth-sanctum'], runtimeConfig: { public: { sanctum: { baseUrl: 'http://localhost:80', }, }, }, }) ``` ## Server vs Client Configuration The module supports different configurations for server-side (SSR) and client-side (CSR) contexts. ### Configuration Priority | Context | Priority (highest to lowest) | | ------- | -------------------------------------------------------------------------- | | Server | `runtimeConfig.sanctum` → `runtimeConfig.public.sanctum` → module defaults | | Client | `runtimeConfig.public.sanctum` → module defaults | ### Examples **Shared config** (same for both server and client): ```typescript [nuxt.config.ts] runtimeConfig: { public: { sanctum: { baseUrl: 'http://localhost:80', }, }, } ``` **Different config** (server vs client): ```typescript [nuxt.config.ts] 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, // Verbose server logs }, // Client-specific overrides public: { sanctum: { baseUrl: 'https://myapp.com', // Public TLD logLevel: 2, // Minimal client logs } } }, }) ``` ### Server-Only Options The following options are only available on the server-side: - `serverProxy.enabled` - `serverProxy.route` - `serverProxy.baseUrl` ## Environment variables It is possible to override options via environment variables too. It might be useful when you want to use `.env` file to provide baseUrl for Laravel API. ::warning If you are using SSR (Server-Side Rendering) and relying *entirely* on `.env` files rather than hardcoding the `baseUrl` in your `nuxt.config.ts`, you **must** provide both the public and private environment variables. Otherwise, the server-side fetch will not see the public variable and the page will hang indefinitely with an infinite loop of SSR requests. :: Here is what it should look like in your `.env` file: ```env [.env] # Used by the browser (CSR) NUXT_PUBLIC_SANCTUM_BASE_URL='http://localhost:8000' # Used by the Nuxt server (SSR) NUXT_SANCTUM_BASE_URL='http://localhost:8000' ``` ::warning The `origin` option requires a static default in `nuxt.config.ts` for environment variables to work. Unlike other options, Nuxt ignores env var overrides for keys that are `undefined` by default. ```typescript sanctum: { origin: 'http://localhost:3000', // Set static default first } ``` Then in your `.env`: ```env # For client-side (CSR) NUXT_PUBLIC_SANCTUM_ORIGIN=https://your-domain.com # For server-side (SSR) NUXT_SANCTUM_ORIGIN=https://your-domain.com ``` Note: `NUXT_PUBLIC_SANCTUM_ORIGIN` only affects client-side, while `NUXT_SANCTUM_ORIGIN` only affects server-side. :: ## Configuration example Here is an example of a full module configuration ```typescript [nuxt.config.ts] export default defineNuxtConfig({ modules: ['nuxt-auth-sanctum'], sanctum: { mode: 'cookie', userStateKey: 'sanctum.user.identity', redirectIfAuthenticated: false, redirectIfUnauthenticated: false, endpoints: { csrf: '/sanctum/csrf-cookie', login: '/login', logout: '/logout', user: '/api/user', }, csrf: { cookie: 'XSRF-TOKEN', header: 'X-XSRF-TOKEN', }, client: { retry: false, initialRequest: true, }, redirect: { keepRequestedRoute: false, keepRouteOnUnauthenticated: false, onLogin: '/', onLogout: '/', onAuthOnly: '/login', onGuestOnly: '/', }, globalMiddleware: { enabled: false, allow404WithoutAuth: true, }, logLevel: 3, appendPlugin: false, } }) ``` # Cookie Authentication ## Usage By default, the module provides configuration to integrate seamlessly with Laravel Sanctum authentication based on the XSRF token. To explicitly set this authentication mode, update `sanctum.mode` configuration property to `cookie`. You can check the official Laravel documentation here - [SPA Authentication](https://laravel.com/docs/12.x/sanctum#spa-authentication){rel=""nofollow""}. ::warning Nuxt and Laravel applications must share the same top-level domain. For instance: - Nuxt application - `domain.com` - Laravel application - `api.domain.com` :: ## How it works First, you need to authenticate a user by submitting credentials to `endpoints.login` endpoint: ```typescript const { login } = useSanctumAuth() const credentials = { email: "john@doe.com", password: "password", remember: true, } await login(credentials) ``` The client will be automatically redirected to `redirect.onLogin` route of your application. Once the module has an authentication state, it will take care of requesting a CSRF cookie from the API and passing it as an XSRF header to each subsequent request as well as passing all other headers and cookies from CSR to SSR requests. You can also [extend default interceptors](https://sanctum.manchenkoff.me/advanced/interceptors) and add your information into headers or cookie collections. To check other available methods, please refer to the composables section. ## Laravel configuration Your Laravel API should be configured properly to support Nuxt domain and share cookies: - The Nuxt application domain should be registered in `stateful` domain list (`SANCTUM_STATEFUL_DOMAINS`) - The Nuxt application domain should be registered in `config/cors.php` in `allowed_origins` domain list - Also `config/cors.php` configuration should have `support_credentials=true` - Sanctum `statefulApi` middleware should be enabled - The top-level domain should be used for the session (`SESSION_DOMAIN=.domain.com`), or `localhost` during development (without port) If you notice incorrect behavior of the module or authentication flow, feel free to [raise an issue](https://github.com/manchenkoff/nuxt-auth-sanctum/issues/new/choose){rel=""nofollow""}! # Token Authentication ## Usage ::caution Beware, that token-based authentication is not recommended for SPA applications. :: Sometimes, token authentication might be useful when you cannot host your application on the same TLD or have a mobile or desktop application built with Nuxt (e.g. based on Capacitor). To explicitly set this authentication mode, update `sanctum.mode` configuration property to `token`. You can check the official Laravel documentation here - [API Token Authentication](https://laravel.com/docs/12.x/sanctum#api-token-authentication){rel=""nofollow""}. ## How it works First, you need to authenticate a user by submitting credentials to `endpoints.login` endpoint: ```typescript const { login } = useSanctumAuth() const credentials = { email: "john@doe.com", password: "password", remember: true, } await login(credentials) ``` The client will be automatically redirected to `redirect.onLogin` route of your application. To check other available methods, please refer to the **composables** section. The module expects a plain token value in the response from the API that can be stored in cookies to be included in all subsequent requests as `Authorization` header. You can also implement your [own token storage](https://sanctum.manchenkoff.me/advanced/token-storage){rel=""nofollow""} if cookies are not supported, for example - *Capacitor, Ionic, LocalStorage, etc*. ## Laravel configuration Your API should have at least two endpoints for login and logout which are included in `api.php` routes, so make sure that you do not use the same endpoints as for cookie-based authentication (`web.php` routes) to avoid **CSRF token mismatch** errors. ```php [routes/api.php] post('/login', [TokenAuthenticationController::class, 'store']); Route::middleware(['auth:sanctum'])->post('/logout', [TokenAuthenticationController::class, 'destroy']); ``` ::warning Keep in mind, that the domain where API requests are coming from should not be included in `SANCTUM_STATEFUL_DOMAINS` variable, otherwise you will get a **CSRF mismatch error**. :: The login endpoint must return a JSON response that contains `token` key like this ```json { "token": "" } ``` Here you can find an example from official documentation - [Issue API Token](https://laravel.com/docs/12.x/sanctum#issuing-api-tokens){rel=""nofollow""}. The logout endpoint should revoke the current client token to avoid inconsistencies with your Nuxt application state, please check official documentation - [Revoke API Tokens](https://laravel.com/docs/12.x/sanctum#revoking-tokens){rel=""nofollow""}. ::tip You can also try our API template with the already implemented authentication logic for both cookie and token approach - [breeze-nuxt](https://sanctum.manchenkoff.me/advanced/breeze-nuxt-template). :: ## Custom token storage Default token storage uses cookies to keep the API Authentication token and automatically load it for both CSR and SSR requests. However, you are free to define custom storage in your `app.config.ts` by implementing an interface. Check this section for more details - [Token storage](https://sanctum.manchenkoff.me/advanced/token-storage). # Server Proxy ## Usage When using **SSR**, it's often convenient to serve all operations under a single domain. You can achieve this by enabling the **server proxy catch-all** feature, which forwards client requests to your Laravel API while preserving cookies and headers. To enable it, update your `nuxt.config.ts`: ```typescript [nuxt.config.ts] export default defineNuxtConfig({ modules: ['nuxt-auth-sanctum'], ssr: true, sanctum: { baseUrl: '/api/sanctum', serverProxy: { enabled: true, route: '/api/sanctum', baseUrl: 'http://api.frontend.dev', }, }, }) ``` Once `serverProxy.enabled` is set to `true`, Nuxt adds a server route at: `http://frontend.dev/api/sanctum` which is defined as `serverProxy.route` parameter. Note that `sanctum.baseUrl` is now `/api/sanctum` (a local path), while `serverProxy.baseUrl` points to your Laravel backend. This setup tells Nuxt where to forward requests internally. You can test the proxy using any helper like `useSanctumFetch`: ```typescript // Request URL = http://frontend.dev/api/sanctum/user/profile // Actual URL = http://api.frontend.dev/user/profile const { data } = await useSanctumFetch('/user/profile') ``` The catch-all route (`/api/sanctum`) is stripped from the final proxied URL. If needed, you can customise this behaviour by modifying `serverProxy.baseUrl`. # useSanctumAuth ## Usage Composable provides 2 computed properties and 4 methods: - `user` - currently authenticated user (basically the same as `useSanctumUser`) - `isAuthenticated` - a boolean flag indicating whether the user is authenticated or not - `login` - method for logging in the user - `logout` - method for logging out the user - `refreshIdentity` - method for manually re-fetching current authenticated user data To authenticate a user you should pass the credentials payload as an argument to the `login` method. The payload should contain all fields required by your Laravel Sanctum backend. ```typescript const { login } = useSanctumAuth(); const userCredentials = { email: "user@mail.com", password: "123123", } await login(userCredentials) ``` If the login operation was successful, the `user` property will be updated with the current user information returned by the Laravel API. If you do not want to update the `user` property automatically (e.g. *for 2FA authentication*), you can disable identity fetching by passing optional argument to `login` method: ```typescript // user identity will not be loaded after successful response await login(userCredentials, false) ``` By default, methods will use the following Laravel endpoints: - `/login` to authenticate the user - `/logout` to log out the user - `/api/user` to get the current user information - `/sanctum/csrf-cookie` to get the `CSRF` token cookie To change the default endpoints, please check the [Configuration](https://sanctum.manchenkoff.me/usage/configuration) section. ### Additional `fetch` options If you want to pass additional header or change HTTP method for either `login` or `logout` calls, you can pass optional `options: SanctumFetchOptions` argument. For example, to log out the user with `DELETE` method instead of default `POST`: ```typescript const { logout } = useSanctumAuth() await logout({ method: "DELETE" }) ``` Use the same approach when you need to pass additional params to `login` call: ```typescript const { login } = useSanctumAuth() const userCredentials = { email: "user@mail.com", password: "123123", } await login( userCredentials, false, { headers: { "X-Custom-Header": "header_value" } } ) ``` # useSanctumUser ## Usage This composable provides access to the current authenticated user. It supports generic types, so you can get the user as any class you want. ```typescript interface MyCustomUser { id: number; login: string; custom_metadata: { group: string; role: string; }; } const user = useSanctumUser(); ``` If there is no authenticated user, the composable will return `null`. # useSanctumClient ## Usage All previous composables work on top of the `ofetch` client which can be used in your application as well. The client is pre-configured with `CSRF` token header and cookie management. All requests will be sent to the `baseUrl` specified in the [Configuration](https://sanctum.manchenkoff.me/usage/configuration) section. ```typescript const client = useSanctumClient(); const { data, status, error, refresh } = await useAsyncData('users', () => client('/api/users') ); ``` Since client implements `$Fetch` interface, you can use it as a regular `ofetch` client. Check examples in the ofetch [documentation](https://github.com/unjs/ofetch?tab=readme-ov-file#%EF%B8%8F-create-fetch-with-default-options){rel=""nofollow""}. # useSanctumFetch ## Usage Besides `useSanctumClient` you can directly send a request by using a module-specific version of fetch composable - `useSanctumFetch`. This composable uses Nuxt's native `createUseFetch` factory internally, providing full parity with Nuxt's built-in `useFetch`. For complete usage details, options, and return types, see the [official Nuxt documentation](https://nuxt.com/docs/4.x/api/composables/use-fetch){rel=""nofollow""}. ```typescript const { data, status, error, refresh, clear } = await useSanctumFetch("/api/users") // with options const { data } = await useSanctumFetch("/api/users", { method: "GET", query: { is_active: true }, }) ``` You can also use type casting to work with the response as an interface: ```typescript interface MyResponse { name: string } const { data } = await useSanctumFetch("/api/endpoint") const name = data.value?.name ``` # useLazySanctumFetch ## Usage This composable uses Nuxt's native `createUseFetch` factory internally, providing full parity with Nuxt's built-in `useLazyFetch`. For complete usage details, options, and return types, see the [official Nuxt documentation](https://nuxt.com/docs/4.x/api/composables/use-lazy-fetch){rel=""nofollow""}. Note: `lazy: true` is set internally. ```typescript const { data, status, error, refresh, clear } = await useLazySanctumFetch("/api/users") // with options const { data } = await useLazySanctumFetch("/api/users", { method: "GET", query: { page: 1 }, }) ``` You can also use type casting to work with the response as an interface: ```typescript interface MyResponse { name: string } const { data } = await useLazySanctumFetch("/api/endpoint") const name = data.value?.name ``` # useSanctumConfig ## Usage This composable provides quick access to the module configuration instead of using `useRuntimeConfig` and several keys like `public.sanctum`. The composable is **context-aware** - it automatically returns the appropriate configuration based on where it's executed: - **Server-side (SSR)**: Returns `runtimeConfig.sanctum` configuration - **Client-side (CSR)**: Returns `runtimeConfig.public.sanctum` configuration This allows you to have different settings for server and client contexts (e.g., different `baseUrl` for Docker internal network vs public TLD). ```typescript const config = useSanctumConfig(); // On server: runtimeConfig.sanctum.baseUrl // On client: runtimeConfig.public.sanctum.baseUrl console.log(config.baseUrl); ``` More details about the configuration structure can be found [here](https://sanctum.manchenkoff.me/usage/configuration). # useSanctumAppConfig ## Usage This composable provides quick access to the module configuration instead of using `useAppConfig().sanctum`. Take a look at the following example ```typescript const config = useSanctumAppConfig(); console.log(config.interceptors.onRequest); // appConfig.sanctum.interceptors.onRequest ``` More details about the configuration structure can be found [here](https://sanctum.manchenkoff.me/usage/configuration). # sanctum:auth ## Usage This middleware checks if the user is authenticated. If not, it will redirect a user to the page specified in the `redirect.onAuthOnly` option (default is `/login`). Also, you might want to remember what page the **user was trying to access** and redirect him back to that page after successful authentication. To do that, just enable the `redirect.keepRequestedRoute` option and it will be automatically stored in the URL for later redirect. Similarly, if the user's session expires while on a page and an API call returns `401`, you can enable `redirect.keepRouteOnUnauthenticated` to pass the current route as a `?redirect=` parameter to the login page, preserving the intended destination across re-authentication. If there is no redirect rule the middleware will throw `403` error. ## Example This is an example of middleware usage ```vue [app/pages/dashboard.vue] ``` # sanctum:guest ## Usage This middleware checks if the user is not authenticated. If not, it will redirect a user to the page specified in the `redirect.onGuestOnly` option (default is `/`). If there is no redirect rule the middleware will throw `403` error. ## Example This is an example of middleware usage ```vue [app/pages/login.vue] ``` # Global middleware ## Usage Instead of usage `sanctum:auth` and `sanctum:guest` on each page, you can enable global middleware that checks every route and restricts unauthenticated access. The behavior of this middleware is the same as [global middleware](https://nuxt.com/docs/guide/directory-structure/middleware){rel=""nofollow""} in Nuxt applications. ::warning Once global middleware is enabled, you can no longer use `sanctum:auth` and `sanctum:guest` on your pages. :: ## Configuration To enable middleware, use the following configuration in your `nuxt.config.ts` ```typescript [nuxt.config.ts] export default defineNuxtConfig({ modules: [ 'nuxt-auth-sanctum' ], sanctum: { baseUrl: 'http://localhost:80', redirect: { onAuthOnly: '/login', onGuestOnly: '/profile', }, globalMiddleware: { enabled: true, }, } }) ``` Keep in mind, you must define `onAuthOnly` and `onGuestOnly` routes to help the plugin understand which page should be excluded from the middleware. - `onAuthOnly` - this route is used to redirect unauthenticated users to let them log in, similar to `sanctum:auth` middleware - `onGuestOnly` - this route is used to redirect already authenticated users, similar to `sanctum:guest` middleware ::tip You can also set `globalMiddleware.prepend` to true to load it before any other middleware. :: ## Exceptions If you want to exclude an additional page besides `onAuthOnly` route, then you can define page metadata like in the example below: ```typescript definePageMeta({ sanctum: { excluded: true, } }) ``` This page will not be checked by global middleware regardless of user authentication status. ## Guest mode Sometimes, you may have more than one page which are available only for unauthenticated users, for instance: - "Sign up" page - "Forgot my password" page In these situations, you can use `sanctum.guestOnly` property of the page meta: ```typescript definePageMeta({ sanctum: { guestOnly: true, } }) ``` ::warning Keep in mind, that those pages still will be handled by global middleware to check the user authentication state, so for public pages, it is still better to use `sanctum.excluded` to speed up the loading process. :: ## Non-existing routes By default, when a user requests a non-existing route an error page will be thrown with 404 status, but you can also enable redirect to `onAuthOnly` instead by setting `allow404WithoutAuth` to `false`. ```typescript [nuxt.config.ts] export default defineNuxtConfig({ modules: [ 'nuxt-auth-sanctum', ], sanctum: { baseUrl: 'http://localhost:80', redirect: { onAuthOnly: '/login', onGuestOnly: '/profile', }, globalMiddleware: { enabled: true, allow404WithoutAuth: false, }, } }) ``` # sanctum:request ## Usage When your Nuxt application sends any request against the Laravel API, you can subscribe to the `sanctum:request` hook the same way as the ofetch interceptor `onRequest`. ::tip More details about interceptors can be found here - [interceptors](https://sanctum.manchenkoff.me/advanced/interceptors). :: ```typescript [app/plugins/sanctum-listener.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('sanctum:request', (nuxtApp, context, logger) => { logger.info('Sanctum request hook triggered', context.request) }) }) ``` Here is what the hook looks like ```typescript interface RuntimeNuxtHooks { /** * Triggers on every client request. */ 'sanctum:request': (app: NuxtApp, ctx: FetchContext, logger: ConsolaInstance) => HookResult } ``` # sanctum:proxy:request ## Usage ::warning This hook works only when you use [Server Proxy](https://sanctum.manchenkoff.me/usage/proxy) endpoint. :: When your Nuxt application sends any request against the Laravel API via proxy endpoint, you can subscribe to the `sanctum:proxy:request` hook the same way as the ofetch interceptor `onRequest`. ::tip More details about interceptors can be found here - [interceptors](https://sanctum.manchenkoff.me/advanced/interceptors). :: ```typescript [server/plugins/sanctum-listener.ts] export default defineNitroPlugin((nuxtApp) => { nitroApp.hooks.hook("sanctum:proxy:request", (context, logger) => { logger.info("Sanctum proxy request hook triggered", context.request); }); }); ``` Here is what the hook looks like ```typescript interface NitroRuntimeHooks { /** * Triggers on every client proxy request. */ "sanctum:proxy:request": (ctx: FetchContext, logger: ConsolaInstance) => void; } ``` # sanctum:response ## Usage When your Laravel API returns any response, you can subscribe to the `sanctum:response` hook the same way as the ofetch interceptor `onResponse`. ::tip More details about interceptors can be found here - [interceptors](https://sanctum.manchenkoff.me/advanced/interceptors). :: ```typescript [app/plugins/sanctum-listener.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('sanctum:response', (nuxtApp, context, logger) => { logger.info('Sanctum response hook triggered', context.request) }) }) ``` Here is what the hook looks like ```typescript interface RuntimeNuxtHooks { /** * Triggers on every server response. */ 'sanctum:response': (app: NuxtApp, ctx: FetchContext, logger: ConsolaInstance) => HookResult } ``` # sanctum:error:request ## Usage When you send a request to the API, it could raise an exception even before reaching the remote server. For these cases, `ofetch` uses `onRequestError`. All these errors are available via `sanctum:error:request` hook. ```typescript [app/plugins/sanctum-listener.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('sanctum:error:request', (context) => { console.log('Sanctum request error hook triggered', context) }) }) ``` Here is what the hook looks like ```typescript interface RuntimeNuxtHooks { /** * Triggers when receiving an error on fetch request. */ 'sanctum:error:request': (context: FetchContext) => HookResult } ``` # sanctum:error:response ## Usage When you send a request to Laravel API using any available module's composable, it may return an error, such as 401, 419, 403, 404, etc. ::tip By default, `nuxt-auth-sanctum` will try to redirect a user if 401 is returned. Unless you disable this by setting `sanctum.redirectIfUnauthenticated` to `false` in your `nuxt.config.ts` file. :: However, if you need more granular control over API errors, you can subscribe to the `sanctum:error` hook and process the HTTP response according to your requirements. ```typescript [app/plugins/sanctum-listener.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('sanctum:error:response', (response) => { console.log('Sanctum error hook triggered', response) }) }) ``` Here is what the hook looks like ```typescript interface RuntimeNuxtHooks { /** * Triggers when receiving an error response. */ 'sanctum:error:response': (response: FetchResponse) => HookResult } ``` # sanctum:redirect ## Usage The module can apply redirects in different situations, like `onLogin` or `onLogout` and you can subscribe to this event to keep track of any redirect happening before it is done. Subscribe to the `sanctum:redirect` hook which receives the URL of the target path of a redirect. ```typescript [app/plugins/sanctum-listener.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('sanctum:redirect', (url) => { console.log('Sanctum redirect hook triggered', url) }) }) ``` Here is what the hook looks like ```typescript interface RuntimeNuxtHooks { /** * Triggers when user has been redirected. */ 'sanctum:redirect': (response: FetchResponse) => HookResult } ``` # sanctum:init ## Usage Our module registers a plugin which requests a user identity once an application is started. This is needed for middleware and redirects to properly function. Subscribe to the `sanctum:init` hook which triggers once the identity request is completed. ```typescript [app/plugins/sanctum-listener.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('sanctum:init', () => { console.log('Sanctum init hook triggered') }) }) ``` ::warning Keep in mind, since `nuxt-auth-sanctum` is loaded before any other module/plugin, you might need to configure your own plugin and set dependencies as described in [Plugin dependencies](https://sanctum.manchenkoff.me/advanced/dependencies). :: Here is what the hook looks like ```typescript interface RuntimeNuxtHooks { /** * Triggers when an initial user identity request has been made. */ 'sanctum:init': () => HookResult } ``` # sanctum:refresh ## Usage When the authentication state changes (e.g. `onLogin`, `onLogout`), the module has to refresh the user identity. Subscribe to the `sanctum:refresh` hook which triggers once the identity refresh request is completed. ```typescript [app/plugins/sanctum-listener.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('sanctum:refresh', () => { console.log('Sanctum refresh hook triggered') }) }) ``` Here is what the hook looks like ```typescript interface RuntimeNuxtHooks { /** * Triggers when user identity has been refreshed. */ 'sanctum:refresh': () => HookResult } ``` # sanctum:login ## Usage Subscribe to the `sanctum:login` hook which triggers once the user is logged in and the identity refresh request is completed. ```typescript [app/plugins/sanctum-listener.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('sanctum:login', () => { console.log('Sanctum login hook triggered') }) }) ``` Here is what the hook looks like ```typescript interface RuntimeNuxtHooks { /** * Triggers when user successfully logs in. */ 'sanctum:login': () => HookResult } ``` # sanctum:logout ## Usage Subscribe to the `sanctum:logout` hook which triggers once the user is logged out and the identity reset is done. ```typescript [app/plugins/sanctum-listener.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('sanctum:logout', () => { console.log('Sanctum logout hook triggered') }) }) ``` Here is what the hook looks like ```typescript interface RuntimeNuxtHooks { /** * Triggers when user successfully logs out. */ 'sanctum:logout': () => HookResult } ``` # Interceptors ## Usage Interceptors allow you to define custom functions that will be used by [sanctumClient](https://sanctum.manchenkoff.me/composables/usesanctumclient) during API calls. Here are some examples of what you can do with it: - Add custom headers to all requests (e.g. `X-Localization`, `Accept-Language`, etc) - Use telemetry or logging for requests/responses - Modify the request payload before sending ::warning If you are not familiar with [ofetch](https://github.com/unjs/ofetch){rel=""nofollow""} interceptors, check this [documentation](https://github.com/unjs/ofetch?tab=readme-ov-file#%EF%B8%8F-interceptors){rel=""nofollow""} first. :: ## Configuration This module provides special hooks to define your interceptors: - `sanctum:request` - `sanctum:response` You can set up a new plugin and describe the behaviour of handling each outgoing request and incoming response. Here is an example of the plugin that writes a log entry for each request and response: ```typescript [app/plugins/sanctum-listener.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('sanctum:request', (app, ctx, logger) => { logger.info('Sanctum request hook triggered', ctx.request) }) nuxtApp.hook('sanctum:response', (app, ctx, logger) => { logger.info('Sanctum response hook triggered', ctx.request) }) }) ``` Each interceptor receives 3 arguments: 1. `app` - an instance of the current `NuxtApp` 2. `ctx` - `FetchContext` instance for the current operation with access to request, response, and options (*query, headers, etc*) 3. `logger` - an instance of a Consola logger used by the module (will be prefixed with `nuxt-auth-sanctum`) # Error handling ## Usage Error handling of API responses is not a part of this module since the main goal is to provide an authentication layer and configured API client, but on this page, you can find useful hints. When Laravel returns an error of any kind (*403, 404, 500, etc*), the module will throw this as an exception that has a generic `Error` type. ::tip By default, when Laravel API returns a `401` status code, **the module will reset the user identity** and redirect to `sanctum.redirect.onAuthOnly` route (if `sanctum.redirectIfUnauthenticated` is enabled). If you enable `redirect.keepRouteOnUnauthenticated`, the current route path will be passed as a `?redirect=` query parameter to the login page, allowing you to redirect the user back after re-authentication. :: ## Error type check This is how you can check what type of error you received ```typescript import { FetchError } from 'ofetch' const { login } = useSanctumAuth() const userCredentials = { email: 'user@mail.com', password: '123123', } async function onCredentialsFormSubmit() { try { await login(userCredentials) } catch (e) { if (error instanceof FetchError && error.response?.status === 422) { // here you can extract errors from a response // and put it in your form for example console.log(e.response?._data.errors) } } } ``` Sometimes, it is not convenient, especially when it comes to validation errors in plenty of forms and components. ## Error helper Here you can get inspiration from error handling specifically for this case and implement it your way. Create a new composable `useApiError` with the following content: ```typescript [app/composables/useApiError.ts] import { FetchError } from 'ofetch' const VALIDATION_ERROR_CODE = 422 const SERVER_ERROR_CODE = 500 export const useApiError = (error: any) => { const isFetchError = error instanceof FetchError const isValidationError = isFetchError && error.response?.status === VALIDATION_ERROR_CODE const code = isFetchError ? error.response?.status : SERVER_ERROR_CODE const bag: Record = isValidationError ? error.response?._data.errors : {} return { isValidationError, code, bag, } } ``` Use it as in the next example to extract all the errors from the response and handle it according to your logic: ```typescript try { await login(credentials) } catch (e) { const error = useApiError(e) if (error.isValidationError) { form.setErrors(error.bag) return } console.error('Request failed not because of a validation', error.code) } ``` Have a good debugging! 😎 # Logging ## Usage Sometimes it might be useful to check the system logs and messages from the plugin, especially if you want to check what headers and cookies are being sent. All messages will be written in the same manner as regular `console.log` messages and can be checked in the browser (for CSR) or in the Node console (for SSR). By default, the plugin uses `3` as logging level and shows error and informational logs without debugging details. You can override `sanctum.logLevel` parameter in the `nuxt.config.ts` and set one of these levels: - 0 - Fatal and Error - 1 - Warnings - 2 - Normal logs - 3 - Informational logs - 4 - Debug logs - 5 - Trace logs It follows the convention of the Consola project, more details can be found here - [Log Level](https://github.com/unjs/consola?tab=readme-ov-file#log-level){rel=""nofollow""}. # Token Storage ## Usage Token storage is used for keeping authentication token value from the Laravel API available for module consumption during requests assembling. Storage is used only when `sanctum.mode` equals to `token` in your nuxt configuration: ```typescript [nuxt.config.ts] export default defineNuxtConfig({ modules: ["nuxt-auth-sanctum"], sanctum: { // ... mode: "token", // ... }, }); ``` ::warning By default, if there is no custom token storage defined, cookies will be used. :: ## How it works Each token storage implements the following interface: ```typescript /** * Handlers to work with authentication token. */ export interface TokenStorage { /** * Function to load a token from the storage. */ get: (app: NuxtApp) => Promise; /** * Function to save a token to the storage. */ set: (app: NuxtApp, token?: string) => Promise; } ``` After the user sends credentials to the API module passes a token from the response to `set` method as well as the current Nuxt application instance to allow calls like `app.runWithConext()`. Once the user logs out, the module sends `undefined` as a token value to reset the stored value. Before each request against the API, the module loads the token by calling get method with Nuxt instance passed. ## Define token storage There are two approaches to define custom token storage: ### Using the `sanctum:storage:token` Hook (Recommended) The recommended way to define custom token storage is using the `sanctum:storage:token` hook. This approach works with all builds including `nuxt generate` (e.g. static builds for Capacitor/Ionic apps). ```typescript [plugins/sanctum-storage.client.ts] 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); }); }); ``` ::tip For Capacitor/Ionic apps, this is the **only** approach that works with `nuxt generate` because plugins are compiled by Vite into the JavaScript bundle, preserving the functions. :: ### Using app.config.ts You can define your own handler in the `app.config.ts` configuration file: ::warning The `app.config.ts` approach only works in **dev mode** and **Node.js production builds**. When using `nuxt generate` (static builds for platforms like Capacitor/Ionic), functions are stripped during JSON serialization and the custom tokenStorage won't work. :: ```typescript [app/app.config.ts] // LocalStorage example for Laravel Authentication token const tokenStorageKey = "sanctum.storage.token"; const localTokenStorage: TokenStorage = { get: async () => { if (import.meta.server) { return undefined; } return window.localStorage.getItem(tokenStorageKey) ?? undefined; }, set: async (app: NuxtApp, token?: string) => { if (import.meta.server) { return; } if (!token) { window.localStorage.removeItem(tokenStorageKey); return; } window.localStorage.setItem(tokenStorageKey, token); }, }; export default defineAppConfig({ sanctum: { tokenStorage: localTokenStorage, }, }); ``` Now your application will store tokens in a local storage of your browser. ::warning Keep in mind, `localStorage` is not available for SSR mode, so you should turn it off in your `nuxt.config.ts`. :: ## When to use which approach? | Approach | Works with `nuxt dev` | Works with SSR | Works with `nuxt generate` | | --------------------------------- | --------------------- | -------------- | -------------------------- | | Hook + `useSanctumTokenStorage()` | Yes | Yes | Yes | | `app.config.ts` | Yes | Yes | No | # Plugin dependencies ## Usage Sometimes you might need to use other plugins while making requests against your Laravel API, for instance - `i18n` headers enrichment on each sanctum fetch request like this: ```typescript [app/plugins/sanctum-plugin.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('sanctum:request', (app, ctx, logger) => { ctx .options .headers .set("X-Language", app.$i18n.localeProperties.value.code) }) }) ``` Since this module cannot know about its dependencies in your application, you should use one of the following approaches to configure this behaviour: - use `sanctum.appendPlugin` to register the Sanctum client plugin only after the previous modules are registered already - disable an automatic initial user request and call it from your custom plugin with a properly set list of dependencies ## Append plugin By default, all Nuxt plugins registered by the module use `prepend` operation on a list of plugins, which makes it load before other plugins. To change this behaviour, you can set the `sanctum.appendPlugin` config key to `true` and see that the sanctum plugin will be registered after most of the plugins from other modules. This is done by using `append` operation instead of `prepend` on the list of plugins. For more details, please check the Nuxt documentation [here](https://nuxt.com/docs/api/kit/plugins#options){rel=""nofollow""}. ## Manual initial identity request Even after changing the loading order of the plugin, there might be some cases when you need more granular control of the execution flow of the initial identity requests. For these kinds of situations, you should disable a plugin initialization request by setting `sanctum.client.initialRequest` to `false` and use it in your custom plugin like this: ```typescript [app/plugins/custom-auth.ts] export default defineNuxtPlugin({ name: 'custom-auth', dependsOn: ['@nuxtjs/i18n', 'nuxt-auth-sanctum'], async setup() { nuxtApp.hook('sanctum:request', (app, ctx, logger) => { ctx .options .headers .set("X-Language", app.$i18n.localeProperties.value.code) }) const { init } = useSanctumAuth() await init() } }) ``` This approach can guarantee that the user's identity will be requested with all dependent plugins loaded properly. ::warning Beware, in the case of using a custom plugin for identity initial requests, you might need to handle API errors on your own (e.g. 401, 419) due to missing CSRF cookie values. You can check the default implementation for reference - [identity request error handling](https://github.com/manchenkoff/nuxt-auth-sanctum/blob/main/src/runtime/plugin.ts#L62){rel=""nofollow""}. :: # Breeze Nuxt Template ## Application template Suppose you want to start a fresh project based on Nuxt and Laravel Sanctum with Laravel Echo integration. In that case, you may consider trying out the template repository that has implemented Echo integration and all authentication logic and also contains several pages such as: - Landing - Login - Sign up - Password reset - Dashboard The repository is available here - [breeze-nuxt](https://github.com/manchenkoff/breeze-nuxt){rel=""nofollow""}, follow the guide in the `readme.md` file to set up Laravel API and connect it to the front-end application. Also, it uses the Nuxt UI module that allows you to start building complex interfaces with ease thanks to predefined components and Tailwind CSS. For more details, check the repository. As for the backend API part, we also have you covered. Check out our [breeze-api](https://github.com/manchenkoff/breeze-api){rel=""nofollow""} template. # Troubleshooting ## Usage Since Laravel Sanctum requires a specific configuration for your application, your production might work differently in comparison to a local development environment. On this page, you can find a description of the most common issues raised on GitHub and how to solve them by adjusting either your Laravel or Nuxt application configurations. ## Common problems First of all, if you experience any unexpected behaviour, we recommend enabling `logLevel: 5` in your `nuxt.config.ts` to get more details in SSR (server console) or CSR (browser console) output. For more details about logging, please refer to this page - [Logging](https://sanctum.manchenkoff.me/advanced/logging). In case of misconfiguration on either Nuxt or Laravel side, you may experience: - Authentication state reset on page reload - Difference between CSR and SSR state - Errors while trying to log in - Failing subsequent requests after successfully logging in ::tip Before searching for a solution to your problem, we highly recommend double-checking your configuration and ensuring that the cache is cleared and your runtime reflects the latest changes. :: ## Known problems Below you can find the description of the most popular issues, which were already resolved. ### Works on client-side (CSR), but not on server-side (SSR) If you have SSR enabled, our module sends some of the requests on plugin initialisation before returning content to the client, which means we have to proxy some initial request data which might work differently on the server side. For example, to fetch user identity we are passing cookies from the initial client request. ::warning Since CSR and SSR requests are sent from different environments, you have to ensure that your API is accessible from both of them and Laravel allow accepting requests from CSR/SSR hosts. :: If you use a Docker container, make sure that the `sanctum.baseUrl` in your `nuxt.config.ts` is accessible from both your web browser and the Nuxt container. We recommend using a domain name as the name of the container in the virtual network to avoid side effects. ::tip You can check the example here - [breeze-api](https://github.com/manchenkoff/breeze-api/blob/main/docker-compose.yml#L2){rel=""nofollow""}. :: In case you use additional software to set up virtual domains for development purposes (e.g. Laravel Valet, Homestead, dnsmasq, etc), you may end up with incorrect DNS resolving by Node. We recommend to use `localhost` domain with different ports instead. ### Request blocked by CORS policy Incorrect CORS configuration on the Laravel side can cause the following problem ::caution Access to fetch at 'X' from origin 'Y' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'Z' that is not equal to the supplied origin. Have the server send the header with a valid value, or, if an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. :: In this case, your Nuxt application is calling API endpoint **X** from host **Y**, which is not the same as **Z** configured as `allowed_origins` in Laravel's `config/cors.php`. If you are using Laravel Breeze, then adjusting `FRONTEND_URL` environment variable would be enough. ### Unable to load user identity from API (Code 500 / 403) If Nuxt cannot retrieve user identity on plugin initialization, that means that either your API is not reachable or there is an endpoint misconfiguration. For example, the following error means that `fetch` could not find a host with `laravel.test` URL due to network problems. ```text Unable to load user identity from API [GET] "https://laravel.test/api/user": fetch failed ``` You should double-check: - URL exists and is reachable, - schema is chosen correctly (*http/https*), - API port is set correctly (e.g. *80, 8080, 8000, 3000*) - a Docker container is up and running (if applicable), - `artisan serve` is using `localhost` instead of `127.0.0.1` (if applicable) Also, while working locally with enabled SSL, you may face the following error: ```text [nuxt-auth-sanctum:ssr] ERROR Unable to load user identity from API [GET] "https://laravel.test/api/user": fetch failed [cause]: fetch failed [cause]: unable to verify the first certificate ``` To enable HTTPS protocol, you might need to set an environment variable `NODE_TLS_REJECT_UNAUTHORIZED=0`. ### Page hangs on load with infinite SSR requests when using only NUXT\_PUBLIC\_SANCTUM\_BASE\_URL If your app never finishes loading and your terminal shows an infinite loop of SSR plugin setup and requests to your base URL with no response, this is likely the cause. This happens because you defined your API URL in your `.env` file using **only** `NUXT_PUBLIC_SANCTUM_BASE_URL`. Nuxt strictly isolates public and private runtime configurations. When the server attempts to fetch your user identity during SSR, it cannot see the public variable and the request never resolves. **Fix:** Add `NUXT_SANCTUM_BASE_URL=http://your-api-url` to your `.env` file alongside the public one to ensure the server context uses the correct URL. ### User is not authenticated on plugin initialization (Code 401) With enabled logging, you can check your Nuxt logs to find errors and warnings about the reason for the 401 response. For example, if you see the following message there: ```text [nuxt-auth-sanctum:ssr] WARN [response] set-cookie header is missing [nuxt-auth-sanctum:ssr] ⚙ User is not authenticated on plugin initialization, status: 401 ``` then you should check your SANCTUM\_STATEFUL\_DOMAINS environment variable on the Laravel side. If you have a domain different than your Nuxt application is hosted on, it can cause an issue. ### CSRF mismatch (Code 419) In the logs you can see this entry - **`CSRF token mismatch, check your API configuration`**. This error usually occurs if your API returns a 419 status code, meaning Laravel expects a different cookie value which in most cases can be solved by adjusting the `SANCTUM_STATEFUL_DOMAINS` or `SESSION_DOMAIN` environment variables in your Laravel application. Keep in mind, that Laravel supports cookies only from the same TLD, meaning you cannot call your API from a different domain. For instance: - frontend app - `https://myapp.com` - backoffice app - `https://admin.myapp.com` - Laravel API - `https://api.myapp.com` In this setup, `SESSION_DOMAIN` should be `.myapp.com` and `SANCTUM_STATEFUL_DOMAINS` should be `myapp.com,admin.myapp.com`. ::warning If you want to use token authentication, make sure to remove your frontend application from stateful domains to avoid CSRF-check middleware. :: ### Missing headers in the API request If you use `routeRules` and do not see Nuxt passing some of the expected headers to your Laravel API, it might be because of proxying behaviour, which is a bit different from the direct fetch request. Make sure that you also define supported headers in your `nuxt.config.ts` like this: ```typescript [nuxt.config.ts] export default defineNuxtConfig({ // ... other config routeRules: { '/backend/api/**': { proxy: { to: `http://laravel.test/api/**`, headers: { YOUR_HEADER: 'header_value' }, } } } }) ``` If you could not find anything useful, please check the [Issues](https://github.com/manchenkoff/nuxt-auth-sanctum/issues?q=is%3Aissue%20state%3Aclosed){rel=""nofollow""} section on GitHub or feel free to [create a new one](https://github.com/manchenkoff/nuxt-auth-sanctum/issues/new?template=bug_report.md){rel=""nofollow""}!