Giter Club home page Giter Club logo

signet's Introduction

Signet

Homepage
https://github.com/googleapis/signet/
Author
Bob Aman
Copyright
Copyright © 2010 Google, Inc.
License
Apache 2.0

Gem Version

Description

Signet is an OAuth 1.0 / OAuth 2.0 implementation.

Reference

  • {Signet::OAuth1}
  • {Signet::OAuth1::Client}
  • {Signet::OAuth1::Credential}
  • {Signet::OAuth1::Server}
  • {Signet::OAuth2}
  • {Signet::OAuth2::Client}

Example Usage for Google

Initialize the client

require 'signet/oauth_2/client'
client = Signet::OAuth2::Client.new(
  :authorization_uri => 'https://accounts.google.com/o/oauth2/auth',
  :token_credential_uri =>  'https://oauth2.googleapis.com/token',
  :client_id => "#{YOUR_CLIENT_ID}.apps.googleusercontent.com",
  :client_secret => YOUR_CLIENT_SECRET,
  :scope => 'email profile',
  :redirect_uri => 'https://example.client.com/oauth'
)

Request an authorization code

redirect_to(client.authorization_uri)

Obtain an access token

client.code = request.query['code']
client.fetch_access_token!

Install

gem install signet

Be sure https://rubygems.org is in your gem sources.

Supported Ruby Versions

This library is supported on Ruby 2.7+.

Google provides official support for Ruby versions that are actively supported by Ruby Core—that is, Ruby versions that are either in normal maintenance or in security maintenance, and not end of life. Older versions of Ruby may still work, but are unsupported and not recommended. See https://www.ruby-lang.org/en/downloads/branches/ for details about the Ruby support schedule.

signet's People

Contributors

analogj avatar bajajneha27 avatar blowmage avatar cc avatar dazuma avatar foxtacles avatar jashenson avatar justinbeckwith avatar kossnocorp avatar mechazoidal avatar momer avatar nevir avatar petergoldstein avatar pkern avatar release-please[bot] avatar renovate-bot avatar samuelreh avatar sanemat avatar seanstory avatar senid231 avatar sethladd avatar seuros avatar sporkmonger avatar sqrrrl avatar swifthand avatar theroyaltnetennba avatar viraptor avatar ylecuyer avatar yoshi-code-bot avatar ysksn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

signet's Issues

undefined method `bytesize` for Hash

This works with an older (pre-0.7.0) version of signet. It seems like this possibly broke when hurley support was added.

NoMethodError: undefined method `bytesize' for #<Hash:0x007fd8c5dd31a0>
/project/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:82:in `perform_request'
/project/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:40:in `block in call'
/project/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:87:in `with_net_http_connection'
/project/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:32:in `call'
/project/gems/faraday-0.9.2/lib/faraday/response.rb:8:in `call'
/project/gems/faraday-0.9.2/lib/faraday/rack_builder.rb:139:in `build_response'
/project/gems/faraday-0.9.2/lib/faraday/connection.rb:377:in `run_request'
/project/gems/faraday-0.9.2/lib/faraday/connection.rb:177:in `post'
/project/gems/signet-0.7.1/lib/signet/oauth_2/client.rb:960:in `fetch_access_token'
/project/gems/signet-0.7.1/lib/signet/oauth_2/client.rb:997:in `fetch_access_token!'
/project/lib/tasks/fetch.rake:32:in `get_access_token'
/project/lib/tasks/fetch.rake:148:in `block (2 levels) in <top (required)>'

My code in fetch.rake looks like this:

    client = Google::APIClient.new
    auth = client.authorization
    auth.client_id = CLIENT_ID
    auth.client_secret = CLIENT_SECRET
    auth.scope = 'https://www.googleapis.com/auth/drive https://spreadsheets.google.com/feeds'
    auth.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"

    print("1. Open this page:\n%s\n\n" % auth.authorization_uri)
    print("2. Enter the authorization code shown in the page: ")
    auth.code = $stdin.gets.chomp
    auth.fetch_access_token!(:connection => client.connection)
    access_token = auth.access_token

    return access_token

Replace faraday with HTTPI

@sqrrrl
Recently i was updating an application that has the the adsword gem, and i noticed that this gem use a different http wrapper than the rest of google's gem.

Do you think it make sense to replace Faraday with HTTPI ? i could do it in the coming days.

Also travis is not configured for this project.

IAP: Support for additional claim target_audience

Hello,

I'm trying to authenticate with a service account with the following guide:
https://cloud.google.com/iap/docs/authentication-howto#authenticating_from_a_service_account
image

But when I try to fetch_access_token, I get "Invalid ID token audience":

2.5.1 :001 > client = Google::Auth::ServiceAccountCredentials.make_creds(json_key_io: Rails.root.join('config', 'gcp_credentials.json'))
 => #<Google::Auth::ServiceAccountCredentials:0x00007fd589fdf000 @project_id="PROJECT", @authorization_uri=nil, @token_credential_uri=#<Addressable::URI:0x3feac4fef6ac URI:https://www.googleapis.com/oauth2/v4/token>, @client_id=nil, @client_secret=nil, @code=nil, @expires_at=nil, @issued_at=nil, @issuer="[email protected]", @password=nil, @principal=nil, @redirect_uri=nil, @scope=nil, @state=nil, @username=nil, @access_type=:offline, @expiry=60, @audience="https://www.googleapis.com/oauth2/v4/token", @signing_key=#<OpenSSL::PKey::RSA:0x00007fd589fdf050>, @extension_parameters={}, @additional_parameters={}, @connection_info=nil>
2.5.1 :002 > client.fetch_access_token
Traceback (most recent call last):
        1: from (irb):2
Signet::AuthorizationError (Authorization failed.  Server message:)
{"error":"invalid_scope","error_description":"Invalid OAuth scope or ID token audience provided."}
2.5.1 :003 >

When I set aud to the client id, I get "Invalid JWT: Failed audience check":

2.5.1 :001 > client = Google::Auth::ServiceAccountCredentials.make_creds(json_key_io: Rails.root.join('config', 'gcp_credentials.json'))
 => #<Google::Auth::ServiceAccountCredentials:0x00007fc12b0a3018 @project_id="PROJECT", @authorization_uri=nil, @token_credential_uri=#<Addressable::URI:0x3fe095851690 URI:https://www.googleapis.com/oauth2/v4/token>, @client_id=nil, @client_secret=nil, @code=nil, @expires_at=nil, @issued_at=nil, @issuer="[email protected]", @password=nil, @principal=nil, @redirect_uri=nil, @scope=nil, @state=nil, @username=nil, @access_type=:offline, @expiry=60, @audience="https://www.googleapis.com/oauth2/v4/token", @signing_key=#<OpenSSL::PKey::RSA:0x00007fc12b0a3090>, @extension_parameters={}, @additional_parameters={}, @connection_info=nil>
2.5.1 :002 > client.audience = 'CLIENT-ID.apps.googleusercontent.com'
 => "CLIENT-ID.apps.googleusercontent.com"
2.5.1 :003 > client.fetch_access_token
Traceback (most recent call last):
        1: from (irb):3
Signet::AuthorizationError (Authorization failed.  Server message:)
{"error":"invalid_grant","error_description":"Invalid JWT: Failed audience check."}

With a target_audience monkey patch to #to_jwt, it is possible to retrieve an id_token:

      def to_jwt options = {}
        options = deep_hash_normalize options

        now = Time.new
        skew = options[:skew] || 60
        assertion = {
          "iss" => issuer,
          "aud" => audience,
          "exp" => (now + expiry).to_i,
          "iat" => (now - skew).to_i
        }
        assertion["scope"] = scope.join " " unless scope.nil?
        assertion["prn"] = person unless person.nil?
        assertion["sub"] = sub unless sub.nil?
+       assertion["target_audience"] = options[:target_audience] unless options[:target_audience].nil?
        JWT.encode assertion, signing_key, signing_algorithm
      end
2.5.1 :001 > client = Google::Auth::ServiceAccountCredentials.make_creds(json_key_io: Rails.root.join('config', 'gcp_credentials.json'))
 => #<Google::Auth::ServiceAccountCredentials:0x00007ffad3b992d8 @project_id="PROJECT", @authorization_uri=nil, @token_credential_uri=#<Addressable::URI:0x3ffd69dcc818 URI:https://www.googleapis.com/oauth2/v4/token>, @client_id=nil, @client_secret=nil, @code=nil, @expires_at=nil, @issued_at=nil, @issuer="[email protected]", @password=nil, @principal=nil, @redirect_uri=nil, @scope=nil, @state=nil, @username=nil, @access_type=:offline, @expiry=60, @audience="https://www.googleapis.com/oauth2/v4/token", @signing_key=#<OpenSSL::PKey::RSA:0x00007ffad3b99328>, @extension_parameters={}, @additional_parameters={}, @connection_info=nil>
2.5.1 :002 > res = client.fetch_access_token(target_audience: 'CLIENT-ID.apps.googleusercontent.com')
 => {"id_token"=>"[AN_ID_TOKEN]"}

Is it possible to pass the target_audience options in another way? Or am I using the gem wrong?

Regards,
Stefan

Breaking change introduced

I updated my signet from 0.7 to 0.9 and started getting problems to connect to google adwords with error saying AuthenticationError.OAUTH_TOKEN_INVALID

When reviewing the request, i saw the access token that is used is the original expired one and it's not refreshed.

The issue occurs since, we initialized the signet with the following options

  1. :access_token
  2. :refresh_token
    3,. :issued_at => historical time
  3. :expires_in => 3600,
  4. :id_token

The old signet code would consider this as expired, while the new one identified this as valid and not expired.

I think the fix should be, to reassign expires_at when setting issued_at / other fields and the expires_at was not preset.

uninitialized constant Signet::OAuth2::Client::DateTime (NameError)

I used googleauth gem to use the Calendar API.
When I ran the command line sample code, I got a title error.
I got better at the first authentication, but an error came after the second.
I think that DateTime, which normalizes "expiration_time_millis" in token, may be the cause.
In fact, commenting out the part of DateTime no longer causes an error. (lines 1189 and 1190 of client.rb)

I think that it is necessary to require date in the signet.

8: from bug_test.rb:13:in `<main>'
7: from /Users/touhouota/google_calendar/vendor/bundle/ruby/2.5.0/gems/googleauth-0.6.6/lib/googleauth/user_authorizer.rb:136:in `get_credentials'
6: from /Users/touhouota/google_calendar/vendor/bundle/ruby/2.5.0/gems/googleauth-0.6.6/lib/googleauth/user_authorizer.rb:136:in `new'
5: from /Users/touhouota/google_calendar/vendor/bundle/ruby/2.5.0/gems/googleauth-0.6.6/lib/googleauth/user_refresh.rb:89:in `initialize'
4: from /Users/touhouota/google_calendar/vendor/bundle/ruby/2.5.0/gems/signet-0.9.0/lib/signet/oauth_2/client.rb:107:in `initialize'
3: from /Users/touhouota/google_calendar/vendor/bundle/ruby/2.5.0/gems/signet-0.9.0/lib/signet/oauth_2/client.rb:192:in `update!'
2: from /Users/touhouota/google_calendar/vendor/bundle/ruby/2.5.0/gems/signet-0.9.0/lib/signet/oauth_2/client.rb:230:in `update_token!'
1: from /Users/touhouota/google_calendar/vendor/bundle/ruby/2.5.0/gems/signet-0.9.0/lib/signet/oauth_2/client.rb:786:in `expires_at='

/Users/touhouota/google_calendar/vendor/bundle/ruby/2.5.0/gems/signet-0.9.0/lib/signet/oauth_2/client.rb:1189:in `normalize_timestamp': uninitialized constant Signet::OAuth2::Client::DateTime (NameError)

Use case vs standard oauth2 gem

I apologize if this is covered elsewhere already but couldn't really find what I'm looking for. I'm doing some up-front research for a project trying to decide which dependencies to take on. The oauth2 gem from intridea has ben around for quite some time and is used by several other ruby projects when working with OAuth 2 APIs.

Why would a project use signet instead of the standard oauth2 gem?

After upgraded to 0.7.3 i started to get "Missing token endpoint URI" Error message

I guys, after updating the gem to 0.7.3, the auth stop working fine.

What i'm doing is this:

    client = Signet::OAuth2::Client.new(access_token: token)

    service = Google::Apis::PlusV1::PlusService.new

    service.authorization = client

    profile = service.get_person('me', fields: 'displayName,emails/value,image,gender,id')`

when the request is done it throughs "Missing token endpoint URI" this is working with the previous version.

`normalize_timestamp': Invalid time value 1453393549

hello guys i just install the google api credentials under ruby and when i try to run my code or try run again ruby quickstart.rb i got the following error:

/home/user/.rvm/gems/ruby-2.1.6/gems/signet-0.7.2/lib/signet/oauth_2/client.rb:1183:in `normalize_timestamp': Invalid time value 1453393549 (RuntimeError)

looks like that value is no a valid date but i dont know that to do that value come from clien_secrent.json generated in the google console

Tests fail with newest dependencies.

This gem is getting stale - its Gemfile describes dependencies too loosely for tests to pass.

$ rm Gemfile.lock ; bundle
Fetching gem metadata from https://rubygems.org/.........
Fetching gem metadata from https://rubygems.org/..
Resolving dependencies...
Using rake (10.3.2)
Using addressable (2.3.6)
Using diff-lcs (1.2.5)
Using extlib (0.9.16)
Using multipart-post (2.0.0)
Using faraday (0.9.0)
Using json (1.7.7)
Using jwt (1.0.0)
Using kramdown (1.4.0)
Using launchy (2.4.2)
Using multi_json (1.10.1)
Using rspec-support (3.0.3)
Using rspec-core (3.0.3)
Using rspec-expectations (3.0.3)
Using rspec-mocks (3.0.3)
Using rspec (3.0.0)
Using yard (0.8.7.4)
Using bundler (1.3.5)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
~/Projects/signet on master
$ bundle exec rspec
..................................................................................................................................F.......F..FF........FFF..........FF.................................................................................................................................FFFF..F.............................FFF..................................

Finished in 2.4 seconds (files took 0.36041 seconds to load)
368 examples, 17 failures

About half the failing tests came from old rspec syntax, and the rest from an updated jwt.

$ git diff   # after some edits..
diff --git a/Gemfile b/Gemfile
...
...
-gem 'jwt', '>= 0.1.5'
+gem 'jwt', '0.1.5'
...
...
-  gem 'rspec', '>= 2.11.0'
+  gem 'rspec', '2.11.0'

~/Projects/signet on master*
$ bundle exec rspec
................................................................................................................................................................................................................................................................................................................................................................................

Finished in 2.77 seconds
368 examples, 0 failures

I don't think locking down to old versions of these gems is the way to go.

I'd gladly put in the time to update this gem, but I'm hesitant when there are a handful of sitting PRs that are months old, and issues over a year old.

@sporkmonger, @sqrrrl, would you accept a PR with bumped gem versions and updated tests/code?

Enable master branch protection

      This repository does not seem to have master branch
      protection enabled, at least in the way I'm expecting.
      I was hoping for:

      - master branch protection
      - requiring at least one code reviewer
      - requiring at least two status checks
      - enforcing rules for admins

      Please turn it on!

Signet library is failing to pass build due to changes to the JWT library

In ref to PR: #48

The Signet library is failing to pass build due to changes to the JWT library. The following PR: jwt/ruby-jwt#45 implements claim expiration enforcement and since the Signet test specs include an expired token the build fails with an exception ...

Failure/Error: expect(@client.decoded_id_token).to eq ({
     JWT::ExpiredSignature:
       Signature has expired
     # ./lib/signet/oauth_2/client.rb:694:in `decoded_id_token'

Default value of approval_prompt conflicts with optional prompt parameter

Google OAuth2 now accepts a prompt parameter that is mutually exclusive with approval_prompt. See http://stackoverflow.com/questions/14384354/force-google-account-chooser.

If approval_prompt is not included in the options hash passed to Signet::OAuth2::Client#authorization_uri, it is set to :force by default. If prompt has also been set, inclusion of approval_prompt results in an invalid auth URL.

Suggest imposing a default of prompt=consent if neither approval_prompt nor prompt is provided.

Client SSL Certificates and Faraday Constructor

Hey @sporkmonger
I'm attempting to integrate with the xero.com API and they use client ssl certificates to validate users. I was wondering if there was a way to directly access the Faraday:Request object, or pass options to it.
It seems like I need to set something like this: (from http://www.sitepoint.com/inter-service-communication-using-client-certificate-authentication/)

 :ssl => {
    :verify => false,
    :client_cert => OpenSSL::X509::Certificate.new(File.read('/path/to/xero.certificate.pem')),
    :client_key => OpenSSL::PKey::RSA.new(File.read('/path/to/xero.client.key')),
 }

as an option in the Faraday::Connection.new constructor.

Fragile test?

I test signet 10times, then 7 passes, 3 fails:

for i in `seq 1 1 10 `; do bundle exec rake spec:normal --trace; done

Fail is same test, 1) Signet::OAuth2::Client configured for Google userinfo API should allow the expires_at time to be updated , and I don't solve this yet. Any suggestion?

Failure and stacktrace:

Failures: 

1) Signet::OAuth2::Client configured for Google userinfo API should allow the expires_at time to be updated
     Failure/Error: @client.should be_expired
       expected expired? to return true, got false
     # ./spec/signet/oauth_2/client_spec.rb:362:in `block (2 levels) in <top (required)>'
Finished in 2.49 seconds
346 examples, 1 failure
Failed examples:
rspec ./spec/signet/oauth_2/client_spec.rb:355 # Signet::OAuth2::Client configured for Google userinfo API should allow the expires_at time to be updated
rake aborted!
/Users/sane/.rbenv/versions/2.0.0-p0/bin/ruby -S rspec spec/signet/oauth_1/client_spec.rb spec/signet/oauth_1/credential_spec.rb spec/signet/oauth_1/server_spec.rb spec/signet/oauth_1/services/google_spec.rb spec/signet/oauth_1/signature_methods/hmac_sha1_spec.rb spec/signet/oauth_1_spec.rb spec/signet/oauth_2/client_spec.rb spec/signet/oauth_2_spec.rb spec/signet_spec.rb --color --format documentation failed/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rspec-core-2.12.2/lib/rspec/core/rake_task.rb:156:in `run_task'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rspec-core-2.12.2/lib/rspec/core/rake_task.rb:124:in `block (2 levels) in initialize'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/file_utils_ext.rb:61:in `verbose' 
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rspec-core-2.12.2/lib/rspec/core/rake_task.rb:122:in `block in initialize'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:228:in `call'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:228:in `block in execute' 
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:223:in `each'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:223:in `execute'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:166:in `block in invoke_with_call_chain'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:159:in `invoke_with_call_chain'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/task.rb:152:in `invoke'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:143:in `invoke_task'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:101:in `block (2 levels) in top_level'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:101:in `each'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:101:in `block in top_level'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:110:in `run_with_threads'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:95:in `top_level'  
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:73:in `block in run'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:160:in `standard_exception_handling'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/lib/rake/application.rb:70:in `run'
/Users/sane/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rake-10.0.3/bin/rake:33:in `<top (required)>'
/Users/sane/.rbenv/versions/2.0.0-p0/bin/rake:23:in `load'
/Users/sane/.rbenv/versions/2.0.0-p0/bin/rake:23:in `<main>'
Tasks: TOP => spec:normal

Refresh access token using long lived refresh token

I am trying to figure out how to update the access token using the refresh token which I got during the initial authorization. I tried multiple ways to do this but I always receive:

Signet::AuthorizationError: Authorization failed.  Server message:
{
  "error": "invalid_grant",
  "error_description": "Bad Request"
}

The main method to which I have been adding changes to is:

def refresh_google_calendar
  @service = Google::Apis::CalendarV3::CalendarService.new
  @service.authorization = google_secret

  # Attempts to refresh access token if already expired.
  @service.authorization.refresh! if @user.google_expires_at < Time.current
end

def google_secret
  Signet::OAuth2::Client.new(refresh_options)
end

def refresh_options
    { token_credential_uri: 'https://oauth2.googleapis.com/token',
      access_token: @user.google_token,
      expires_at: @user.google_expires_at,
      refresh_token: @user.google_refresh_token,
      client_id: ENV.fetch('GOOGLE_CLIENT_ID'),
      client_secret: ENV.fetch('GOOGLE_CLIENT_SECRET') }
  end

I tried another variation of the refresh_options as:

def refresh_options
  { refresh_token: @user.google_refresh_token,
      token_credential_uri: 'https://oauth2.googleapis.com/token',
      client_id: ENV.fetch('GOOGLE_CLIENT_ID'),
      client_secret: ENV.fetch('GOOGLE_CLIENT_SECRET') }
end

Also attempted using Google::Auth::UserRefreshCredentials as explained here but I still get the invalid_grant error.

I do know that my Client ID and Client Secret work because I use the same for the initial authorization request.

I would appreciate any help in figuring this out.

@sqrrrl @blowmage

Facebook OAuth Token as HTML instead of JSON fails

Hello,

I've started to use this Library for our Website project. I've already implemented Google Sign-In to our website which works fine. Now I am trying to integrate the Facebook Login too.

But it seems like Facebook don't respond with JSON after creating the Token. That's mostly what my code looks like:

client = Signet::OAuth2::Client.new(
   :authorization_uri => "https://www.facebook.com/#{self::FACEBOOK_GRAPH_VERSION}/dialog/oauth",
   :token_credential_uri =>  "https://graph.facebook.com/#{self::FACEBOOK_GRAPH_VERSION}/oauth/access_token",
   :client_id => 'XXXXXX',
   :client_secret => 'XXXXXX',
   :scope => 'public_profile email',
   :redirect_uri => "#{API_URL}/oauth/v2/third_party/facebook",
   :state => state
)

client.code = params['code']
client.fetch_access_token! # here will be the error thrown
#<ArgumentError: Invalid content type 'text/plain; charset=UTF-8'>
["/usr/local/rvm/gems/ruby-2.3.3/gems/signet-0.7.3/lib/signet/oauth_2.rb:86:in `parse_credentials'",
 "/usr/local/rvm/gems/ruby-2.3.3/gems/signet-0.7.3/lib/signet/oauth_2/client.rb:975:in `fetch_access_token'",
 "/usr/local/rvm/gems/ruby-2.3.3/gems/signet-0.7.3/lib/signet/oauth_2/client.rb:998:in `fetch_access_token!'",
 "..............third_party_controller.rb:62:in `facebook'",

Is there any chance to set the Content-Type or Accept Header for this request? I think that should be the issues, don't know why Facebook responds by default with HTML.

Regards,
Alex

Comparing signatures with `==` is bad security practice due to byte-wise short-circuit being a potential source of subtle timing attack.

If you compare user supplied signature to calculated signature using ==, Ruby will helpfully abort the comparison early if you have a mismatch between the signatures. There's at least a few places in Signet that do this, with the OAuth 1 server code that @mechazoidal contributed being the most exposed to the issue. In a nutshell, you probably want a non-short-circuited equality check for that.

Realistically, I doubt anyone could actually build an exploit for this in large part due to Ruby's wildly varying runtimes for web requests, but I don't really want to find out the hard way, so this should probably get eliminated as even a possibility, just in case.

OAuth1 Signature Invalid with array/duplicate query params

The OAuth1 client produces Signature Invalid errors when used with APIs that require duplicate query params in the URL. After much debugging, this seems. to be because Oauth1.generate_base_string makes a call to uri.query_values.to_a rather than uri.query_values(Array), causing the query parameters to be written to a Hash (deduplicating keys) before added to the base string.

Environment details

  • OS: MacOS 10.14.6
  • Ruby version: jruby 9.2.9.0 (2.5.7) 2019-10-30 458ad3e OpenJDK 64-Bit Server VM 25.265-b01 on 1.8.0_265-b01 +jit [darwin-x86_64]
  • Gem name and version: gem 'signet', '0.12'

Steps to reproduce

  1. run Signet::OAuth1.generate_base_string 'GET', 'https://example.com/?foo=3&foo=2&foo=1', {:oauth_token => 'token'}
  2. output is: "GET&https%3A%2F%2Fexample.com%2F&foo%3D1%26oauth_token%3Dtoken"
  3. notice that only foo=1 is included, but not foo=2 or foo=3. The duplicate keys are being dropped.

Code example

Signet::OAuth1.generate_base_string 'GET', 'https://example.com/?foo=3&foo=2&foo=1', {:oauth_token => 'token'}
uri.query_values.to_a
=> [["foo", "1"]]
uri.query_values(Array)
=> [["foo", "3"], ["foo", "2"], ["foo", "1"]]

Update id_token to match spec

Minor fix required to the id_token validation. The audience (aud) may now be either a string or array, validation will need to be updated.

Also worth improving the validation in general to check issued & expiration times.

Support `jwt` versions < 3

The jwt gem is now on version 2.1.0 and has one feature that applies particularly to users using this gem for oauth authentication against Google:

When calling JWT.decode, in version 2+, the custom_options hash now accepts an array to validate against multiple possible iss candidates. (The Google oauth specifications state that the iss may be set to either https://accounts.google.com or accounts.google.com.)

Mac clients cannot discover CA cert path

Invoking SSL results in:

`connect': SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (Faraday::SSLError)

...
from [....] ruby-2.1.1/gems/signet-0.6.0/lib/signet/oauth_2/client.rb:938:in `fetch_access_token'

...

standby for PR --v

URL encoding error with Addressable 2.3.3

Trying to run the Google Drive quickstart (https://developers.google.com/drive/quickstart-ruby) on a pristine 1.8.7 install. Addressable 2.3.3 is included.

The app gives an invalid_request error in the call to fetch_access_token!. Further debugging indicates the error is a malformed redirect_uri that is encoded twice:

...redirect_uri=urn%253Aietf%253Awg%253Aoauth%253A2.0%253Aoob...

Downgrading addressable to 2.3.2 works.

Unable to fetch credentials using quickstart.rb

I'm using the exact same quickstart file from the docs but the response I get back after successfully pasting the code is:

Authorization failed.  Server message: (Signet::AuthorizationError)
{
  "error": "invalid_client",
  "error_description": "Unauthorized"
}

credentials.json

{
  "web": {
    "client_id": "client_id",
    "project_id": "redacted-id",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_secret": "client_secret",
    "redirect_uris": [
      "http://localhost:3000/auth/callback"
    ]
  }
}

quickstart.rb

require "google/apis/people_v1"
require "googleauth"
require "googleauth/stores/file_token_store"
require "fileutils"

OOB_URI = "http://localhost:3000/auth/callback".freeze
APPLICATION_NAME = "Google People API Ruby Quickstart".freeze
CREDENTIALS_PATH = "credentials.json".freeze
# The file token.yaml stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
TOKEN_PATH = "token.yaml".freeze
SCOPE = Google::Apis::PeopleV1::AUTH_CONTACTS_READONLY

##
# Ensure valid credentials, either by restoring from the saved credentials
# files or intitiating an OAuth2 authorization. If authorization is required,
# the user's default browser will be launched to approve the request.
#
# @return [Google::Auth::UserRefreshCredentials] OAuth2 credentials
def authorize
  client_id = Google::Auth::ClientId.from_file CREDENTIALS_PATH
  token_store = Google::Auth::Stores::FileTokenStore.new file: TOKEN_PATH
  authorizer = Google::Auth::UserAuthorizer.new client_id, SCOPE, token_store
  user_id = "default"
  credentials = authorizer.get_credentials user_id
  if credentials.nil?
    url = authorizer.get_authorization_url base_url: OOB_URI
    puts "Open the following URL in the browser and enter the " \
         "resulting code after authorization:\n" + url
    code = gets
    credentials = authorizer.get_and_store_credentials_from_code(
      user_id: user_id, code: code, base_url: OOB_URI
    )
  end
  credentials
end

# Initialize the API
service = Google::Apis::PeopleV1::PeopleServiceService.new
service.client_options.application_name = APPLICATION_NAME
service.authorization = authorize

# Fetch the next 10 events for the user
response = service.list_person_connections(
  "people/me",
  page_size:     10,
  person_fields: "names,emailAddresses"
)

puts "Connection names:"
puts "No connections found" if response.connections.empty?
response.connections.each do |person|
  names = person.names
  if names.nil?
    puts "No names found for connection"
  else
    puts "- #{names[0].display_name}"
  end
end

I would like to see more examples about use of signet

My current attempt on accessing Google always ends up with invalid_grant.

Here is the problematic part of the code:
oauth2_client = Signet::OAuth2::Client.new(authentication_uri: "https://accounts.google.com/o/oauth2/auth",
token_credential_uri: "https://accounts.google.com/o/oauth2/token",
client_id: "XXXXXXXXXXX-lotOfGibberish.apps.googleusercontent.com",
client_secret: "clientSecretFromGoogleConsole",
redirect_uri: "urn:ietf:wg:oauth:2.0:oob",
scope: "https://picasaweb.google.com/data/",
refresh_token: "inventedStringHere")
p oauth2_client.refresh!

And end result is always:
gems/ruby-2.1.1/gems/signet-0.6.0/lib/signet/oauth_2/client.rb:947:in `fetch_access_token': Authorization failed. Server message: (Signet::AuthorizationError)
{
"error" : "invalid_grant"
}

Many OAuth2 errors seem to be related to clock, but that shouldn't be the problem:
$ ntpq -p
remote refid st t when poll reach delay offset jitter
*defra1-ntp-002. .GPSs. 1 u 2 64 377 41.766 31.970 8.969

v0.9.0 appears to have broken auto-refresh

With the upgrade to v0.9.0, we started seeing a bunch of token expiration messages in our production app. Whereas with v0.8.1 access tokens appeared to get refreshed automatically, with v0.9.0 manual refresh appears to be necessary. Is this behavior intentional? Should we be manually refreshing access tokens? Here's how we're configuring the client:

::Google::Apis::AndroidpublisherV2::AndroidPublisherService.new.tap do |client|
  client.authorization = Signet::OAuth2::Client.new(
    client_id: config.google["client_id"],
    client_secret: config.google["client_secret"],
    refresh_token: config.google["refresh_token"],
    token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
    authorization_uri: 'https://accounts.google.com/o/oauth2/auth',
  )
end

signet as Faraday middleware?

With the switch to Faraday, it might make sense to offer signet as Faraday middleware rather than acting as a front-end to it. E.g.

client = Signet::OAuth2::Client.new...
access_token = ... # get access token

conn = Faraday.new do |builder|
  builder. user Signet::OAuth2::Client, access_token
end

conn.get(...)

(Copied from @sqrrrl's issue on the original Signet project.)

no implicit conversion of Fixnum into String

Hi.
I'm using Rails 4, & ruby 2.1 and I'm getting this error in SOAP connection:
no implicit conversion of Fixnum into String

More details:
rails (4.1.0)
faraday (0.9.1)

google-ads-common (0.9.8)
  httpi (~> 1.1.0)
  savon (~> 1.2.0)
  signet (~> 0.6.0)
google-adwords-api (0.14.2)
  google-ads-common (~> 0.9.8)
google-dfp-api (0.9.4)
  google-ads-common (~> 0.9.4)

I notice that similar code works in Rails 3.2.20,and fails with Rails 4.1 , 4.2, and similar Rails 4

https://github.com/google/signet/blob/master/lib/signet/oauth_2/client.rb Line 748:

      def expires_at
        if @expires_at
          @expires_at
        elsif @issued_at && @expires_in
          return @issued_at + @expires_in #TODO: @issued_at is String, String + Fixnum fails. 
        else
          return nil
        end
      end

Related with issue: googleads/google-api-ads-ruby#53 (comment)

Any suggestion?
thanks

OAuth2 fetch_access_token produces an error if response is not JSON

If the response of the fetch_access_token method is not JSON, an error is thown. Here's the specific line that shows that the response is sent to parse_json_credentials where the MultiJson.load() method is then called: https://github.com/google/signet/blob/master/lib/signet/oauth_2/client.rb#L867

I noticed this issue when attempting to manually integrate Facebook OAuth login. The facebook success body for fetch_access_token is in the following form:

access_token={access-token}&expires={seconds-til-expiration}

Here's the documentation url for Facebook OAuth: https://developers.facebook.com/docs/facebook-login/login-flow-for-web-no-jssdk/#confirm

Obviously this breaks when the MultiJson library is loaded. Would it be possible to override that handler so that the response can be manually parsed? Or maybe detect and parse url encoded data as well as json data?

Signet still uses client_id and client_secret parameters instead of Authorization header

"Including the client credentials in the request-body using the two parameters is NOT RECOMMENDED and SHOULD be limited to clients unable to directly utilize the HTTP Basic authentication scheme (or other password-based HTTP authentication schemes). The parameters can only be transmitted in the request-body and MUST NOT be included in the request URI."

Back in draft 7 or so when Signet was originally written, it was the other way around, where client_id and client_secret were the recommended approach. However nowadays, lots of auth servers don't support these parameters (auth servers MAY support them) and Signet should switch to doing what the final RFC recommends.

Faraday call fails behind a proxy

Hello, I've just had the same issue, - the same code works fine out of proxy but fails if there is proxy.
Here is what the env command displays in the Terminal:

http_proxy=http://host-name.net:80
HTTP_PROXY=http://host-name.net:80
https_proxy=http://host-name.net:80
HTTPS_PROXY=http://host-name.net:80
no_proxy=127.0.0.1,localhost,10.1*.*.*,....some.others.sites.com
NO_PROXY=127.0.0.1,localhost,10.1*.*.*,som.others.sites.com

Here is the error I get when trying to access Google APIs:
IPAddr::InvalidAddressError (invalid address), see below function:

def auth_client
    Signet::OAuth2::Client.new(
      authorization_uri: 'https://accounts.google.com/o/oauth2/auth',
      token_credential_uri: 'https://www.googleapis.com/oauth2/v3/token',
      client_id: ENV['GOOGLE_KEY'], client_secret: ENV['GOOGLE_SECRET'],
      scope: 'email profile', redirect_uri: 'http://localhost:4200/oauth2callback'
    ).tap do |client|
      client.code = params['code']
      client.fetch_access_token!
    end
  end

The line that fails is:

client.fetch_access_token!

Here are my settings:

ruby 2.4.0
gem 'google-api-client', '~> 0.19.3'
gem 'jwt', '~> 2.1.0'
gem 'signet', '~> 0.8.1'
gem 'figaro', '~> 1.1.1'
rails '5.1.4'

If I unset the proxy setting, here is the error I have:

Faraday::ConnectionFailed (Failed to open TCP connection to www.googleapis.com:443 (No route to host - connect(2) for "www.googleapis.com" port 443)):

Any ideas on what is wrong here ? Thank you.

Still maintained?

Is this project still being maintained?
The last activity was 5 months ago, since then there have been many PRs and issues which haven't been commented on.
Since the google-api-ruby-client project, which I and probably lots of other folks use, depends on signet, it would be nice to know what the plan is.
Thanks!

Missing "approval_prompt" in authorization_uri

I wrote a Rails app synching with Google Calendar. I'm currently trying to obtain new refresh_tokens from the API as a bug prevented me from saving them in the first place.
I am using approval_prompt: force (or prompt: consent I have tried both), the code is:

google_client = Signet::OAuth2::Client.new({
     client_id: ENV.fetch('GOOGLE_CLIENT_ID'),
     client_secret: ENV.fetch('GOOGLE_CLIENT_SECRET'),
     authorization_uri: 'https://accounts.google.com/o/oauth2/auth',
     scope: Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY,
     redirect_uri: 'http://localhost:8000/google_calendar',
     access_type: "offline",
     approval_prompt: "force"
  })

URI = google_client.authorization_uri.to_s

The URI does not contain the approval_prompt that I would expect (and when I add it manually, I do get prompted for consent). Am I missing something?

Getting invalid grant while fetching access token

I am using Signet::Oauth2::Client to get an access token

client = Signet::OAuth2::Client.new(
  authorization_uri: 'https://accounts.google.com/o/oauth2/v2/auth',
  token_credential_uri:  'https://www.googleapis.com/oauth2/v4/token',
  client_id: 'my client id',
  client_secret: 'my client secret',
  grant_type: 'authorization_code',
  code: 'my code',
  redirect_uri: 'my redirect uri'
)

client.fetch_access_token!

But I am getting an error when I try to fetch access token
{"error": "invalid_grant", "error_description": "Bad Request"}

I am not sure why I'm getting this error. Am I missing something? Please help.

Thanks!
@brainopia @timburks @blowmage @sporkmonger

access_token always expires in 3600

given that I have a refresh_token I was able to issue a new access_token

client = Signet::OAuth2::Client.new(
  token_credential_uri: TOKEN_CRED_URI,
  client_id: ENV['GOOGLE_APP_ID'],
  client_secret: ENV['GOOGLE_APP_SECRET'],
  refresh_token: @refresh_token,
  expires_in: 3600 * 24 # extend the validity to 24 hours
).refresh! ['access_token']

the problem is that no matter the expiry, expires_in params I pass... I always get an access_token
that is only valid for 3600 seconds

I've also tried

client.update!(
  expiry: 180,
  access_token: @access_token,
  refresh_token: @refresh_token
).access_token

both return an access token that only valid for 3600 seconds
(verified at https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=ACESS_TOKEN )

Signet::OAuth2::Client#decoded_id_token should take block and pass to JWT.decode

JWT.decode takes an optional block to fetch or select the public key to use to authenticate an id_token at decode-time, but this feature is not exposed via Signet::OAuth2::Client#decoded_id_token. It should be.

This is especially in light of the fact this feature is the only way (without invoking JWT.decode directly) to handle Google's OAuth id_tokens which can be signed by any of an array of public keys which need to be fetched at the endpoint https://www.googleapis.com/oauth2/v1/certs.

Gemspec dependency version conflict with code

In your gemspec you have specified JWT version ~>1.0 and faraday version ~>0.9.

But inside your oauth 2 client you override the dependencies with incompatible versions.

End result is it's impossible to use the signet gem with Bundler, which will install the gemspeced dependencies and subsequently raise error when trying to initialize the oauth client:

/home/runner/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/bundler-1.7.11/lib/bundler/rubygems_integration.rb:266:in `block in replace_gem': can't activate jwt (~> 0.1.4), already activated jwt-1.2.0. Make sure all dependencies are added to Gemfile. (Gem::LoadError)
    from /home/runner/mynewsdesk/vendor/bundle/ruby/2.1.0/gems/signet-0.4.5/lib/signet/oauth_2/client.rb:25:in `<top (required)>'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.