googleapis/signet

IAP: Support for additional claim target_audience

Closed this issue · 3 comments

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="SA@PROJECT.iam.gserviceaccount.com", @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="SA@PROJECT.iam.gserviceaccount.com", @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="SA@PROJECT.iam.gserviceaccount.com", @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

Thanks for the report. Turns out, we're just now in the process of adding proper support for the target_audience extra assertion and fetching ID tokens from the Google OAuth2 service. So this should be solved in the next few days.

Any updates?

Sorry, forgot to update this issue. Yes, there is now a target_audience field on the client object in Signet 0.14.0. There is also an update to the googleauth gem (version 0.12.0) that lets you pass a target_audience: argument to most credential constructors to make them use identity tokens. We haven't yet updated the Ruby documentation and samples online; that's forthcoming.