omniauth-oauth2
omniauth-oauth2 copied to clipboard
Invalid JWT token type (invalid_credentials: OAuth2::Error)
I am currently using this gem in a couple of Rails projects without issue connecting to an IdentityServer with openid/oauth.
I was previously using a fairly basic handwritten Oauth handler in a modular sinatra project and wanted to migrate to this gem to be consistent.
I transferred over my strategy but when I log in now, I hit my oauth server requesting an id_token code
method, get back a response with a code
. That is then passed to the token endpoint and I get back a response with an id_token
and an access_token
. However when I call access_token.get('connect/userinfo')
to get my user info, the HTTP call seems to be using the id_token
not the access_token
and so I am getting a 401 unauthorised error.
I can't seem to figure out what's going wrong.
my_company.rb
# frozen_string_literal: true
require 'omniauth-oauth2'
require 'jwt'
require 'securerandom'
module OmniAuth
module Strategies
class MyCompany < OmniAuth::Strategies::OAuth2
args %i[client_id client_secret client_scope]
option :name, 'my_company'
option :client_options, {
site: ENV.fetch('OAUTH_SERVER'),
authorize_url: '/connect/authorize',
redirect_uri: "#{ENV['APPLICATION_HOST']}/auth/my_company/callback",
token_url: '/connect/token'
}
option :authorize_params, {
nonce: SecureRandom.hex(24),
scope: ENV.fetch('OAUTH_SCOPE', 'openid profile'),
response_mode: 'form_post',
response_type: 'code id_token'
}
uid { raw_info['sub'] }
info do
{
email: raw_info['email'],
displayname: raw_info['displayname'],
role: raw_info['role']
}
end
extra do
hash = {}
hash[:raw_info] = raw_info unless skip_info?
hash[:id_token] = access_token.token
if !options[:skip_jwt] && !access_token.token.nil?
hash[:id_info] = validated_token(access_token.token)
end
hash
end
def callback_url
options[:redirect_uri] || (full_host + script_name + callback_path)
end
def raw_info
@raw_info ||= access_token.get('connect/userinfo').parsed
end
def authorize_params
super.merge(nonce: new_nonce)
end
alias oauth2_access_token access_token
def access_token
::OAuth2::AccessToken.new(client, oauth2_access_token.token, {
refresh_token: oauth2_access_token.refresh_token,
expires_in: oauth2_access_token.expires_in,
expires_at: oauth2_access_token.expires_at
})
end
private
def new_nonce
session['omniauth.nonce'] = SecureRandom.urlsafe_base64(16)
end
def stored_nonce
session.delete('omniauth.nonce')
end
def verify_options
{ verify_expiration: true,
verify_not_before: true,
verify_iat: true,
verify_iss: true,
'iss' => issuer,
verify_aud: true,
'aud' => client_id }
end
end
end
end
OmniAuth.config.add_camelization 'my_company', 'MyCompany'
Token response
{"id_token":"my_id_token","access_token":"my_access_token","expires_in":3600,"token_type":"Bearer","refresh_token":"my_refresh_token","scope":"openid profile offline_access"}
As such, its not even hitting my callback URL as Puma is catching the error.
Puma caught this error: (OAuth2::Error)
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/oauth2-2.0.6/lib/oauth2/client.rb:139:in `request'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/oauth2-2.0.6/lib/oauth2/access_token.rb:140:in `request'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/oauth2-2.0.6/lib/oauth2/access_token.rb:147:in `get'
/Users/myuser/workprojects/myproject/lib/omni_auth/strategies/my_company.rb:55:in `raw_info'
/Users/myuser/workprojects/myproject/lib/omni_auth/strategies/my_company.rb:42:in `block in <class:MyCompany>'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:109:in `instance_eval'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:109:in `block in compile_stack'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:108:in `each'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:108:in `inject'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:108:in `compile_stack'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:102:in `extra_stack'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:387:in `extra'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:392:in `auth_hash'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:417:in `callback_phase'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-oauth2-1.8.0/lib/omniauth/strategies/oauth2.rb:93:in `callback_phase'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:272:in `callback_call'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:194:in `call!'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:169:in `call'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:202:in `call!'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/strategy.rb:169:in `call'
/Users/myuser/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/omniauth-2.1.0/lib/omniauth/builder.rb:44:in `call'
What version of this gem are you on?
* omniauth-oauth2 (1.8.0)
I think its being caused by this change in the oauth2 gem https://github.com/oauth-xx/oauth2/pull/621/files#diff-b665e6fc2096be34f9c5e92cb0f38eb3a229da37ed4da92487f0a834eb6ae336R53 as per discussion At least reverting the version in my gemfile to 2.0.5 gets passed this error.
Okay, report the issue to them if you wouldn't mind, we'll have to PR here to pin the version below the breaking one
After a lot of testing I found a solution to my issue, but not entirely sure of the cause. I think it was a combination of rack middleware misconfiguring, and double-loading my strategy. At one point I was getting errors about not providing an oauth Client ID from my provider and tracked back the issue.
For some reference tho:
Summary:
I have a modular Sinatra-based app and had previously written a custom OAuth implementation using the Oauth2 gem. When trying to switch to Omniauth, I had AuthenticityToken errors. I was using rack_encrypted_cookie
instead of the standard Rack::Session::Cookie middleware, imported with use Rack::Session::EncryptedCookie
along with custom imports of Rack::Protection::HttpOrigin, and Rack::Cors.
After implementing Omniauth, I had to use the standard enable :sessions
inside my Sinatra configure block, as well as set :session_secret
with a key. Then I later configured Rack::Session::EncryptedCookie.
configure do
# added these lines
enable :sessions
set :session_secret, ENV.fetch('SESSION_SECRET', SecureRandom.hex(32))
set :session_length, 3.days
end
...
use Rack::Session::EncryptedCookie,
key: '_myapp_session',
expire_after: 60 * 60 * 24 * 30, # 30 days in seconds
secure: true,
httponly: Sinatra::Base.environment == :production,
same_site: :none,
secret: ENV.fetch('SESSION_SECRET') { SecureRandom.hex(64) },
key_size: 32,
salt: SecureRandom.hex(32),
signed_salt: SecureRandom.hex(32)
use Rack::Protection::SessionHijacking
use Rack::Protection::RemoteToken
use Rack::Protection, permitted_origins: [
ENV.fetch('ALLOWED_URL')
], except: %i[http_origin remote_token]
use Rack::Protection::HttpOrigin,
allow_if: lambda { |env| env['REQUEST_PATH'].split('/')[1] == 'auth' },
permitted_origins: [
ENV.fetch('ALLOWED_URL')
]
use OmniAuth::Builder do
provider :my_config,
ENV['OAUTH_CLIENT_ID'],
ENV['OAUTH_CLIENT_SECRET'],
origin_param: 'return_url'
end