Lakmal Premaratne
Lakmal Premaratne

Reputation: 1229

Laravel 8, Sanctum, Fortify /logout throws "CSRF token mismatch" in Postman

I installed L8, Sanctum, and Fortify for authentication. I was able to /login (used a Pre-request Script to set the X-XSRF-TOKEN). I even get the /api/user successfully. But when I do /logout, I get "CSRF Token Mismatch" error in Postman. My settings in files are as below:

.env

SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost:8000

cors.php

'paths' => ['api/*', 'login', 'logout', 'register', 'sanctum/csrf-cookie']

fortify.php

'views' => false

/app/Http/Kernel.php

'api' => [
  \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
  'throttle:api',
  \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

I haven't used HasApiTokens trait in User model since this is cookie-based authentication.

In Postman, I am using the below headers for the /logout route:

Accept:application/json
Referer:localhost:8000
X-XSRF-TOKEN:{{xsrf-token}}

and I am making a POST request to http://localhost:8000/logout. Why I am getting "CSRF Token Mismatch" error?

Upvotes: 2

Views: 5244

Answers (3)

kachi_dk
kachi_dk

Reputation: 413

Vendor packages also use the .env (SESSION_DOMAIN and SANCTUM_STATEFUL_DOMAINS), so sometimes there is weird behavior.

Remove these from the .env if present

# SESSION_DOMAIN=
# SANCTUM_STATEFUL_DOMAINS=

Add these to the .env. Make sure the URL is in full (scheme, domain and port (when in development))

APP_URL=http://localhost:8000

FRONTEND_URLS=http://localhost:5173,http://localhost:5174,http://localhost:5175,http://localhost:5176

Add these to config/cors.php

return [
    'paths' => ['*'],

    'allowed_methods' => ['*'],

    'allowed_origins' => explode(',', env('FRONTEND_URLS')),

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,
]

Add these to config/sanctum.php

return [
...
'stateful' => explode(
        ',',
        env(
            'SANCTUM_STATEFUL_DOMAINS',
            sprintf(
                '%s%s%s',
                'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
                env('APP_URL') ? ',' . parse_url(env('APP_URL'), PHP_URL_HOST) : '',
                env('FRONTEND_URLS')
                    ? implode(
                        ',',
                        array_map(function ($url) {
                            return parse_url($url, PHP_URL_HOST);
                        }, explode(',', env('FRONTEND_URLS')))
                    )
                    : ''
            )
        )
    ),
...
]

Make sure this line is present in kernel.php

 protected $middlewareGroups = [
        'web' => [
            ...
        ],

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, // <---
          ...
        ],
    ];

Then don't forget to clear the cache both in development and server.

php artisan config:clear
php artisan route:clear
php artisan cache:clear

Upvotes: 0

NecklessPork
NecklessPork

Reputation: 53

In your App\Http\Middleware\VerifyCsrfToken middleware, add /api/logout to the $except array:

protected $except = [
    '/api/logout'
];

Upvotes: 1

Alan Horrocks
Alan Horrocks

Reputation: 324

I'm not sure if you ever solved this, but if not I've just had to solve the very same issue.

The reason it's failing is because the CSRF token is updated for each request so you need to add post-request script to your login request (and any other requests you make).

e.g. you can add a script to the Tests section of the request:

postman.setEnvironmentVariable("xsrf-token", postman.getResponseCookie("XSRF-TOKEN").value);
tests["CSRF token updated"] = true;

The second line is there to ensure the script doesn't report a fail every time.

I also found that it might be necessary to use decodeURIComponent() on the token value.

Upvotes: 3

Related Questions