Laravel X-CSRF-Token mismatch with POSTMAN
Asked Answered
D

12

31

I try to talk to my REST API built with Laravel. But the call with POSTMAN is rejected due to a token mismatch. I guess I need to include the CSRF token in the header. But do I need the encrypted one? When I insert this token I still get the error that there is a token mismatch.

I retrieve my token by using:

$encrypter = app('Illuminate\Encryption\Encrypter');
$encrypted_token = $encrypter->encrypt(csrf_token());
return $encrypted_token;

but is this supposed to change on every refresh?

Dentation answered 10/6, 2015 at 12:41 Comment(0)
L
29

If you aren't using forms - for an API for example - you can follow the steps here https://gist.github.com/ethanstenis/3cc78c1d097680ac7ef0:

Essentially, add the following to your blade or twig header

<meta name="csrf-token" content="{{ csrf_token() }}">

Install Postman Interceptor if not already installed, and turn it on

Then, in your browser log into the site (you need to be authorised), and either inspect element or view source to retrieve the token

In Postman, set GET/POST etc as needed, and in your header create a new pair

X-CSRF-TOKEN        tokenvaluetobeinserted235kwgeiOIulgsk

Some people recommend turning off the CSRF token when testing the API, but then you aren't really testing it are you.

If you do find you still have errors, check the response back using preview as Laravel tends to be fairly explicit with their error messages. If nothing is coming back, check your php_error.log (what ever it is called).


ps Oct 2018 - I now user Laravel Passport for handling API registration, logins and user tokens - worth a look!

Liaoyang answered 25/4, 2016 at 9:50 Comment(1)
You write one unit test for CSRF if needed and for other tests you can turn it off... you claim something like "I want to test some admin functionality A, but I need to be logged in so I have also test log in feature while I am testing functionality A". Not testing CSRF with every post/patch/put is OK - if you have separate unit test strictly for CSRF.Khalilahkhalin
R
18

Go to app/Http/Middleware/VerifyCsrfToken.php and add this values

    protected $except = [
        '/api/*'
    ];
Raddie answered 18/1, 2022 at 17:31 Comment(5)
This one works with Laravel 8! Thanks!Anticlastic
It would be useful if you explained that this would disabled csrf token verification. Providing a solution is not enough to answer on stackoverflow, you need to explain what it is that you are doing. This is a misleading answer.Joella
Any drawbacks to doing this on POST route as web hook? Could someone maybe post form to it anyway?Fracas
this is dangerous though. Don't do this.Intervention
Disabling CSRF token on post route means the route can be expose to CSRF forgery and an attack can make request on behalf of your users. please don't use thisAmmonate
M
7

I had this error while using a baseURL variable in my Postman environment. Turns out I was calling the site's URL without /api at the end. Sounds silly, but just to eliminate user error make sure you check that your request URL is based on:

https://<your-site-url>/api

Not:

https://<your-site-url>

Mule answered 11/4, 2020 at 11:0 Comment(0)
P
6

Yes it changes every refresh. You should be putting it in the view and when you post it needs to be sent as the value of the "_token" POST var.

If you are just using a standard POST just add this to the form:

<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">

If you are using AJAX make sure you grab the value of _token and pass it with the request.

REF: http://laravel.com/docs/5.1/routing#csrf-protection

Pedrick answered 11/6, 2015 at 2:37 Comment(1)
csrf token solved my issue. i have get it from a GET request and post it on Header for POST, PUT, DELETE requestsInfusionism
O
6

Use Postman
Make a GET request to any page that has
<meta name="csrf-token" content="{{ csrf_token() }}">
Copy the value from the response.

Add a header field to your POST request:

"X-CSRF-TOKEN: "copied_token_in_previous_get_response"
Oryx answered 6/6, 2018 at 4:6 Comment(2)
so I have to do this every time I am sending the request. that is not efficientIciness
Aditya, yes including CSRF protection is inconvenient, but if you could just generate a token it would defeat the purpose of CSRF tokens. If you don't want to use them you can disable them in the VerifyCsrfToken middlewareFrangible
J
4

If you are making REST API use api.php for writing routes, not web.php, according to Laravel documentation web.php is for writing routes for the website that's why you see csrf-token error while using it like API, So for API we have the api.php file which will not give you a csrf-token error.

Jumper answered 11/8, 2022 at 5:31 Comment(1)
I don't know why this post had 0 likes. I've spend all morning trying to find why i get CSRF mismatch. I've ended up sending all csrf related headers and cookies manually just to understand why i get mismatch. the reason was that I was using web routes instead of api routes. a clear example of why we don't have to take things for granted.Contour
T
2

I was following this tutorial when I encountered the error.I was making requests to routes in my web.php and while get requests were successful post requests returned the Token mismatch error even after adding an X-XSRF-TOKEN header. I came across this post that recommended commenting out \App\Http\Middleware\VerifyCsrfToken::class line in your web middleware group in app\Http\kernel.php file when making post requests to routes defined in your web.php. This worked for me. Just do not forget to uncomment it after you are done testing.

'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        // \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \App\Http\Middleware\HandleInertiaRequests::class,
        \Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class,
    ],
Tramp answered 5/5, 2023 at 7:37 Comment(0)
S
2

My solution is not so convenient but work for Laravel 8,9,10 as I've tested. You don't need to change initial values of Laravel as well:

  1. Create POSTMAN environment variable named XRSF-TOKEN (or any name you like).

  2. Create a GET method to access <your_host>/sanctum/csrf-cookie

  3. In the Tests section (POSTMAN), you input this snippet of code:

    pm.environment.set("XSRF-TOKEN", pm.cookies.get("XSRF-TOKEN"));

This will set the generated token from back-end into Postman Environment variable XSRF-TOKEN.

  1. At every request, you put into header X-XSRF-TOKEN variable and set value is {{XSRF-TOKEN}} besides other keys.

Done!!!

Every time you receive CSRF missed match message. You re-run request at step 2 to generate new token.

Hope this helps!

Streetman answered 9/8, 2023 at 11:48 Comment(0)
E
1

I just had the same issue and this answer finally helped me solving it: https://mcmap.net/q/470694/-laravel-8-sanctum-fortify-logout-throws-quot-csrf-token-mismatch-quot-in-postman

Apparently the CSRF token needs to be updated on every POST request. (Not on every GET request somehow.) You can solve this with a pre-request-script in Postman as explained in this tutorial: https://blog.codecourse.com/laravel-sanctum-airlock-with-postman/

Equilibrate answered 15/8, 2022 at 13:54 Comment(0)
L
0

For anyone visiting this recently

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
Lethargy answered 14/6, 2023 at 9:39 Comment(0)
J
0

Instead of defining your Route in routes/web.php define your Route in routes/api.php

Route::post('/user_api', [UserController::class, 'store']);

And the hit a api request in POSTMAN like this.

http://localhost:8000/api/user_api
Jugate answered 1/2 at 16:33 Comment(0)
J
-3

Adding /api to the url should solve this for most people just testing out their APIs... Eg. https://www.yoursite.com/api/register

Janik answered 26/12, 2021 at 22:32 Comment(1)
Your answer also makes no sense... You are basically changing the route and saying maybe it will work. When in reality you end up with 404 not found. I don't understand you answer... You cannot change the route and hope that it will work. You have to still hit the same destination. If you go to Route A and then try route A/api its a completely different destination. –Cockade

© 2022 - 2024 — McMap. All rights reserved.