Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

beertastic's avatar

fortify api and xsrf token with JetClient in PHP Storm

I've got an APi built (I'm using controllers and not resource files to return Json, but I think that's a non issue)

I can login using Sanctum, I get a bearer token and all the routes I want protected, are working.

Now I want to add 2FA to my app and I've installed Fortify, turning off views etc.

So, all via Jet Client (Postman alternative)

  • I log in, I get bearer token.
  • I ping a GET to : /sanctum/csrf-cookie I receive:
Cache-Control	no-cache, private
Date	Sun, 20 Oct 2024 09:16:31 GMT
Server	nginx
Set-Cookie	XSRF-TOKEN=eyJpdiI6InIycWFJRDdrSW1XcDZudmhLSkNOUEE9PSIsInZhbHVlIjoiQUxTVnlYYmxNVHhzRThtdlBQSGdlTzU0SFNmQmRpL3hrYXFEZXJER2lhdks0TXhUYjdQeTVsRFpudFZOZko3Q3hrajE4MlNDc1ZmQ05HMzJwajZzTTN2amhPOHB3dVJPelpDQUxCZVIva1V0Yi9MeDZtS2ZIN1BvUkNoaFcwckwiLCJtYWMiOiIzMTRkNTJiNWNhOTdhMWQzMDU1MGIxNjVhYTUzMmFkZWQ0NzNjOWI0NzczNjI4YjEyZWFiMjcyMDgzYjg4ZmFlIiwidGFnIjoiIn0%3D; expires=Sun, 20 Oct 2024 11:16:31 GMT; Max-Age=7200; path=/; domain=https://cos.ddev.site; secure; samesite=lax
Set-Cookie	laravel_session=eyJpdiI6Iis1d3BWQW43bDJPWmxleW5PTUNzaUE9PSIsInZhbHVlIjoiZXRDaEQvbFJMU1E3amF1dFhtSWFVc1lxeVpRL0U2QmQ2RXoySW43ck5iMFpoeWtibU00ZE1iSWRaL29jS3ZzM0dCMVdDa2IyUW1abGlhbzNEMGFwSXJnUmppMW41QzA0TDhVTzVqVG5qQmdrSnZyUkhzNE05c1hMYzVnTG0rTTQiLCJtYWMiOiJmNmM1ZDFjY2JiN2E0OWIwOGU1NmU4N2ZjNjk1NjNkM2IxNTlkNmMyNTcwZTcyZmQzMzJlOTdlMjQ2MmRjODM1IiwidGFnIjoiIn0%3D; expires=Sun, 20 Oct 2024 11:16:31 GMT; Max-Age=7200; path=/; domain=https://cos.ddev.site; secure; httponly; samesite=lax

I'm supposed to use the XSRF token in calls to the 2FA routes (apparently) but don't know exactly what to extract from the results above, or where to add them to the 2FA calls in jetclient, body, header etc?

I want to POST to /user/two-factor-authentication Ot start the 2FA process.. but I get

{
  "message": "Unauthenticated."
}

I DO pass the bearer token, but as described above, I don't know what I'm supposed to extract from the sanctum call.

I'm a little rusty on APi work.. is this s'thing I can get a nudge with please?

0 likes
11 replies
LaryAI's avatar
Level 58

To integrate 2FA with Laravel Fortify and handle CSRF tokens using JetClient (or any HTTP client), you need to follow these steps:

  1. Understanding the CSRF Token: When you make a request to /sanctum/csrf-cookie, Laravel sets a XSRF-TOKEN cookie. This token is used to protect against CSRF attacks and should be included in the headers of your subsequent requests that require CSRF protection.

  2. Extracting the CSRF Token: From the response headers of the /sanctum/csrf-cookie request, you need to extract the XSRF-TOKEN value. This is typically done by parsing the Set-Cookie header.

  3. Using the CSRF Token in Requests: For any request that requires CSRF protection (like your 2FA routes), you need to include the X-XSRF-TOKEN header with the value of the extracted token.

Here's a step-by-step guide on how to do this:

Step 1: Get the CSRF Token

Make a GET request to /sanctum/csrf-cookie and extract the XSRF-TOKEN from the response cookies.

Step 2: Include the CSRF Token in Your Requests

When making requests to your 2FA routes, include the X-XSRF-TOKEN header. Here's an example of how you might do this in PHP using a generic HTTP client:

// Assuming you have a function to make HTTP requests
function makeRequest($method, $url, $headers = [], $body = null) {
    // Your HTTP client logic here
}

// Step 1: Get the CSRF token
$response = makeRequest('GET', 'https://your-app-url/sanctum/csrf-cookie');

// Extract the XSRF-TOKEN from the response cookies
preg_match('/XSRF-TOKEN=([^;]+)/', $response['headers']['Set-Cookie'], $matches);
$xsrfToken = $matches[1];

// Step 2: Use the CSRF token in your 2FA request
$headers = [
    'Authorization' => 'Bearer your_bearer_token',
    'X-XSRF-TOKEN' => $xsrfToken,
    'Content-Type' => 'application/json',
];

$body = json_encode([
    // Your 2FA request payload
]);

$response = makeRequest('POST', 'https://your-app-url/2fa-endpoint', $headers, $body);

// Handle the response

Notes:

  • JetClient: If you're using JetClient, the process is similar. You need to ensure that the X-XSRF-TOKEN header is set in your requests. JetClient should allow you to set custom headers for your requests.

  • Bearer Token: Ensure that your requests also include the Bearer token for authentication, as shown in the example.

  • Environment: Make sure your environment (e.g., local development, production) is correctly configured to handle cookies and headers, especially if you're working with secure connections (https).

By following these steps, you should be able to successfully integrate 2FA with Fortify and handle CSRF tokens in your API requests.

beertastic's avatar

OK, I think I understand. But I'm passing everything correctly (I get a CSRF mismatch error if I corrupt the token generated, so I know that works) And I'm still getting 'Unauthenticated. It's like the Auth process that Sanctum handles, isn't recognised by Fortify? I don't really understand that.

I assumed that once logged in, Laravel in general would know that Auth:: methods to log a user in were universal.

beertastic's avatar

My order of events: All via JetClient Api, calling a Ddev running instance of my application server

  1. ping GET: /sanctum/csrf-cookie

  2. Extract XSRF Token

  3. Apply to header of login page

  4. login via APi, passing correct credentials. (working as I get the user model back and other things I set in LoginController)

  5. Ping: user/two-factor-authentication to start the 2FA process as per the docs.

  6. Unauthorised.

I as I'm pinging from my local machine, to the Ddev server.. Perhaps the SANCTUM_STATEFUL_DOMAINS in env is wrong? SESSION_DOMAIN too?

puklipo's avatar

I can't answer unless you provide all the information.

If you use stateless API token authentication, don't use fortify, implement 2FA yourself. fortify requires stateful authentication.

beertastic's avatar

Perhaps I'm just misunderstanding the whole process. I want to enable Fortiy and the 2FA features for my Api, that'll be used by other front ends. To do that, I need to use CSRF tokens to confirm the client is who they say they are? Get a CSRF token, login with that token int he header and then that CSRF is 'tied' to the logged in user. Then pass that CSRF with every call and the Fortify processes related to 2FA will know that the requester is the logged in user.

Is that not 'stateless'?

omeratayilmaz's avatar

@beertastic It seems you've a bit of confusion between CSRF and stateless APIs.

CSRF tokens are used in session-based (which we called 'stateful') web applications, whereas stateless APIs typically use JWT/Sanctum tokens for authentication.

In a stateless setup, after the user logs in and completes 2FA (with Fortify), you would issue a JWT token, which the client includes in the headers of each request. (i.e Larry's code)

To sum up; stateless APIs don’t require CSRF tokens. Instead, you would use JWT tokens to authenticate requests

(sorry for typo, I am not good at English :) )

omeratayilmaz's avatar

@beertastic Yes, if both API authentication and two-factor authentication (2FA) are required, using Sanctum and Fortify together would be a good solution.

However, I am not sure if it is easy to understand or not; it actually depends on the developer's experience with authentication

beertastic's avatar

I'll be honest, in all the docs and tutorials I've looked at over the last week, this is the first time I've seen or heard of JWTs Is there another name for this perhaps?

I've build many apps with various levels of security.. but this is my first dive into 2FA and Fortify.. There seems to be much confusion out there.. I'l keep studying.. and seeing if I can find more on JWT in relation to the Sanctum/fortify pairing for Api apps.

JussiMannisto's avatar

@beertastic

this is the first time I've seen or heard of JWTs Is there another name for this perhaps?

Nope. JWT is one of the most used authentication methods on the web.

1 like

Please or to participate in this conversation.