ruby-on-railsrubydevisedoorkeeper

Why am I receiving 401 Unauthorized errors with my Doorkeeper configuration?


I have a Rails 6.1 app using devise 4.7.1, doorkeeper 5.5.1, and devise-doorkeeper 1.2.0.

I'm trying to run through a (PKCE) OAuth flow, but the final step -- a POST request to /oauth/token -- returns a 401 Unauthorized error with the JSON content {"error": "You need to sign in or sign up before continuing."}.

I'm confused about this, since the /oauth/token endpoint should be accessible to unauthenticated users as far as I understand. What's also weird (but perhaps a red herring) is that if I attempt to run the same POST request with curl, but remove the User-Agent header, it succeeds.

My current suspect is this block of code in initializers/doorkeeper.rb:

  resource_owner_authenticator do
    current_user || warden.authenticate!(scope: :user)
  end

This comes from the Doorkeeper docs. By stepping through the code, I can see that it's the call to warden.authenticate! that returns a 401 error. Doorkeeper's TokensController#create is never called.

Is there any important step I'm missing that allows unauthenticated access to this TokensController#create endpoint?


Solution

  • This problem was caused by our use of the Ahoy analytics library.

    By default, this library tracks all page visits in your Rails app. It tries to get the current user using current_user || current_resource_owner. Because current_user was still nil when POSTing to /oauth/token, getting current_resource_owner ended up calling our Doorkeeper resource_owner_authenticator, which returned the 401 error. The source code for this is here.

    This also explains why things worked as expected when unsetting the User-Agent header: with no user agent (or the user agent of e.g. curl), Ahoy treats the request as coming from a bot, and doesn't attempt to track it (source code here).

    Our solution to this is to tell Ahoy to stop tracking all page views automatically by setting Ahoy.api_only = true in its configuration.