googleapis / google-auth-library-ruby

Google Auth Library for Ruby
Apache License 2.0
472 stars 254 forks source link

Support short-lived service account credentials (OAuth 2.0 access tokens) #346

Closed quartzmo closed 3 years ago

quartzmo commented 3 years ago

Is your feature request related to a problem? Please describe.

Per googleapis/google-cloud-ruby#13121, impersonating a service account by generating a short-lived OAuth 2.0 access token does not seem to work with the Google::Auth::Credentials class in this library, in the context of authenticating google-cloud-bigquery or google-cloud-storage via their underlying google-api-ruby-client generated clients.

Here is an example response from the token generation linked above:

{
  "accessToken": "ya29.a0ARrd...",
  "expireTime": "2021-10-12T00:25:18Z"
}

In the BigQuery scenario shown below the short-lived token is already set in Signet::OAuth2::Client#access_token before the BigQuery client passes the Signet client to Google::Auth::Credentials#initialize, which errors on the call to Signet::OAuth2::Client#fetch_access_token!. (The erroring method, Signet::OAuth2::Client#fetch_access_token!, depends on a token endpoint URI and various other inputs, none of which are needed if you already have the short-lived access token, which is valid/invalid and cannot be refreshed.)

token = "ya29.a0ARrd..."

require "google/cloud/bigquery"

client = Signet::OAuth2::Client.new
client.access_token = token
Google::Cloud::Bigquery.configure do |config|
  config.credentials = client
end

bigquery = Google::Cloud::Bigquery.new

The example above results in the following error:

irb(main):006:1* Google::Cloud::Bigquery.configure do |config|
irb(main):007:1*   config.credentials = client
irb(main):008:0> end
irb(main):009:0> 
irb(main):010:0> bigquery = Google::Cloud::Bigquery.new
/Users/quartzmo/.rbenv/versions/3.0.1/gemsets/quartzmo/gems/googleauth-0.16.2/lib/googleauth/signet.rb:118:in `rescue in retry_with_error': Unexpected error: #<ArgumentError: Missing token endpoint URI.> (Signet::AuthorizationError)
    from /Users/quartzmo/.rbenv/versions/3.0.1/gemsets/quartzmo/gems/googleauth-0.16.2/lib/googleauth/signet.rb:107:in `retry_with_error'
    from /Users/quartzmo/.rbenv/versions/3.0.1/gemsets/quartzmo/gems/googleauth-0.16.2/lib/googleauth/signet.rb:80:in `fetch_access_token!'
    from /Users/quartzmo/.rbenv/versions/3.0.1/gemsets/quartzmo/gems/googleauth-0.16.2/lib/googleauth/credentials.rb:382:in `initialize'
    from /Users/quartzmo/.rbenv/versions/3.0.1/gemsets/quartzmo/gems/google-cloud-bigquery-1.29.0/lib/google/cloud/bigquery.rb:78:in `new'
    from /Users/quartzmo/.rbenv/versions/3.0.1/gemsets/quartzmo/gems/google-cloud-bigquery-1.29.0/lib/google/cloud/bigquery.rb:78:in `new'

Describe the solution you'd like

If Google::Auth::Credentials could support using a Signet::OAuth2::Client initialized with a short-lived OAuth 2.0 access_token by conditionally skipping the call to Signet::OAuth2::Client#fetch_access_token! in Google::Auth::Credentials#initialize, I think that would solve the problem. Naturally, the conditional should be written to be backward-compatible with any existing use cases that depend on #fetch_access_token!.

Describe alternatives you've considered

I can't think of any practical alternatives.