Creating an API via module routing files that includes csrf verification headers I am working on migrating a custom API from D7 to D8 and am doing so by using module routing. I have a routing.yml file that looks like so:
ytp_user.api.login:
  path: &quot;/api/v1/user/login&quot;
  defaults:
    _title: &quot;User API Login&quot;
    _controller: '\Drupal\ytp_user\Controller\UserAPIController::login'
  methods: [POST]
  requirements:
    _csrf_request_header_token: &quot;TRUE&quot;
ytp_user.api.get_current_user:
  path: &quot;/api/v2/user/me&quot;
  defaults:
    _title: &quot;User API Get Current User&quot;
    _controller: '\Drupal\ytp_user\Controller\UserAPIController::getCurrentUser'
  methods: [GET]
  requirements:
    _custom_access: '\Drupal\ytp_user\Controller\UserAPIController::authorize'

In the first route, I am trying to validate a csrf token provided in the headers, via the requirement _csrf_request_header_token: &quot;TRUE&quot;, however, this does absolutely nothing. It doesn't validate or even check that the header exists as the documentation suggests it should.
I've tried replacing this with _csrf_token: 'TRUE' which at least checks that the token exists in the query, but I can't get a valid token!!! If I grab a token from /session/token and then pass it along in the query as ?token=&lt;token-from-session/token&gt; then I just get an error saying the CSRF token is invalid.
Am I missing another step or is the documentation just straight up incorrect? I have been searching for hours and everything suggests that what I have should work, so why is the CSRF token not being validated properly?
The requirement _csrf_request_header_token: &quot;TRUE&quot; works only if user is connected because CsrfRequestHeaderAccessCheck use session informations.
If you look at CsrfRequestHeaderAccessCheck in the access method there is:
 if ($account
      -&gt;isAuthenticated() &amp;&amp; $this-&gt;sessionConfiguration
      -&gt;hasSession($request)) {

To check if the user is connected and it there is a session identifier in the request if it's not it will go directly to:
// Let other access checkers decide if the request is legit.
    return AccessResult::allowed()-&gt;setCacheMaxAge(0);

so it will not check if X-CSRF-Token is in the headers or not even if you set the route requirement _csrf_request_header_token: &quot;TRUE&quot;.
Code
MODULE.routing.yml
MODULE.login:
  path: &quot;/api/v1/user/login&quot;
  defaults:
    _title: &quot;User API Login&quot;
    _controller: '\Drupal\MODULE\Controller\UserAPIController::login'
  methods: 'POST'
  requirements:
    _csrf_request_header_token: 'TRUE'

UserAPIController.php
class UserAPIController extends ControllerBase {
  
  public function login(Request $request) {
    $response['data'] = 'Some test data to return';
    $response['method'] = 'POST';

    return new JsonResponse( $response );
  }

}

Test

With Connected user

with anonymous user