How to make Axios JS use Cookies and XSRF Token
I am working on an application that needs to be able to login to a Laravel instance via API.
In my usual workflow all the session management and XSRF protection has been automagical but I needed to be able to build a test case - and all the docs seemed to assume it would “Just Work™”
It took me a while to figure out what was going on and how to make it work.
The Default Scenario
https://axios-http.com/docs/req_config
// `withCredentials` indicates whether or not cross-site Access-Control requests
// should be made using credentials
withCredentials: false, // default
If you are operating in a browser to a secure (https) site this should be all you need.
Testing Outside a Browser
If you are not in a browser you have to use a client that adds a cookie jar and make your requests with this instead of using axios directly.
This will retain cookies from previous responses and add the to future requests.
Test Server Does Not Have https
My test environment is running without https, while this is becoming increasingly problematic for JavaSCript development I can workaround it in this case.
The cookie behaviour still works - but for the XSRF protection to work I need the XSRF-TOKEN cookie value added to the request as an X-XSRF-TOKEN header (this acts as proof that the request comes from the same client).
Sending this header in plain text is insecure - so axios doesn’t automatically do it.
But for test purposes in a dev environment I am manually adding this header
Resulting Code
import axios from 'axios';
import { wrapper } from 'axios-cookiejar-support';
import { CookieJar } from 'tough-cookie';
// in a browser cookies are managed for us
// in this test script we need to setup a cookiejar
const jar = new CookieJar();
const client = wrapper(axios.create({ jar }));
const host = 'http://localhost'
try {
let options = { method: 'GET', url: host + '/sanctum/csrf-cookie', withCredentials: true };
let crsfReq = await client.request(options);
let now = new Date().valueOf();
options = {
method: 'POST',
url: host + '/api/auth/signup',
data: {
firstname: 'foo',
lastname: 'bar',
email: `${now}@example.com`,
password: now,
},
withCredentials: true
};
// I think that in a secure browser context axios does this bit for you
const cookie = (crsfReq.headers['set-cookie'])
.find(cookie => cookie.includes('XSRF-TOKEN'))
?.match(new RegExp(`^${'XSRF-TOKEN'}=(.+?);`))
?.[1];
options.headers = { 'X-XSRF-TOKEN': decodeURIComponent(cookie) }
const { data } = await client.request(options);
console.log(data);
} catch (error) {
console.error(error);
}
Install the pre-requisites with
npm install axios tough-cookie axios-cookiejar-support
Summary
I think it most cases this will happen fairly transparently, but I hope this helps anyone else who needs to make this work outside of a browser context.