5

I have a route that requires a CSRF token:

mp.ajax.authorized:
  path: /mp/ajax/authorized
  defaults:
    _controller: 'Drupal\mp\Controller\AjaxController::authorizedCallback'
  requirements:
    _csrf_token: 'TRUE'

I need to generate a URL for this route. As the route has _csrf_token set to TRUE, the route needs to contain a CSRF token.

I've generated a URL from the route using Url:

Url::fromRoute('mp.ajax.authorized', [], ['absolute' => TRUE])->toString()

It generates a route with a token:

http://example.com/mp/ajax/authorized?token=91018d3f93299fe116c5596ea32d9addca063a92

I also tried the url_generator service to generate the URL:

$url_generator = \Drupal::service('url_generator');
$url = $url_generator->generateFromRoute('mp.ajax.authorized', [], ['absolute' => TRUE]);

And it generates the same URL.

However, the token on the URL is incorrect, as the actual CSRF token that is retrieved from CsrfTokenGenerator is:

1qG-Tw4AyQgNmVWtlkp_to7mielSz8ZfQZAkhWqAlsQ

I've done some reading, and it appears that the token given from Url is a placeholder, but I need the actual token, not the placeholder. Does anyone know how to get this?

Update: I've discovered I can retrieve the URL with a token in it by passing token as a parameter to the Url builder:

Url::fromRoute('mp.ajax.authorized', [], ['absolute' => TRUE, 'query' => ['token' => \Drupal::csrfToken()->get()]])->toString()

However, this token fails validation in Drupal\Core\Access\CsrfAccessCheck, so it appears this is not the correct solution.

1
  • Could this be related to drupal.org/project/drupal/issues/2670798? I think it's worth checking, since you're using 'ajax' in the path; that could cause it to face the same problem as ajaxified links (those get 'nojs' converted to 'ajax'). – Jedihe Aug 19 '19 at 17:13
3

Well, I found a solution, however, I'd be amazed if there isn't a better way to do it, and in fact I'm pretty sure it's bypassing the placeholder API, so it's likely the wrong way to do it. But until someone gives me a better solution, it's a working one.

The solution is to generate the token manually, then set it as a GET query on the URL when generating the URL.

When generating the token however, you need the internal path for the route, which is used as a basis for generating the token.

$url = Url::fromRoute('mp.ajax.authorized');
$token = \Drupal::csrfToken()->get($url->getInternalPath());
$url->setOptions(['absolute' => TRUE, 'query' => ['token' => $token]]);
$absolute_url = $url->toString()

Note that I've already found this method doesn't work on routes with optional parameters, so if someone comes across this, you'll have to find another method in such a case.

3
  • Just ran into this issue again - still stuck on how to do it with optional parameters. – Jaypan Apr 9 '17 at 7:40
  • any update on this? also the answer above with optional parameters wont work – user780 Jul 5 '19 at 19:26
  • i get csrf_token URL query argument is invalid even for route without parameters – user780 Jul 6 '19 at 15:15
2

Based on the code of Jaypan but with additional parameter (node id):

routing.yml:

entity.node.publish:
  path: '/node/{node}/toggleStatus'
  defaults:
    _controller: 'Drupal\publishcontent\Controller\PublishContentPublishEntity::toggleEntityStatus'
  requirements:
    _csrf_token: 'TRUE'
    _publish_access_check: 'TRUE'

custom function:

$nid = 100;
$url = Url::fromRoute('entity.node.publish', ['node' => $nid]);
$token = \Drupal::csrfToken()->get($url->getInternalPath());
$url->setOptions(['absolute' => TRUE, 'query' => ['token' => $token]]);
$absolute_url = $url->toString();

Will return something like this:

https://www.exapmle.com/node/100/toggleStatus?token=PF06yJ7PLR052_HxeTWvsuye88d7NAZgwanN3yiswU

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.