Laravel Sanctum CSRF Token Mismatch using Thunder Cilent (Testing API) - laravel

CSRF Token Mismatch when using Thunder Cilent to test Laravel Sanctum API. Works fine on app, just not when testing API outside app.
Trying to test API with Laravel Sanctum with Thunder Cilent (Think Postman but VSCode extension). However, despite setting the xsrf-token and Cookie, I'm always getting CSRF token mismatch.
Note logging on works fine in the app, it's only the api testing with thundercilent that's failing.
Getting the CSRF Token
GET http://localhost:8000/sanctum/csrf-cookie
Response Cookies
xsrf-token: eyJpdiI6ImN6Q3JLVEQrYnhXVXhyVWFQWC9YQlE9PSIsInZhbHVlIjoia2F2aTNjNDU2cTZURHRSSTN5Ny9ETnFJMGZoN0I2dmZ3bTA0UEZ6UjhzdCtCRjRPam9OSW5TWVkzYzAvMTQ0ZEp6b2JvYVdhRWg2TGsrejlkcnYzTGY3eGNFcTRGN253dUUxZjE3YXJBSFlVUHk4aGM5RmVYRWF6UFY2ZGRnYUEiLCJtYWMiOiI4OWU3OGI3MzQ3ZTdiNTNiZDQ2Yjg0ZDE3YWNiYmVhNDQ1NTI0MmI3MTY1NjdlZGI5ZGJlZDJlN2Q5NTc0ZjRhIiwidGFnIjoiIn0%3D
laravel_session: eyJpdiI6IjV6VTV4di9IMXNST1ZvNVh0K1pZelE9PSIsInZhbHVlIjoiTSsycEVWdjJ1VTc4dU81TVNJWTJ4aTRHOE81WTVHVW1OeU55OEt3cVU3bHc5N090dEdPQy9yZGJsamhOaDUzaFZmZVp0Z2FTeGp4UWJyVFVmSDdnVytTNS9SZTF5c0daak9EZ1I1V0w3aWpjTnVESWtIRmR2QzNGZ1VqWlZHZ2oiLCJtYWMiOiI0Mzg4NGI4MTc5MGQ1MDE1NTUxY2VmNGRmNGFkNjUyYmI1MjUwMTJiODQ4NmY4M2E5OTRlZGRlNTM3NjAzNTg1IiwidGFnIjoiIn0%3D
Logging in
POST http://localhost:8000/login
Body: {
"email": "example#example.com",
"password": "password"
}
Raw Headers:
User-Agent: Thunder Client (https://www.thunderclient.com)
Accept: application/json
Referer: http://localhost:3000
xsrf-token: eyJpdiI6ImN6Q3JLVEQrYnhXVXhyVWFQWC9YQlE9PSIsInZhbHVlIjoia2F2aTNjNDU2cTZURHRSSTN5Ny9ETnFJMGZoN0I2dmZ3bTA0UEZ6UjhzdCtCRjRPam9OSW5TWVkzYzAvMTQ0ZEp6b2JvYVdhRWg2TGsrejlkcnYzTGY3eGNFcTRGN253dUUxZjE3YXJBSFlVUHk4aGM5RmVYRWF6UFY2ZGRnYUEiLCJtYWMiOiI4OWU3OGI3MzQ3ZTdiNTNiZDQ2Yjg0ZDE3YWNiYmVhNDQ1NTI0MmI3MTY1NjdlZGI5ZGJlZDJlN2Q5NTc0ZjRhIiwidGFnIjoiIn0%3D
Cookie: XSRF-TOKEN=eyJpdiI6ImN6Q3JLVEQrYnhXVXhyVWFQWC9YQlE9PSIsInZhbHVlIjoia2F2aTNjNDU2cTZURHRSSTN5Ny9ETnFJMGZoN0I2dmZ3bTA0UEZ6UjhzdCtCRjRPam9OSW5TWVkzYzAvMTQ0ZEp6b2JvYVdhRWg2TGsrejlkcnYzTGY3eGNFcTRGN253dUUxZjE3YXJBSFlVUHk4aGM5RmVYRWF6UFY2ZGRnYUEiLCJtYWMiOiI4OWU3OGI3MzQ3ZTdiNTNiZDQ2Yjg0ZDE3YWNiYmVhNDQ1NTI0MmI3MTY1NjdlZGI5ZGJlZDJlN2Q5NTc0ZjRhIiwidGFnIjoiIn0;laravel_session=eyJpdiI6IjV6VTV4di9IMXNST1ZvNVh0K1pZelE9PSIsInZhbHVlIjoiTSsycEVWdjJ1VTc4dU81TVNJWTJ4aTRHOE81WTVHVW1OeU55OEt3cVU3bHc5N090dEdPQy9yZGJsamhOaDUzaFZmZVp0Z2FTeGp4UWJyVFVmSDdnVytTNS9SZTF5c0daak9EZ1I1V0w3aWpjTnVESWtIRmR2QzNGZ1VqWlZHZ2oiLCJtYWMiOiI0Mzg4NGI4MTc5MGQ1MDE1NTUxY2VmNGRmNGFkNjUyYmI1MjUwMTJiODQ4NmY4M2E5OTRlZGRlNTM3NjAzNTg1IiwidGFnIjoiIn0%3D;
I copied how this SO Postman example, but it's not working at all. Thunder Cilent doesn't have pre-run scripts so I can't add cookies that way.

Open thunder client, switch to Env and create an environment.
Click on the options button of your collection and choose settings.
Add a test to your collection by navigating to the Tests tab, then select Set Env Variable, set query to cookie.xsrf-token and value to {{XSRF-TOKEN}}
In the Headers tab, add an header named X-XSRF-TOKEN with value {{XSRF-TOKEN | urlDecode}}.
Create a request to /sanctum/csrf-cookie to refresh CSRF Token if it expires
This should fix CSRF Token.
Consult docs for more information: https://github.com/rangav/thunder-client-support

Decided just to use token based auth for API testing instead. Much easier then messing around with CSRF cookies.
Notes for my future self.
When testing the Token based auth, you need to set the accept to application/json otherwise it won't work.
If using collections, don't delete the Accept header in the request, just untick the box. Otherwise the request would override the collection header with something else and not work.

Related

Postman request receiving cookies but Postman not detecting them

I've recently started using Postman and I am testing an API where I get a CSRF token and then login but I always get a CSRF token mismatch. I am including X-XSRF-TOKEN header but I think the issue is around the cookies not being stored correctly.
I am calling a Laravel Sanctum endpoint to get a CSRF token and when I look in the in the console I can see Set-Cookie response headers
However, when I look in the cookies tab it says no cookies were received from the server
However, when I look at the cookies store they are listed for my test domain (home.local)
Due to this issue, when I send a request to the login, the session cookies are not sent in the request as shown in the console on the request to the login endpoint
I can do this fine using Insomnia.Rest client so I know the API is working as expected - I am however trying to replace Insomnia with Postman.
I've tried Google, but I've only found some bugs that were introduced that seemed to cause something similar back in 2016
Update
I managed to Postman working with production fine using a pre-request script to get the CSRF token and set the environment variable using the below:
const url = pm.environment.get('base_url');
const referer = pm.environment.get('Referer');
pm.sendRequest({
url: `${url}/sanctum/csrf-cookie`,
method: 'GET',
}, function (err, response, {cookies}) {
if (!err) {
console.log("cookies", cookies);
pm.environment.set('xsrf_token', cookies.get('XSRF-TOKEN'))
}
});
Although this worked on production and successfully did the POST request, on my local dev PC, I was still getting the CSRF mismatch.
Although the request/response looked the same between dev/and prod I for some reason had the idea to change my dev URL from my-app.home.local to my-app.home.com and now the cookies are received and send in the next request to login without getting a CSRF token mismatch.
There's clearly an issue with postman here but not sure if it's something I'm doing or a bug in Postman. Does .local mean something different?

Django REST framework no CSRF token with runserver but with runserver_plus

Using django-extensions' "runserver_plus" command, I've no problem with CSRF token. The token is defined in cookies as expected in server responses after login and I use it to sent it back to server in HTTP headers during unsafe HTTP requests (POST, PUT, etc...)
But for some reasons, I have to not use "runserver_plus" anymore and switch back to legacy "runserver" command. And since, the CSRF token I get from server after login does not work anymore. I got the error CSRF Failed: CSRF token missing or incorrect.. There is no other changes in the code. Just switch from runserver_plus to legacy runserver.
I really don't understand why. It's a mistake for me.
Of course, REST framework authentication classe is SessionAuthentication
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
),
}
Is someone have any idea ?
I've found why. It's because I use a underscore in HTTP header name for setting the CSRFTOKEN : I do this in javascript : header['X_CSRFTOKEN'] = mytoken. But because production servers strip underscored header name runserver do so. runserver_plus does not have the same behavior and allow underscored http header names.
Replacing my javascript by header['X-CSRFTOKEN'] = mytoken (dash instead of underscore) make it work.

How to develop test-automation using Postman when OAuth 2.0 authorization is required

I have an ASP.NET Web API 2 which is using OAuth 2.0 for authorization. And let's imagine I have a simple Web API method, like:
GET: http://host/api/profiles/user123 (requires OAuth 2.0 token)
So, with Postman, it is easy to test this Web API. I get an OAuth token for user123 from the Web API OAuthAuthorization method and then I use that token in the header of the HTTP request:
GET /api/profiles/user123 HTTP/1.1
Host: {host}
Authorization: Bearer {Token}
Content-Type: application/json
Cache-Control: no-cache
However, if I save my test and run it later (either by Postman itself or by Newman), the token will be expired at that time and it won't work.
How can I make Newman to get a new token automatically for user123 and use it in the HTTP request?
Note: I know how to use Postman's Authentication helpers to ask for a new token. But this scenario doesn't fit the test-automation. In test-automation, I want to remove any human interaction.
It's simple, get your access token at run time and save it into environment variable. Then use it in your next Get request.
In Get Token request, do this in Tests sections:
var body = JSON.parse(responseBody);
pm.environment.set('AccessToken', body.access_token);
In your main Get request, you can use the environment variable in Authorization header:
Authorization: Bearer {{AccessToken}}
Hope this helps.

TokenMismatchException in Postman even with token entered

I'm trying to do a simple PUT in the Postman Mac app to a Laravel 5.3 update web route.
Every time I do, I'm given a TokenMismatchException error.
I've tried:
Putting the token value in _token in the PUT body.
Putting the token headers as X-CSRF-TOKEN.
I've tried the Postman Interceptor with the proxy on so that it auto-gathers the correct cookies and token when I submit the form I'm simulating on my dev site.
I've tried a combination of all three of these.
None of this works.
Postman is normally a very valuable tool but Laravel seems to be defeating it (and me) at the moment.
What am I missing here?
I think you are missing sending token in 'Authorization' header for the request in postman in this format:
Authorization: Bearer {token here}
I have attached the screenshot here as well.

How to handle CSRF in preflighted CORS POST request in django?

I am trying to make POST request via AJAX from abc.com to URL from xyz.com (which is a Django application).
I am getting CSRF token by making a GET request to a URL on xyz.com, but the token changes when an OPTIONS request is made to xyz.com in the preflighted request.
Is there any way to get the response of OPTIONS request in the preflighted request ?
Note:
I am following instructions from following sources :
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
http://www.html5rocks.com/en/tutorials/cors/
Django CSRF protection will allow OPTIONS requests, so no problem with the first stage:
https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#how-it-works
If I understand correctly, you then want the next request (e.g. a cross-domain POST) to be allowed through. For this to work and get past Django's CSRF protection, the request must send a CSRF token (in POST data or in header for AJAX) and a matching CSRF cookie.
Now, cross-domain restrictions make it impossible for abc.com to set or read a cookie for xyz.com, whether from javascript or from a server side response. Therefore, this approach is impossible.
Instead you will have to apply #csrf_exempt to the view. This would allow any site to post to it. Therefore, you'll need to build in some other protection to the view. You are, of course, on your own in checking the security of your protection. Remember that 'Referer' and 'Origin' headers can easily be forged with something as basic as curl.
See django-cors-headers, you may find it how it works more suitable to solve your problem:
https://github.com/ottoyiu/django-cors-headers/
Django-rest-framework recommends http://www.django-rest-framework.org/topics/ajax-csrf-cors

Resources