Expose controller method for 2FA application to obtain challenge nonce (CSFR check failed but no error was generated in log files)


I’m trying to implement a 2FA provider and I’m having some troubles.

Before sending the challenge, I need to obtain a challenge nonce from my application. These are the steps I have implemented:

  1. created new ChallengeController with function getChallenge() and annotated this function as @PublicPage - this function creates the nonce and returns an array for example (in order to return JSON response)
  2. registered the route:
return [
	'routes' => [
		['name' => 'challenge#get_challenge', 'url' => '/auth/challenge', 'verb' => 'GET'],
  1. created a provider template with one button (there is also valid form with challenge in order for it to work with provider, but for simplicity assume one button)

  2. in the template, inlude local javascript file challenge.js by using

script('twofactor_webeid', 'challenge');
  1. in the javascript file, let’s add a handler for getting the challenge nonce from my app:
button.addEventListener("click", async () => {
    try {
        var challengeNonce = "";

        var url = OC.generateUrl(

        var request = $.ajax(url, {
            method: "GET",
        $.when(request).done(function(data) {
            challengeNonce = data;
        }).fail(function() {

        console.log("after request");

So, here is the PROBLEM I’m trying to solve:

  • “after request” is actually displayed BEFORE the “error” (this is not the main problem, but still not expected)
  • when I try to look up what was the error, I had to debug the code itself and in OC\AppFramework\App::main found out that the output message is "{"message":"CSRF check failed"}"

How do I solve this? Do I need to disable CSRF check with @NoCSRFRequired annotation? I would rather not do this unless I really have to.

Thanks, Petr.


By further debugging source code I have found I need to add CSRF token into the request:

var request = $.ajax(url, {
            method: "GET",
            headers: {
                'requestToken': OC.requestToken

Let’s see what middlewares are being executed in “beforeController” stage:


In this way the SecurityMiddleware falls through (before, it failed here on CSRF check).

But now the response still fails on TwoFactorMiddleware.

The comment says // Disallow access to any controller if 2FA needs to be checked. In order to pass the Controller needs to be instance of TwoFactorChallengeController, which is located in core folder. I guess I am not supposed to implement this as normal Controller.

So the question is now: can you even call for controllers while doing 2FA authentication?


I have managed to solve this by embedding the challenge nonce into the template with $_ variable and reading it with the JavaScript.