diff --git a/lib/omniauth/strategies/google_oauth2.rb b/lib/omniauth/strategies/google_oauth2.rb index 1f59ca9..c98c488 100644 --- a/lib/omniauth/strategies/google_oauth2.rb +++ b/lib/omniauth/strategies/google_oauth2.rb @@ -69,9 +69,9 @@ def authorize_params extra do hash = {} - hash[:id_token] = access_token['id_token'] - if !options[:skip_jwt] && !access_token['id_token'].nil? - decoded = ::JWT.decode(access_token['id_token'], nil, false).first + hash[:id_token] = access_token.token + if !options[:skip_jwt] && !nil_or_empty(access_token.token) + decoded = ::JWT.decode(access_token.token, nil, false).first # We have to manually verify the claims because the third parameter to # JWT.decode is false since no verification key is provided. @@ -108,6 +108,10 @@ def custom_build_access_token private + def nil_or_empty(obj) + obj.is_a?(String) ? obj.empty? : obj.nil? + end + def callback_url options[:redirect_uri] || (full_host + callback_path) end diff --git a/omniauth-google-oauth2.gemspec b/omniauth-google-oauth2.gemspec index 56c6d87..5c40982 100644 --- a/omniauth-google-oauth2.gemspec +++ b/omniauth-google-oauth2.gemspec @@ -21,9 +21,9 @@ Gem::Specification.new do |gem| gem.required_ruby_version = '>= 2.2' gem.add_runtime_dependency 'jwt', '>= 2.0' - gem.add_runtime_dependency 'oauth2', '~> 1.1' + gem.add_runtime_dependency 'oauth2', '~> 2.0.6' gem.add_runtime_dependency 'omniauth', '~> 2.0' - gem.add_runtime_dependency 'omniauth-oauth2', '~> 1.7.1' + gem.add_runtime_dependency 'omniauth-oauth2', '~> 1.8.0' gem.add_development_dependency 'rake', '~> 12.0' gem.add_development_dependency 'rspec', '~> 3.6' diff --git a/spec/omniauth/strategies/google_oauth2_spec.rb b/spec/omniauth/strategies/google_oauth2_spec.rb index 41b0599..4bf1874 100644 --- a/spec/omniauth/strategies/google_oauth2_spec.rb +++ b/spec/omniauth/strategies/google_oauth2_spec.rb @@ -339,7 +339,7 @@ end end end - let(:access_token) { OAuth2::AccessToken.from_hash(client, {}) } + let(:access_token) { OAuth2::AccessToken.from_hash(client, { 'access_token' => 'a' }) } before { allow(subject).to receive(:access_token).and_return(access_token) } context 'with verified email' do @@ -405,8 +405,6 @@ end end end - let(:access_token) { OAuth2::AccessToken.from_hash(client, {}) } - before { allow(subject).to receive(:access_token).and_return(access_token) } describe 'id_token' do @@ -467,7 +465,10 @@ end end - context 'when the id_token is missing' do + context 'when the access token is empty or nil' do + let(:access_token) { OAuth2::AccessToken.new(client, nil, { 'refresh_token' => 'foo' }) } + before { allow(subject.extra).to receive(:access_token).and_return(access_token) } + it 'should not include id_token' do expect(subject.extra).not_to have_key(:id_token) end @@ -479,6 +480,19 @@ end describe 'raw_info' do + let(:token_info) do + { + 'abc' => 'xyz', + 'exp' => Time.now.to_i + 3600, + 'nbf' => Time.now.to_i - 60, + 'iat' => Time.now.to_i, + 'aud' => 'appid', + 'iss' => 'accounts.google.com' + } + end + let(:id_token) { JWT.encode(token_info, 'secret') } + let(:access_token) { OAuth2::AccessToken.from_hash(client, 'id_token' => id_token) } + context 'when skip_info is true' do before { subject.options[:skip_info] = true } @@ -663,15 +677,22 @@ end it 'should read access_token from hash if this is not an AJAX request with a code parameter' do + client = OAuth2::Client.new('abc', 'def') do |builder| + builder.request :url_encoded + builder.adapter :test do |stub| + stub.get('/oauth2/v3/userinfo') { [200, { 'content-type' => 'application/json' }, '{"sub": "12345"}'] } + end + end + allow(request).to receive(:xhr?).and_return(false) allow(request).to receive(:params).and_return('access_token' => 'valid_access_token') expect(subject).to receive(:verify_token).with('valid_access_token').and_return true - expect(subject).to receive(:client).and_return(:client) + expect(subject).to receive(:client).and_return(client) token = subject.build_access_token expect(token).to be_instance_of(::OAuth2::AccessToken) expect(token.token).to eq('valid_access_token') - expect(token.client).to eq(:client) + expect(token.client).to eq(client) end it 'reads the code from a json request body' do @@ -708,18 +729,24 @@ it 'reads the access token from a json request body' do body = StringIO.new(%({"access_token":"valid_access_token"})) + client = OAuth2::Client.new('abc', 'def') do |builder| + builder.request :url_encoded + builder.adapter :test do |stub| + stub.get('/oauth2/v3/userinfo') { [200, { 'content-type' => 'application/json' }, '{"sub": "12345"}'] } + end + end allow(request).to receive(:xhr?).and_return(false) allow(request).to receive(:content_type).and_return('application/json') allow(request).to receive(:body).and_return(body) - expect(subject).to receive(:client).and_return(:client) + expect(subject).to receive(:client).and_return(client) expect(subject).to receive(:verify_token).with('valid_access_token').and_return true token = subject.build_access_token expect(token).to be_instance_of(::OAuth2::AccessToken) expect(token.token).to eq('valid_access_token') - expect(token.client).to eq(:client) + expect(token.client).to eq(client) end it 'should use callback_url without query_string if this is not an AJAX request' do @@ -795,7 +822,7 @@ end end end - let(:access_token) { OAuth2::AccessToken.from_hash(client, {}) } + let(:access_token) { OAuth2::AccessToken.from_hash(client, { 'access_token' => 'foo' }) } context 'when domain is nil' do let(:client) do