Doorkeeper: Constantly getting "Invalid Credentials: invalid_grant"

Created on 25 Oct 2015  路  15Comments  路  Source: doorkeeper-gem/doorkeeper

I've been trying to get a basic provider/ client setup running for a couple days now. Yet no matter what I do my client app is getting an invalid_grant error.

I've combed through the repo to better understand what would cause and invalid_grant error and it seems it should only be fired if the access_grant has been revoked.

When my client application fails due to the invalid_grant error devise is properly displaying the "invalid credentials error. When I check the db there is an unused access_grant. If grab the access_grants's token and use a ruby console everything works fine.

Example:

Loading development environment (Rails 4.2.4)
[1] pry(main)> callback = "http://localhost:3001/users/auth/doorkeeper/callback"
=> "http://localhost:3001/users/auth/doorkeeper/callback"
[2] pry(main)> app_id = "867e46cc052aafc5c580d328daa475d7024328df71eedfe3d640e197f651f0ed"
=> "867e46cc052aafc5c580d328daa475d7024328df71eedfe3d640e197f651f0ed"
[3] pry(main)> secret = "857c7e1f887d52064703ef19852032729e321643ab4638d302ab33eb0afe1a57"
=> "857c7e1f887d52064703ef19852032729e321643ab4638d302ab33eb0afe1a57"
[4] pry(main)> client = OAuth2::Client.new(app_id, secret, site: "http://localhost:3000")
=> #<OAuth2::Client:0x007f94d01fb0c8
 @id="867e46cc052aafc5c580d328daa475d7024328df71eedfe3d640e197f651f0ed",
 @options={:authorize_url=>"/oauth/authorize", :token_url=>"/oauth/token", :token_method=>:post, :connection_opts=>{}, :connection_build=>nil, :max_redirects=>5, :raise_errors=>true},
 @secret="857c7e1f887d52064703ef19852032729e321643ab4638d302ab33eb0afe1a57",
 @site="http://localhost:3000">
[5] pry(main)> client.auth_code.authorize_url(redirect_uri: callback)
=> "http://localhost:3000/oauth/authorize?client_id=867e46cc052aafc5c580d328daa475d7024328df71eedfe3d640e197f651f0ed&redirect_uri=http%3A%2F%2Flocalhost%3A3001%2Fusers%2Fauth%2Fdoorkeeper%2Fcallback&response_type=code"

The resulting url will fail to authenticate and return {"error":"invalid_grant","error_description":"The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."}

So I go to the database and grab the unused access grant token and continue. _NOTE: At this point and time the access grant token revoked_at is still NULL_

[6] pry(main)> access_grant_token = "fe0d66bb2d5445f19337ac15f6973a6042b45055bc5973faa9d9820d95f49464"
=> "fe0d66bb2d5445f19337ac15f6973a6042b45055bc5973faa9d9820d95f49464"
[7] pry(main)> access = client.auth_code.get_token(access_grant_token, redirect_uri: callback)
=> #<OAuth2::AccessToken:0x007f94cf7482e8
 @client=
  #<OAuth2::Client:0x007f94d01fb0c8
   @auth_code=#<OAuth2::Strategy::AuthCode:0x007f94cfba3338 @client=#<OAuth2::Client:0x007f94d01fb0c8 ...>>,
   @connection=
    #<Faraday::Connection:0x007f94cfba3158
     @builder=
      #<Faraday::RackBuilder:0x007f94cfba2dc0
       @app=
        #<Faraday::Request::UrlEncoded:0x007f94d0960228
         @app=
          #<Faraday::Adapter::NetHttp:0x007f94d09602a0 @app=#<Proc:0x007f94d0960390@/Users/jonathan/.rvm/gems/ruby-2.2.2/gems/faraday-0.9.2/lib/faraday/rack_builder.rb:152 (lambda)>>>,
       @handlers=[Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]>,
     @default_parallel_manager=nil,
     @headers={"User-Agent"=>"Faraday v0.9.2"},
     @options=#<struct Faraday::RequestOptions params_encoder=nil, proxy=nil, bind=nil, timeout=nil, open_timeout=nil, boundary=nil, oauth=nil>,
     @parallel_manager=nil,
     @params={},
     @proxy=nil,
     @ssl=
      #<struct Faraday::SSLOptions
       verify=nil,
       ca_file=nil,
       ca_path=nil,
       verify_mode=nil,
       cert_store=nil,
       client_cert=nil,
       client_key=nil,
       certificate=nil,
       private_key=nil,
       verify_depth=nil,
       version=nil>,
     @url_prefix=#<URI::HTTP http://localhost:3000/>>,
   @id="867e46cc052aafc5c580d328daa475d7024328df71eedfe3d640e197f651f0ed",
   @options={:authorize_url=>"/oauth/authorize", :token_url=>"/oauth/token", :token_method=>:post, :connection_opts=>{}, :connection_build=>nil, :max_redirects=>5, :raise_errors=>true},
   @secret="857c7e1f887d52064703ef19852032729e321643ab4638d302ab33eb0afe1a57",
   @site="http://localhost:3000">,
 @expires_at=1445794685,
 @expires_in=7200,
 @options={:mode=>:header, :header_format=>"Bearer %s", :param_name=>"access_token"},
 @params={"token_type"=>"bearer", "scope"=>"user", "created_at"=>1445787485},
 @refresh_token=nil,
 @token="00c560260d550167c871ecf89567d9a07e826e896072ae1a209960e0fe182b93">
[8] pry(main)> access.get("/me").parsed
=> {"id"=>1, "email"=>"[email protected]", "created_at"=>"2015-10-23T04:20:30.871Z", "updated_at"=>"2015-10-25T04:19:20.137Z"}

What am I doing wrong here? Why does my client constantly return and invalid grant_error.

Most helpful comment

So after days of frustration It turns out this was all because of a change in the omniauth-oauth2 repo

This issue reports the problem. The fix is to just roll back to 1.3.1 or override the callback_url method in your strategy.

All 15 comments

So after vendoring the gem locally and putting adding ruby puts entries everywhere to track the sequence of events, it turns out the authorization is failing because of this rediret_url validation

So I modified the to see exactly what was going on like so:

# my modified method
def validate_redirect_uri
  puts "client redirect url is: #{redirect_uri}"
  puts "grant redirect uri is: #{grant.redirect_uri}"
  grant.redirect_uri == redirect_uri
end

# which outputs
client redirect uri is: http://localhost:3001/users/auth/doorkeeper/callback?code=dcf15a0e87ae6b168b4ae3ec5d840fb4e2acb62146b8b06a9a2005d3ce2a6467&state=cb154ebce1b3f7f42547d867c25ec0650579a3b7be3adc01
grant redirect uri is: redirect_uri: http://localhost:3001/users/auth/doorkeeper/callback

So the urls are different, but of course they are the client redirect_uri won't have the code and state since those are dynamic attributes.. right?

How did this ever work?

My problem seems similar to this issue in the Omniauth-Facebook repo. https://github.com/mkdynamic/omniauth-facebook/issues/220

It looks as if the strategy in the client-app is what defines the callback url. So maybe there is something wrong with mine?

# lib/omniauth/strategies/doorkeeper.rb
module OmniAuth
  module Strategies
    class Doorkeeper < OmniAuth::Strategies::OAuth2
      option :name, :doorkeeper

      option :client_options, {
        site: 'http://localhost:3000',
        authorize_path: 'oauth/authorize'
      }

      uid do
        raw_info['id']
      end

      info do
        {
          :email => raw_info['email']
        }
      end

      def raw_info
        @raw_info ||= access_token.get('/me').parsed
      end
    end
  end
end

Could this issue somehow be a problem with devise?

So after days of frustration It turns out this was all because of a change in the omniauth-oauth2 repo

This issue reports the problem. The fix is to just roll back to 1.3.1 or override the callback_url method in your strategy.

@jonathansimmons Thanks so much for posting your results here! This was driving me nuts for almost an hour..

@rjayroach :+1:

Really appreciate you posting your debugging here! @jonathansimmons

@jonathansimmons Thanks, really help me! :+1:

@jonathansimmons Thanks!

@jonathansimmons thanks a lot :frog:

Thanks for your notes on this, this was really helpful.

It makes me sad that this still hasn't been fixed in omniauth-oauth2.

Thank you so much. I spent almost two days checking all parts of my doorkeeper provider and client to see what was wrong. Why did all of it suddenly stop working.

Thank you so much. I reverted the gem version and it all started working again. :)

Thanks for notes of this, I solved the same problem @jonathansimmons

Thank you so much for working through this bug here. I got to the omniauth downgrade as a result of it. Thanks.

Still dont working for me =/
even after the downgrade. Any other solution, folks?

@igorrfc I made a local copy of the Doorkeeper gem, version 3.1.0. In Doorkeeper::Server I added the second line below to overwrite the redirect_uri.

def parameters
  context.request.parameters['redirect_uri'] = context.request.parameters['redirect_uri'].split("?")[0]
  context.request.parameters
end

This made the problem go away because (as @jonathansimmons points out), the redirect_uri sometimes had extra parameters at the end such as this:

?code=dcf15a0e87ae6b168b4ae3ec5d840fb4e2acb62146b8b06a9a2005d3ce2a6467&state=cb154ebce1b3f7f42547d867c25ec0650579a3b7be3adc01

I ended not doing this in production and rolling back to a previous version of the gem as suggested above, because it's not clear to me what those extra parameters are used for.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

f3ndot picture f3ndot  路  6Comments

eebasadre picture eebasadre  路  4Comments

yemartin picture yemartin  路  6Comments

krtschmr picture krtschmr  路  6Comments

rafaelsales picture rafaelsales  路  4Comments