googleauth 1.2.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +99 -0
 - data/README.md +49 -8
 - data/lib/googleauth/application_default.rb +5 -9
 - data/lib/googleauth/base_client.rb +80 -0
 - data/lib/googleauth/client_id.rb +25 -8
 - data/lib/googleauth/compute_engine.rb +65 -35
 - data/lib/googleauth/credentials.rb +32 -30
 - data/lib/googleauth/credentials_loader.rb +6 -14
 - data/lib/googleauth/default_credentials.rb +5 -2
 - data/lib/googleauth/external_account/aws_credentials.rb +378 -0
 - data/lib/googleauth/external_account/base_credentials.rb +159 -0
 - data/lib/googleauth/external_account/external_account_utils.rb +103 -0
 - data/lib/googleauth/external_account/identity_pool_credentials.rb +118 -0
 - data/lib/googleauth/external_account/pluggable_credentials.rb +156 -0
 - data/lib/googleauth/external_account.rb +94 -0
 - data/lib/googleauth/helpers/connection.rb +35 -0
 - data/lib/googleauth/id_tokens/key_sources.rb +9 -10
 - data/lib/googleauth/id_tokens.rb +2 -2
 - data/lib/googleauth/json_key_reader.rb +2 -1
 - data/lib/googleauth/oauth2/sts_client.rb +109 -0
 - data/lib/googleauth/scope_util.rb +35 -2
 - data/lib/googleauth/service_account.rb +25 -11
 - data/lib/googleauth/signet.rb +14 -38
 - data/lib/googleauth/user_authorizer.rb +66 -9
 - data/lib/googleauth/user_refresh.rb +4 -2
 - data/lib/googleauth/version.rb +1 -1
 - data/lib/googleauth/web_user_authorizer.rb +19 -8
 - metadata +29 -20
 
| 
         @@ -130,13 +130,8 @@ module Google 
     | 
|
| 
       130 
130 
     | 
    
         
             
                        end
         
     | 
| 
       131 
131 
     | 
    
         
             
                        n_bn = OpenSSL::BN.new n_data, 2
         
     | 
| 
       132 
132 
     | 
    
         
             
                        e_bn = OpenSSL::BN.new e_data, 2
         
     | 
| 
       133 
     | 
    
         
            -
                         
     | 
| 
       134 
     | 
    
         
            -
                         
     | 
| 
       135 
     | 
    
         
            -
                          rsa_key.set_key n_bn, e_bn, nil
         
     | 
| 
       136 
     | 
    
         
            -
                        else
         
     | 
| 
       137 
     | 
    
         
            -
                          rsa_key.n = n_bn
         
     | 
| 
       138 
     | 
    
         
            -
                          rsa_key.e = e_bn
         
     | 
| 
       139 
     | 
    
         
            -
                        end
         
     | 
| 
      
 133 
     | 
    
         
            +
                        sequence = [OpenSSL::ASN1::Integer.new(n_bn), OpenSSL::ASN1::Integer.new(e_bn)]
         
     | 
| 
      
 134 
     | 
    
         
            +
                        rsa_key =  OpenSSL::PKey::RSA.new OpenSSL::ASN1::Sequence(sequence).to_der
         
     | 
| 
       140 
135 
     | 
    
         
             
                        rsa_key.public_key
         
     | 
| 
       141 
136 
     | 
    
         
             
                      end
         
     | 
| 
       142 
137 
     | 
    
         | 
| 
         @@ -161,9 +156,13 @@ module Google 
     | 
|
| 
       161 
156 
     | 
    
         
             
                        x_hex = x_data.unpack1 "H*"
         
     | 
| 
       162 
157 
     | 
    
         
             
                        y_hex = y_data.unpack1 "H*"
         
     | 
| 
       163 
158 
     | 
    
         
             
                        bn = OpenSSL::BN.new ["04#{x_hex}#{y_hex}"].pack("H*"), 2
         
     | 
| 
       164 
     | 
    
         
            -
                         
     | 
| 
       165 
     | 
    
         
            -
                         
     | 
| 
       166 
     | 
    
         
            -
             
     | 
| 
      
 159 
     | 
    
         
            +
                        point =  OpenSSL::PKey::EC::Point.new group, bn
         
     | 
| 
      
 160 
     | 
    
         
            +
                        sequence = OpenSSL::ASN1::Sequence([
         
     | 
| 
      
 161 
     | 
    
         
            +
                                                             OpenSSL::ASN1::Sequence([OpenSSL::ASN1::ObjectId("id-ecPublicKey"),
         
     | 
| 
      
 162 
     | 
    
         
            +
                                                                                      OpenSSL::ASN1::ObjectId(curve_name)]),
         
     | 
| 
      
 163 
     | 
    
         
            +
                                                             OpenSSL::ASN1::BitString(point.to_octet_string(:uncompressed))
         
     | 
| 
      
 164 
     | 
    
         
            +
                                                           ])
         
     | 
| 
      
 165 
     | 
    
         
            +
                        OpenSSL::PKey::EC.new sequence.to_der
         
     | 
| 
       167 
166 
     | 
    
         
             
                      end
         
     | 
| 
       168 
167 
     | 
    
         
             
                    end
         
     | 
| 
       169 
168 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/googleauth/id_tokens.rb
    CHANGED
    
    | 
         @@ -153,7 +153,7 @@ module Google 
     | 
|
| 
       153 
153 
     | 
    
         
             
                    #     one of the provided values, or the verification will fail with
         
     | 
| 
       154 
154 
     | 
    
         
             
                    #     {Google::Auth::IDToken::AuthorizedPartyMismatchError}. If `nil`
         
     | 
| 
       155 
155 
     | 
    
         
             
                    #     (the default), no azp checking is performed.
         
     | 
| 
       156 
     | 
    
         
            -
                    # @param  
     | 
| 
      
 156 
     | 
    
         
            +
                    # @param iss [String,Array<String>,nil] The expected issuer. At least
         
     | 
| 
       157 
157 
     | 
    
         
             
                    #     one `iss` field in the token must match at least one of the
         
     | 
| 
       158 
158 
     | 
    
         
             
                    #     provided issuers, or the verification will fail with
         
     | 
| 
       159 
159 
     | 
    
         
             
                    #     {Google::Auth::IDToken::IssuerMismatchError}. If `nil`, no issuer
         
     | 
| 
         @@ -191,7 +191,7 @@ module Google 
     | 
|
| 
       191 
191 
     | 
    
         
             
                    #     one of the provided values, or the verification will fail with
         
     | 
| 
       192 
192 
     | 
    
         
             
                    #     {Google::Auth::IDToken::AuthorizedPartyMismatchError}. If `nil`
         
     | 
| 
       193 
193 
     | 
    
         
             
                    #     (the default), no azp checking is performed.
         
     | 
| 
       194 
     | 
    
         
            -
                    # @param  
     | 
| 
      
 194 
     | 
    
         
            +
                    # @param iss [String,Array<String>,nil] The expected issuer. At least
         
     | 
| 
       195 
195 
     | 
    
         
             
                    #     one `iss` field in the token must match at least one of the
         
     | 
| 
       196 
196 
     | 
    
         
             
                    #     provided issuers, or the verification will fail with
         
     | 
| 
       197 
197 
     | 
    
         
             
                    #     {Google::Auth::IDToken::IssuerMismatchError}. If `nil`, no issuer
         
     | 
| 
         @@ -0,0 +1,109 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Copyright 2023 Google LLC
         
     | 
| 
      
 2 
     | 
    
         
            +
            #
         
     | 
| 
      
 3 
     | 
    
         
            +
            # Licensed under the Apache License, Version 2.0 (the "License");
         
     | 
| 
      
 4 
     | 
    
         
            +
            # you may not use this file except in compliance with the License.
         
     | 
| 
      
 5 
     | 
    
         
            +
            # You may obtain a copy of the License at
         
     | 
| 
      
 6 
     | 
    
         
            +
            #
         
     | 
| 
      
 7 
     | 
    
         
            +
            #      http://www.apache.org/licenses/LICENSE-2.0
         
     | 
| 
      
 8 
     | 
    
         
            +
            #
         
     | 
| 
      
 9 
     | 
    
         
            +
            # Unless required by applicable law or agreed to in writing, software
         
     | 
| 
      
 10 
     | 
    
         
            +
            # distributed under the License is distributed on an "AS IS" BASIS,
         
     | 
| 
      
 11 
     | 
    
         
            +
            # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         
     | 
| 
      
 12 
     | 
    
         
            +
            # See the License for the specific language governing permissions and
         
     | 
| 
      
 13 
     | 
    
         
            +
            # limitations under the License.
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            require "googleauth/helpers/connection"
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            module Google
         
     | 
| 
      
 18 
     | 
    
         
            +
              module Auth
         
     | 
| 
      
 19 
     | 
    
         
            +
                module OAuth2
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # OAuth 2.0 Token Exchange Spec.
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # This module defines a token exchange utility based on the
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # [OAuth 2.0 Token Exchange](https://tools.ietf.org/html/rfc8693) spec. This will be mainly
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # used to exchange external credentials for GCP access tokens in workload identity pools to
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # access Google APIs.
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # The implementation will support various types of client authentication as allowed in the spec.
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #
         
     | 
| 
      
 27 
     | 
    
         
            +
                  # A deviation on the spec will be for additional Google specific options that cannot be easily
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # mapped to parameters defined in the RFC.
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # The returned dictionary response will be based on the [rfc8693 section 2.2.1]
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # (https://tools.ietf.org/html/rfc8693#section-2.2.1) spec JSON response.
         
     | 
| 
      
 31 
     | 
    
         
            +
                  #
         
     | 
| 
      
 32 
     | 
    
         
            +
                  class STSClient
         
     | 
| 
      
 33 
     | 
    
         
            +
                    include Helpers::Connection
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    URLENCODED_HEADERS = { "Content-Type": "application/x-www-form-urlencoded" }.freeze
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    # Create a new instance of the STSClient.
         
     | 
| 
      
 38 
     | 
    
         
            +
                    #
         
     | 
| 
      
 39 
     | 
    
         
            +
                    # @param [String] token_exchange_endpoint
         
     | 
| 
      
 40 
     | 
    
         
            +
                    #  The token exchange endpoint.
         
     | 
| 
      
 41 
     | 
    
         
            +
                    def initialize options = {}
         
     | 
| 
      
 42 
     | 
    
         
            +
                      raise "Token exchange endpoint can not be nil" if options[:token_exchange_endpoint].nil?
         
     | 
| 
      
 43 
     | 
    
         
            +
                      self.default_connection = options[:connection]
         
     | 
| 
      
 44 
     | 
    
         
            +
                      @token_exchange_endpoint = options[:token_exchange_endpoint]
         
     | 
| 
      
 45 
     | 
    
         
            +
                    end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    # Exchanges the provided token for another type of token based on the
         
     | 
| 
      
 48 
     | 
    
         
            +
                    # rfc8693 spec
         
     | 
| 
      
 49 
     | 
    
         
            +
                    #
         
     | 
| 
      
 50 
     | 
    
         
            +
                    # @param [Faraday instance] connection
         
     | 
| 
      
 51 
     | 
    
         
            +
                    # A callable faraday instance used to make HTTP requests.
         
     | 
| 
      
 52 
     | 
    
         
            +
                    # @param [String] grant_type
         
     | 
| 
      
 53 
     | 
    
         
            +
                    #   The OAuth 2.0 token exchange grant type.
         
     | 
| 
      
 54 
     | 
    
         
            +
                    # @param [String] subject_token
         
     | 
| 
      
 55 
     | 
    
         
            +
                    #   The OAuth 2.0 token exchange subject token.
         
     | 
| 
      
 56 
     | 
    
         
            +
                    # @param [String] subject_token_type
         
     | 
| 
      
 57 
     | 
    
         
            +
                    #   The OAuth 2.0 token exchange subject token type.
         
     | 
| 
      
 58 
     | 
    
         
            +
                    # @param [String] resource
         
     | 
| 
      
 59 
     | 
    
         
            +
                    #   The optional OAuth 2.0 token exchange resource field.
         
     | 
| 
      
 60 
     | 
    
         
            +
                    # @param [String] audience
         
     | 
| 
      
 61 
     | 
    
         
            +
                    #   The optional OAuth 2.0 token exchange audience field.
         
     | 
| 
      
 62 
     | 
    
         
            +
                    # @param [Array<String>] scopes
         
     | 
| 
      
 63 
     | 
    
         
            +
                    #   The optional list of scopes to use.
         
     | 
| 
      
 64 
     | 
    
         
            +
                    # @param [String] requested_token_type
         
     | 
| 
      
 65 
     | 
    
         
            +
                    #   The optional OAuth 2.0 token exchange requested token type.
         
     | 
| 
      
 66 
     | 
    
         
            +
                    # @param additional_headers (Hash<String,String>):
         
     | 
| 
      
 67 
     | 
    
         
            +
                    #   The optional additional headers to pass to the token exchange endpoint.
         
     | 
| 
      
 68 
     | 
    
         
            +
                    #
         
     | 
| 
      
 69 
     | 
    
         
            +
                    # @return [Hash] A hash containing the token exchange response.
         
     | 
| 
      
 70 
     | 
    
         
            +
                    def exchange_token options = {}
         
     | 
| 
      
 71 
     | 
    
         
            +
                      missing_required_opts = [:grant_type, :subject_token, :subject_token_type] - options.keys
         
     | 
| 
      
 72 
     | 
    
         
            +
                      unless missing_required_opts.empty?
         
     | 
| 
      
 73 
     | 
    
         
            +
                        raise ArgumentError, "Missing required options: #{missing_required_opts.join ', '}"
         
     | 
| 
      
 74 
     | 
    
         
            +
                      end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                      # TODO: Add the ability to add authentication to the headers
         
     | 
| 
      
 77 
     | 
    
         
            +
                      headers = URLENCODED_HEADERS.dup.merge(options[:additional_headers] || {})
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                      request_body = make_request options
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                      response = connection.post @token_exchange_endpoint, URI.encode_www_form(request_body), headers
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                      if response.status != 200
         
     | 
| 
      
 84 
     | 
    
         
            +
                        raise "Token exchange failed with status #{response.status}"
         
     | 
| 
      
 85 
     | 
    
         
            +
                      end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                      MultiJson.load response.body
         
     | 
| 
      
 88 
     | 
    
         
            +
                    end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                    private
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                    def make_request options = {}
         
     | 
| 
      
 93 
     | 
    
         
            +
                      request_body = {
         
     | 
| 
      
 94 
     | 
    
         
            +
                        grant_type: options[:grant_type],
         
     | 
| 
      
 95 
     | 
    
         
            +
                        audience: options[:audience],
         
     | 
| 
      
 96 
     | 
    
         
            +
                        scope: Array(options[:scopes])&.join(" ") || [],
         
     | 
| 
      
 97 
     | 
    
         
            +
                        requested_token_type: options[:requested_token_type],
         
     | 
| 
      
 98 
     | 
    
         
            +
                        subject_token: options[:subject_token],
         
     | 
| 
      
 99 
     | 
    
         
            +
                        subject_token_type: options[:subject_token_type]
         
     | 
| 
      
 100 
     | 
    
         
            +
                      }
         
     | 
| 
      
 101 
     | 
    
         
            +
                      unless options[:additional_options].nil?
         
     | 
| 
      
 102 
     | 
    
         
            +
                        request_body[:options] = CGI.escape MultiJson.dump(options[:additional_options], symbolize_name: true)
         
     | 
| 
      
 103 
     | 
    
         
            +
                      end
         
     | 
| 
      
 104 
     | 
    
         
            +
                      request_body
         
     | 
| 
      
 105 
     | 
    
         
            +
                    end
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
              end
         
     | 
| 
      
 109 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -18,27 +18,60 @@ require "multi_json" 
     | 
|
| 
       18 
18 
     | 
    
         | 
| 
       19 
19 
     | 
    
         
             
            module Google
         
     | 
| 
       20 
20 
     | 
    
         
             
              module Auth
         
     | 
| 
       21 
     | 
    
         
            -
                 
     | 
| 
      
 21 
     | 
    
         
            +
                ##
         
     | 
| 
      
 22 
     | 
    
         
            +
                # Small utility for normalizing scopes into canonical form.
         
     | 
| 
      
 23 
     | 
    
         
            +
                #
         
     | 
| 
      
 24 
     | 
    
         
            +
                # The canonical form of scopes is as an array of strings, each in the form
         
     | 
| 
      
 25 
     | 
    
         
            +
                # of a full URL. This utility converts space-delimited scope strings into
         
     | 
| 
      
 26 
     | 
    
         
            +
                # this form, and handles a small number of common aliases.
         
     | 
| 
      
 27 
     | 
    
         
            +
                #
         
     | 
| 
      
 28 
     | 
    
         
            +
                # This is used by UserRefreshCredentials to verify that a credential grants
         
     | 
| 
      
 29 
     | 
    
         
            +
                # a requested scope.
         
     | 
| 
      
 30 
     | 
    
         
            +
                #
         
     | 
| 
       22 
31 
     | 
    
         
             
                module ScopeUtil
         
     | 
| 
      
 32 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # Aliases understood by this utility
         
     | 
| 
      
 34 
     | 
    
         
            +
                  #
         
     | 
| 
       23 
35 
     | 
    
         
             
                  ALIASES = {
         
     | 
| 
       24 
36 
     | 
    
         
             
                    "email"   => "https://www.googleapis.com/auth/userinfo.email",
         
     | 
| 
       25 
37 
     | 
    
         
             
                    "profile" => "https://www.googleapis.com/auth/userinfo.profile",
         
     | 
| 
       26 
38 
     | 
    
         
             
                    "openid"  => "https://www.googleapis.com/auth/plus.me"
         
     | 
| 
       27 
39 
     | 
    
         
             
                  }.freeze
         
     | 
| 
       28 
40 
     | 
    
         | 
| 
      
 41 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 42 
     | 
    
         
            +
                  # Normalize the input, which may be an array of scopes or a whitespace-
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # delimited scope string. The output is always an array, even if a single
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # scope is input.
         
     | 
| 
      
 45 
     | 
    
         
            +
                  #
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # @param scope [String,Array<String>] Input scope(s)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # @return [Array<String>] An array of scopes in canonical form.
         
     | 
| 
      
 48 
     | 
    
         
            +
                  #
         
     | 
| 
       29 
49 
     | 
    
         
             
                  def self.normalize scope
         
     | 
| 
       30 
50 
     | 
    
         
             
                    list = as_array scope
         
     | 
| 
       31 
51 
     | 
    
         
             
                    list.map { |item| ALIASES[item] || item }
         
     | 
| 
       32 
52 
     | 
    
         
             
                  end
         
     | 
| 
       33 
53 
     | 
    
         | 
| 
      
 54 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # Ensure the input is an array. If a single string is passed in, splits
         
     | 
| 
      
 56 
     | 
    
         
            +
                  # it via whitespace. Does not interpret aliases.
         
     | 
| 
      
 57 
     | 
    
         
            +
                  #
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # @param scope [String,Array<String>] Input scope(s)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # @return [Array<String>] Always an array of strings
         
     | 
| 
      
 60 
     | 
    
         
            +
                  # @raise ArgumentError If the input is not a string or array of strings
         
     | 
| 
      
 61 
     | 
    
         
            +
                  #
         
     | 
| 
       34 
62 
     | 
    
         
             
                  def self.as_array scope
         
     | 
| 
       35 
63 
     | 
    
         
             
                    case scope
         
     | 
| 
       36 
64 
     | 
    
         
             
                    when Array
         
     | 
| 
      
 65 
     | 
    
         
            +
                      scope.each do |item|
         
     | 
| 
      
 66 
     | 
    
         
            +
                        unless item.is_a? String
         
     | 
| 
      
 67 
     | 
    
         
            +
                          raise ArgumentError, "Invalid scope value: #{item.inspect}. Must be string or array"
         
     | 
| 
      
 68 
     | 
    
         
            +
                        end
         
     | 
| 
      
 69 
     | 
    
         
            +
                      end
         
     | 
| 
       37 
70 
     | 
    
         
             
                      scope
         
     | 
| 
       38 
71 
     | 
    
         
             
                    when String
         
     | 
| 
       39 
72 
     | 
    
         
             
                      scope.split
         
     | 
| 
       40 
73 
     | 
    
         
             
                    else
         
     | 
| 
       41 
     | 
    
         
            -
                      raise "Invalid scope value. Must be string or array"
         
     | 
| 
      
 74 
     | 
    
         
            +
                      raise ArgumentError, "Invalid scope value: #{scope.inspect}. Must be string or array"
         
     | 
| 
       42 
75 
     | 
    
         
             
                    end
         
     | 
| 
       43 
76 
     | 
    
         
             
                  end
         
     | 
| 
       44 
77 
     | 
    
         
             
                end
         
     | 
| 
         @@ -39,7 +39,11 @@ module Google 
     | 
|
| 
       39 
39 
     | 
    
         
             
                  attr_reader :quota_project_id
         
     | 
| 
       40 
40 
     | 
    
         | 
| 
       41 
41 
     | 
    
         
             
                  def enable_self_signed_jwt?
         
     | 
| 
       42 
     | 
    
         
            -
                     
     | 
| 
      
 42 
     | 
    
         
            +
                    # Use a self-singed JWT if there's no information that can be used to
         
     | 
| 
      
 43 
     | 
    
         
            +
                    # obtain an OAuth token, OR if there are scopes but also an assertion
         
     | 
| 
      
 44 
     | 
    
         
            +
                    # that they are default scopes that shouldn't be used to fetch a token,
         
     | 
| 
      
 45 
     | 
    
         
            +
                    # OR we are not in the default universe and thus OAuth isn't supported.
         
     | 
| 
      
 46 
     | 
    
         
            +
                    target_audience.nil? && (scope.nil? || @enable_self_signed_jwt || universe_domain != "googleapis.com")
         
     | 
| 
       43 
47 
     | 
    
         
             
                  end
         
     | 
| 
       44 
48 
     | 
    
         | 
| 
       45 
49 
     | 
    
         
             
                  # Creates a ServiceAccountCredentials.
         
     | 
| 
         @@ -53,12 +57,13 @@ module Google 
     | 
|
| 
       53 
57 
     | 
    
         
             
                    raise ArgumentError, "Cannot specify both scope and target_audience" if scope && target_audience
         
     | 
| 
       54 
58 
     | 
    
         | 
| 
       55 
59 
     | 
    
         
             
                    if json_key_io
         
     | 
| 
       56 
     | 
    
         
            -
                      private_key, client_email, project_id, quota_project_id = read_json_key json_key_io
         
     | 
| 
      
 60 
     | 
    
         
            +
                      private_key, client_email, project_id, quota_project_id, universe_domain = read_json_key json_key_io
         
     | 
| 
       57 
61 
     | 
    
         
             
                    else
         
     | 
| 
       58 
62 
     | 
    
         
             
                      private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
         
     | 
| 
       59 
63 
     | 
    
         
             
                      client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
         
     | 
| 
       60 
64 
     | 
    
         
             
                      project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
         
     | 
| 
       61 
65 
     | 
    
         
             
                      quota_project_id = nil
         
     | 
| 
      
 66 
     | 
    
         
            +
                      universe_domain = nil
         
     | 
| 
       62 
67 
     | 
    
         
             
                    end
         
     | 
| 
       63 
68 
     | 
    
         
             
                    project_id ||= CredentialsLoader.load_gcloud_project_id
         
     | 
| 
       64 
69 
     | 
    
         | 
| 
         @@ -70,7 +75,8 @@ module Google 
     | 
|
| 
       70 
75 
     | 
    
         
             
                        issuer:                 client_email,
         
     | 
| 
       71 
76 
     | 
    
         
             
                        signing_key:            OpenSSL::PKey::RSA.new(private_key),
         
     | 
| 
       72 
77 
     | 
    
         
             
                        project_id:             project_id,
         
     | 
| 
       73 
     | 
    
         
            -
                        quota_project_id:       quota_project_id 
     | 
| 
      
 78 
     | 
    
         
            +
                        quota_project_id:       quota_project_id,
         
     | 
| 
      
 79 
     | 
    
         
            +
                        universe_domain:        universe_domain || "googleapis.com")
         
     | 
| 
       74 
80 
     | 
    
         
             
                      .configure_connection(options)
         
     | 
| 
       75 
81 
     | 
    
         
             
                  end
         
     | 
| 
       76 
82 
     | 
    
         | 
| 
         @@ -93,16 +99,18 @@ module Google 
     | 
|
| 
       93 
99 
     | 
    
         
             
                  # Extends the base class to use a transient
         
     | 
| 
       94 
100 
     | 
    
         
             
                  # ServiceAccountJwtHeaderCredentials for certain cases.
         
     | 
| 
       95 
101 
     | 
    
         
             
                  def apply! a_hash, opts = {}
         
     | 
| 
       96 
     | 
    
         
            -
                     
     | 
| 
       97 
     | 
    
         
            -
                    # obtain an OAuth token, OR if there are scopes but also an assertion
         
     | 
| 
       98 
     | 
    
         
            -
                    # that they are default scopes that shouldn't be used to fetch a token.
         
     | 
| 
       99 
     | 
    
         
            -
                    if target_audience.nil? && (scope.nil? || enable_self_signed_jwt?)
         
     | 
| 
      
 102 
     | 
    
         
            +
                    if enable_self_signed_jwt?
         
     | 
| 
       100 
103 
     | 
    
         
             
                      apply_self_signed_jwt! a_hash
         
     | 
| 
       101 
104 
     | 
    
         
             
                    else
         
     | 
| 
       102 
105 
     | 
    
         
             
                      super
         
     | 
| 
       103 
106 
     | 
    
         
             
                    end
         
     | 
| 
       104 
107 
     | 
    
         
             
                  end
         
     | 
| 
       105 
108 
     | 
    
         | 
| 
      
 109 
     | 
    
         
            +
                  # Modifies this logic so it also requires self-signed-jwt to be disabled
         
     | 
| 
      
 110 
     | 
    
         
            +
                  def needs_access_token?
         
     | 
| 
      
 111 
     | 
    
         
            +
                    super && !enable_self_signed_jwt?
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
       106 
114 
     | 
    
         
             
                  private
         
     | 
| 
       107 
115 
     | 
    
         | 
| 
       108 
116 
     | 
    
         
             
                  def apply_self_signed_jwt! a_hash
         
     | 
| 
         @@ -130,7 +138,7 @@ module Google 
     | 
|
| 
       130 
138 
     | 
    
         
             
                # cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
         
     | 
| 
       131 
139 
     | 
    
         
             
                class ServiceAccountJwtHeaderCredentials
         
     | 
| 
       132 
140 
     | 
    
         
             
                  JWT_AUD_URI_KEY = :jwt_aud_uri
         
     | 
| 
       133 
     | 
    
         
            -
                  AUTH_METADATA_KEY =  
     | 
| 
      
 141 
     | 
    
         
            +
                  AUTH_METADATA_KEY = Google::Auth::BaseClient::AUTH_METADATA_KEY
         
     | 
| 
       134 
142 
     | 
    
         
             
                  TOKEN_CRED_URI = "https://www.googleapis.com/oauth2/v4/token".freeze
         
     | 
| 
       135 
143 
     | 
    
         
             
                  SIGNING_ALGORITHM = "RS256".freeze
         
     | 
| 
       136 
144 
     | 
    
         
             
                  EXPIRY = 60
         
     | 
| 
         @@ -138,6 +146,7 @@ module Google 
     | 
|
| 
       138 
146 
     | 
    
         
             
                  extend JsonKeyReader
         
     | 
| 
       139 
147 
     | 
    
         
             
                  attr_reader :project_id
         
     | 
| 
       140 
148 
     | 
    
         
             
                  attr_reader :quota_project_id
         
     | 
| 
      
 149 
     | 
    
         
            +
                  attr_accessor :universe_domain
         
     | 
| 
       141 
150 
     | 
    
         | 
| 
       142 
151 
     | 
    
         
             
                  # Create a ServiceAccountJwtHeaderCredentials.
         
     | 
| 
       143 
152 
     | 
    
         
             
                  #
         
     | 
| 
         @@ -154,14 +163,16 @@ module Google 
     | 
|
| 
       154 
163 
     | 
    
         
             
                  def initialize options = {}
         
     | 
| 
       155 
164 
     | 
    
         
             
                    json_key_io = options[:json_key_io]
         
     | 
| 
       156 
165 
     | 
    
         
             
                    if json_key_io
         
     | 
| 
       157 
     | 
    
         
            -
                      @private_key, @issuer, @project_id, @quota_project_id =
         
     | 
| 
      
 166 
     | 
    
         
            +
                      @private_key, @issuer, @project_id, @quota_project_id, @universe_domain =
         
     | 
| 
       158 
167 
     | 
    
         
             
                        self.class.read_json_key json_key_io
         
     | 
| 
       159 
168 
     | 
    
         
             
                    else
         
     | 
| 
       160 
169 
     | 
    
         
             
                      @private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
         
     | 
| 
       161 
170 
     | 
    
         
             
                      @issuer = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
         
     | 
| 
       162 
171 
     | 
    
         
             
                      @project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
         
     | 
| 
       163 
172 
     | 
    
         
             
                      @quota_project_id = nil
         
     | 
| 
      
 173 
     | 
    
         
            +
                      @universe_domain = nil
         
     | 
| 
       164 
174 
     | 
    
         
             
                    end
         
     | 
| 
      
 175 
     | 
    
         
            +
                    @universe_domain ||= "googleapis.com"
         
     | 
| 
       165 
176 
     | 
    
         
             
                    @project_id ||= CredentialsLoader.load_gcloud_project_id
         
     | 
| 
       166 
177 
     | 
    
         
             
                    @signing_key = OpenSSL::PKey::RSA.new @private_key
         
     | 
| 
       167 
178 
     | 
    
         
             
                    @scope = options[:scope]
         
     | 
| 
         @@ -192,8 +203,6 @@ module Google 
     | 
|
| 
       192 
203 
     | 
    
         
             
                    proc { |a_hash, opts = {}| apply a_hash, opts }
         
     | 
| 
       193 
204 
     | 
    
         
             
                  end
         
     | 
| 
       194 
205 
     | 
    
         | 
| 
       195 
     | 
    
         
            -
                  protected
         
     | 
| 
       196 
     | 
    
         
            -
             
     | 
| 
       197 
206 
     | 
    
         
             
                  # Creates a jwt uri token.
         
     | 
| 
       198 
207 
     | 
    
         
             
                  def new_jwt_token jwt_aud_uri = nil, options = {}
         
     | 
| 
       199 
208 
     | 
    
         
             
                    now = Time.new
         
     | 
| 
         @@ -212,6 +221,11 @@ module Google 
     | 
|
| 
       212 
221 
     | 
    
         | 
| 
       213 
222 
     | 
    
         
             
                    JWT.encode assertion, @signing_key, SIGNING_ALGORITHM
         
     | 
| 
       214 
223 
     | 
    
         
             
                  end
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
                  # Duck-types the corresponding method from BaseClient
         
     | 
| 
      
 226 
     | 
    
         
            +
                  def needs_access_token?
         
     | 
| 
      
 227 
     | 
    
         
            +
                    false
         
     | 
| 
      
 228 
     | 
    
         
            +
                  end
         
     | 
| 
       215 
229 
     | 
    
         
             
                end
         
     | 
| 
       216 
230 
     | 
    
         
             
              end
         
     | 
| 
       217 
231 
     | 
    
         
             
            end
         
     | 
    
        data/lib/googleauth/signet.rb
    CHANGED
    
    | 
         @@ -13,16 +13,27 @@ 
     | 
|
| 
       13 
13 
     | 
    
         
             
            # limitations under the License.
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
            require "signet/oauth_2/client"
         
     | 
| 
      
 16 
     | 
    
         
            +
            require "googleauth/base_client"
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
       17 
18 
     | 
    
         
             
            module Signet
         
     | 
| 
       18 
19 
     | 
    
         
             
              # OAuth2 supports OAuth2 authentication.
         
     | 
| 
       19 
20 
     | 
    
         
             
              module OAuth2
         
     | 
| 
       20 
     | 
    
         
            -
                AUTH_METADATA_KEY = :authorization
         
     | 
| 
       21 
21 
     | 
    
         
             
                # Signet::OAuth2::Client creates an OAuth2 client
         
     | 
| 
       22 
22 
     | 
    
         
             
                #
         
     | 
| 
       23 
23 
     | 
    
         
             
                # This reopens Client to add #apply and #apply! methods which update a
         
     | 
| 
       24 
24 
     | 
    
         
             
                # hash with the fetched authentication token.
         
     | 
| 
       25 
25 
     | 
    
         
             
                class Client
         
     | 
| 
      
 26 
     | 
    
         
            +
                  include Google::Auth::BaseClient
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  alias update_token_signet_base update_token!
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def update_token! options = {}
         
     | 
| 
      
 31 
     | 
    
         
            +
                    options = deep_hash_normalize options
         
     | 
| 
      
 32 
     | 
    
         
            +
                    update_token_signet_base options
         
     | 
| 
      
 33 
     | 
    
         
            +
                    self.universe_domain = options[:universe_domain] if options.key? :universe_domain
         
     | 
| 
      
 34 
     | 
    
         
            +
                    self
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
       26 
37 
     | 
    
         
             
                  def configure_connection options
         
     | 
| 
       27 
38 
     | 
    
         
             
                    @connection_info =
         
     | 
| 
       28 
39 
     | 
    
         
             
                      options[:connection_builder] || options[:default_connection]
         
     | 
| 
         @@ -34,36 +45,8 @@ module Signet 
     | 
|
| 
       34 
45 
     | 
    
         
             
                    target_audience ? :id_token : :access_token
         
     | 
| 
       35 
46 
     | 
    
         
             
                  end
         
     | 
| 
       36 
47 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
                  #  
     | 
| 
       38 
     | 
    
         
            -
                   
     | 
| 
       39 
     | 
    
         
            -
                    send(token_type).nil? || expires_within?(60)
         
     | 
| 
       40 
     | 
    
         
            -
                  end
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
                  # Updates a_hash updated with the authentication token
         
     | 
| 
       43 
     | 
    
         
            -
                  def apply! a_hash, opts = {}
         
     | 
| 
       44 
     | 
    
         
            -
                    # fetch the access token there is currently not one, or if the client
         
     | 
| 
       45 
     | 
    
         
            -
                    # has expired
         
     | 
| 
       46 
     | 
    
         
            -
                    fetch_access_token! opts if needs_access_token?
         
     | 
| 
       47 
     | 
    
         
            -
                    a_hash[AUTH_METADATA_KEY] = "Bearer #{send token_type}"
         
     | 
| 
       48 
     | 
    
         
            -
                  end
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                  # Returns a clone of a_hash updated with the authentication token
         
     | 
| 
       51 
     | 
    
         
            -
                  def apply a_hash, opts = {}
         
     | 
| 
       52 
     | 
    
         
            -
                    a_copy = a_hash.clone
         
     | 
| 
       53 
     | 
    
         
            -
                    apply! a_copy, opts
         
     | 
| 
       54 
     | 
    
         
            -
                    a_copy
         
     | 
| 
       55 
     | 
    
         
            -
                  end
         
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
                  # Returns a reference to the #apply method, suitable for passing as
         
     | 
| 
       58 
     | 
    
         
            -
                  # a closure
         
     | 
| 
       59 
     | 
    
         
            -
                  def updater_proc
         
     | 
| 
       60 
     | 
    
         
            -
                    proc { |a_hash, opts = {}| apply a_hash, opts }
         
     | 
| 
       61 
     | 
    
         
            -
                  end
         
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
                  def on_refresh &block
         
     | 
| 
       64 
     | 
    
         
            -
                    @refresh_listeners = [] unless defined? @refresh_listeners
         
     | 
| 
       65 
     | 
    
         
            -
                    @refresh_listeners << block
         
     | 
| 
       66 
     | 
    
         
            -
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # Set the universe domain
         
     | 
| 
      
 49 
     | 
    
         
            +
                  attr_accessor :universe_domain
         
     | 
| 
       67 
50 
     | 
    
         | 
| 
       68 
51 
     | 
    
         
             
                  alias orig_fetch_access_token! fetch_access_token!
         
     | 
| 
       69 
52 
     | 
    
         
             
                  def fetch_access_token! options = {}
         
     | 
| 
         @@ -78,13 +61,6 @@ module Signet 
     | 
|
| 
       78 
61 
     | 
    
         
             
                    info
         
     | 
| 
       79 
62 
     | 
    
         
             
                  end
         
     | 
| 
       80 
63 
     | 
    
         | 
| 
       81 
     | 
    
         
            -
                  def notify_refresh_listeners
         
     | 
| 
       82 
     | 
    
         
            -
                    listeners = defined?(@refresh_listeners) ? @refresh_listeners : []
         
     | 
| 
       83 
     | 
    
         
            -
                    listeners.each do |block|
         
     | 
| 
       84 
     | 
    
         
            -
                      block.call self
         
     | 
| 
       85 
     | 
    
         
            -
                    end
         
     | 
| 
       86 
     | 
    
         
            -
                  end
         
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
64 
     | 
    
         
             
                  def build_default_connection
         
     | 
| 
       89 
65 
     | 
    
         
             
                    if !defined?(@connection_info)
         
     | 
| 
       90 
66 
     | 
    
         
             
                      nil
         
     | 
| 
         @@ -16,6 +16,7 @@ require "uri" 
     | 
|
| 
       16 
16 
     | 
    
         
             
            require "multi_json"
         
     | 
| 
       17 
17 
     | 
    
         
             
            require "googleauth/signet"
         
     | 
| 
       18 
18 
     | 
    
         
             
            require "googleauth/user_refresh"
         
     | 
| 
      
 19 
     | 
    
         
            +
            require "securerandom"
         
     | 
| 
       19 
20 
     | 
    
         | 
| 
       20 
21 
     | 
    
         
             
            module Google
         
     | 
| 
       21 
22 
     | 
    
         
             
              module Auth
         
     | 
| 
         @@ -54,17 +55,26 @@ module Google 
     | 
|
| 
       54 
55 
     | 
    
         
             
                  #  Authorization scope to request
         
     | 
| 
       55 
56 
     | 
    
         
             
                  # @param [Google::Auth::Stores::TokenStore] token_store
         
     | 
| 
       56 
57 
     | 
    
         
             
                  #  Backing storage for persisting user credentials
         
     | 
| 
       57 
     | 
    
         
            -
                  # @param [String]  
     | 
| 
      
 58 
     | 
    
         
            +
                  # @param [String] legacy_callback_uri
         
     | 
| 
       58 
59 
     | 
    
         
             
                  #  URL (either absolute or relative) of the auth callback.
         
     | 
| 
       59 
     | 
    
         
            -
                  #  Defaults to '/oauth2callback'
         
     | 
| 
       60 
     | 
    
         
            -
                   
     | 
| 
      
 60 
     | 
    
         
            +
                  #  Defaults to '/oauth2callback'.
         
     | 
| 
      
 61 
     | 
    
         
            +
                  #  @deprecated This field is deprecated. Instead, use the keyword
         
     | 
| 
      
 62 
     | 
    
         
            +
                  #   argument callback_uri.
         
     | 
| 
      
 63 
     | 
    
         
            +
                  # @param [String] code_verifier
         
     | 
| 
      
 64 
     | 
    
         
            +
                  #  Random string of 43-128 chars used to verify the key exchange using
         
     | 
| 
      
 65 
     | 
    
         
            +
                  #  PKCE.
         
     | 
| 
      
 66 
     | 
    
         
            +
                  def initialize client_id, scope, token_store,
         
     | 
| 
      
 67 
     | 
    
         
            +
                                 legacy_callback_uri = nil,
         
     | 
| 
      
 68 
     | 
    
         
            +
                                 callback_uri: nil,
         
     | 
| 
      
 69 
     | 
    
         
            +
                                 code_verifier: nil
         
     | 
| 
       61 
70 
     | 
    
         
             
                    raise NIL_CLIENT_ID_ERROR if client_id.nil?
         
     | 
| 
       62 
71 
     | 
    
         
             
                    raise NIL_SCOPE_ERROR if scope.nil?
         
     | 
| 
       63 
72 
     | 
    
         | 
| 
       64 
73 
     | 
    
         
             
                    @client_id = client_id
         
     | 
| 
       65 
74 
     | 
    
         
             
                    @scope = Array(scope)
         
     | 
| 
       66 
75 
     | 
    
         
             
                    @token_store = token_store
         
     | 
| 
       67 
     | 
    
         
            -
                    @callback_uri = callback_uri || "/oauth2callback"
         
     | 
| 
      
 76 
     | 
    
         
            +
                    @callback_uri = legacy_callback_uri || callback_uri || "/oauth2callback"
         
     | 
| 
      
 77 
     | 
    
         
            +
                    @code_verifier = code_verifier
         
     | 
| 
       68 
78 
     | 
    
         
             
                  end
         
     | 
| 
       69 
79 
     | 
    
         | 
| 
       70 
80 
     | 
    
         
             
                  # Build the URL for requesting authorization.
         
     | 
| 
         @@ -80,14 +90,29 @@ module Google 
     | 
|
| 
       80 
90 
     | 
    
         
             
                  # @param [String, Array<String>] scope
         
     | 
| 
       81 
91 
     | 
    
         
             
                  #  Authorization scope to request. Overrides the instance scopes if not
         
     | 
| 
       82 
92 
     | 
    
         
             
                  #  nil.
         
     | 
| 
      
 93 
     | 
    
         
            +
                  # @param [Hash] additional_parameters
         
     | 
| 
      
 94 
     | 
    
         
            +
                  #  Additional query parameters to be added to the authorization URL.
         
     | 
| 
       83 
95 
     | 
    
         
             
                  # @return [String]
         
     | 
| 
       84 
96 
     | 
    
         
             
                  #  Authorization url
         
     | 
| 
       85 
97 
     | 
    
         
             
                  def get_authorization_url options = {}
         
     | 
| 
       86 
98 
     | 
    
         
             
                    scope = options[:scope] || @scope
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                    options[:additional_parameters] ||= {}
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                    if @code_verifier
         
     | 
| 
      
 103 
     | 
    
         
            +
                      options[:additional_parameters].merge!(
         
     | 
| 
      
 104 
     | 
    
         
            +
                        {
         
     | 
| 
      
 105 
     | 
    
         
            +
                          code_challenge: generate_code_challenge(@code_verifier),
         
     | 
| 
      
 106 
     | 
    
         
            +
                          code_challenge_method: code_challenge_method
         
     | 
| 
      
 107 
     | 
    
         
            +
                        }
         
     | 
| 
      
 108 
     | 
    
         
            +
                      )
         
     | 
| 
      
 109 
     | 
    
         
            +
                    end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
       87 
111 
     | 
    
         
             
                    credentials = UserRefreshCredentials.new(
         
     | 
| 
       88 
112 
     | 
    
         
             
                      client_id:     @client_id.id,
         
     | 
| 
       89 
113 
     | 
    
         
             
                      client_secret: @client_id.secret,
         
     | 
| 
       90 
     | 
    
         
            -
                      scope:         scope
         
     | 
| 
      
 114 
     | 
    
         
            +
                      scope:         scope,
         
     | 
| 
      
 115 
     | 
    
         
            +
                      additional_parameters: options[:additional_parameters]
         
     | 
| 
       91 
116 
     | 
    
         
             
                    )
         
     | 
| 
       92 
117 
     | 
    
         
             
                    redirect_uri = redirect_uri_for options[:base_url]
         
     | 
| 
       93 
118 
     | 
    
         
             
                    url = credentials.authorization_uri(access_type:            "offline",
         
     | 
| 
         @@ -144,6 +169,9 @@ module Google 
     | 
|
| 
       144 
169 
     | 
    
         
             
                  #  Absolute URL to resolve the configured callback uri against.
         
     | 
| 
       145 
170 
     | 
    
         
             
                  #  Required if the configured
         
     | 
| 
       146 
171 
     | 
    
         
             
                  #  callback uri is a relative.
         
     | 
| 
      
 172 
     | 
    
         
            +
                  # @param [Hash] additional_parameters
         
     | 
| 
      
 173 
     | 
    
         
            +
                  #  Additional parameters to be added to the post body of token
         
     | 
| 
      
 174 
     | 
    
         
            +
                  #  endpoint request.
         
     | 
| 
       147 
175 
     | 
    
         
             
                  # @return [Google::Auth::UserRefreshCredentials]
         
     | 
| 
       148 
176 
     | 
    
         
             
                  #  Credentials if exchange is successful
         
     | 
| 
       149 
177 
     | 
    
         
             
                  def get_credentials_from_code options = {}
         
     | 
| 
         @@ -151,11 +179,14 @@ module Google 
     | 
|
| 
       151 
179 
     | 
    
         
             
                    code = options[:code]
         
     | 
| 
       152 
180 
     | 
    
         
             
                    scope = options[:scope] || @scope
         
     | 
| 
       153 
181 
     | 
    
         
             
                    base_url = options[:base_url]
         
     | 
| 
      
 182 
     | 
    
         
            +
                    options[:additional_parameters] ||= {}
         
     | 
| 
      
 183 
     | 
    
         
            +
                    options[:additional_parameters].merge!({ code_verifier: @code_verifier })
         
     | 
| 
       154 
184 
     | 
    
         
             
                    credentials = UserRefreshCredentials.new(
         
     | 
| 
       155 
     | 
    
         
            -
                      client_id: 
     | 
| 
       156 
     | 
    
         
            -
                      client_secret: 
     | 
| 
       157 
     | 
    
         
            -
                      redirect_uri: 
     | 
| 
       158 
     | 
    
         
            -
                      scope: 
     | 
| 
      
 185 
     | 
    
         
            +
                      client_id:             @client_id.id,
         
     | 
| 
      
 186 
     | 
    
         
            +
                      client_secret:         @client_id.secret,
         
     | 
| 
      
 187 
     | 
    
         
            +
                      redirect_uri:          redirect_uri_for(base_url),
         
     | 
| 
      
 188 
     | 
    
         
            +
                      scope:                 scope,
         
     | 
| 
      
 189 
     | 
    
         
            +
                      additional_parameters: options[:additional_parameters]
         
     | 
| 
       159 
190 
     | 
    
         
             
                    )
         
     | 
| 
       160 
191 
     | 
    
         
             
                    credentials.code = code
         
     | 
| 
       161 
192 
     | 
    
         
             
                    credentials.fetch_access_token!({})
         
     | 
| 
         @@ -221,6 +252,23 @@ module Google 
     | 
|
| 
       221 
252 
     | 
    
         
             
                    credentials
         
     | 
| 
       222 
253 
     | 
    
         
             
                  end
         
     | 
| 
       223 
254 
     | 
    
         | 
| 
      
 255 
     | 
    
         
            +
                  # The code verifier for PKCE for OAuth 2.0. When set, the
         
     | 
| 
      
 256 
     | 
    
         
            +
                  # authorization URI will contain the Code Challenge and Code
         
     | 
| 
      
 257 
     | 
    
         
            +
                  # Challenge Method querystring parameters, and the token URI will
         
     | 
| 
      
 258 
     | 
    
         
            +
                  # contain the Code Verifier parameter.
         
     | 
| 
      
 259 
     | 
    
         
            +
                  #
         
     | 
| 
      
 260 
     | 
    
         
            +
                  # @param [String|nil] new_code_erifier
         
     | 
| 
      
 261 
     | 
    
         
            +
                  def code_verifier= new_code_verifier
         
     | 
| 
      
 262 
     | 
    
         
            +
                    @code_verifier = new_code_verifier
         
     | 
| 
      
 263 
     | 
    
         
            +
                  end
         
     | 
| 
      
 264 
     | 
    
         
            +
             
     | 
| 
      
 265 
     | 
    
         
            +
                  # Generate the code verifier needed to be sent while fetching
         
     | 
| 
      
 266 
     | 
    
         
            +
                  # authorization URL.
         
     | 
| 
      
 267 
     | 
    
         
            +
                  def self.generate_code_verifier
         
     | 
| 
      
 268 
     | 
    
         
            +
                    random_number = rand 32..96
         
     | 
| 
      
 269 
     | 
    
         
            +
                    SecureRandom.alphanumeric random_number
         
     | 
| 
      
 270 
     | 
    
         
            +
                  end
         
     | 
| 
      
 271 
     | 
    
         
            +
             
     | 
| 
       224 
272 
     | 
    
         
             
                  private
         
     | 
| 
       225 
273 
     | 
    
         | 
| 
       226 
274 
     | 
    
         
             
                  # @private Fetch stored token with given user_id
         
     | 
| 
         @@ -265,6 +313,15 @@ module Google 
     | 
|
| 
       265 
313 
     | 
    
         
             
                  def uri_is_postmessage? uri
         
     | 
| 
       266 
314 
     | 
    
         
             
                    uri.to_s.casecmp("postmessage").zero?
         
     | 
| 
       267 
315 
     | 
    
         
             
                  end
         
     | 
| 
      
 316 
     | 
    
         
            +
             
     | 
| 
      
 317 
     | 
    
         
            +
                  def generate_code_challenge code_verifier
         
     | 
| 
      
 318 
     | 
    
         
            +
                    digest = Digest::SHA256.digest code_verifier
         
     | 
| 
      
 319 
     | 
    
         
            +
                    Base64.urlsafe_encode64 digest, padding: false
         
     | 
| 
      
 320 
     | 
    
         
            +
                  end
         
     | 
| 
      
 321 
     | 
    
         
            +
             
     | 
| 
      
 322 
     | 
    
         
            +
                  def code_challenge_method
         
     | 
| 
      
 323 
     | 
    
         
            +
                    "S256"
         
     | 
| 
      
 324 
     | 
    
         
            +
                  end
         
     | 
| 
       268 
325 
     | 
    
         
             
                end
         
     | 
| 
       269 
326 
     | 
    
         
             
              end
         
     | 
| 
       270 
327 
     | 
    
         
             
            end
         
     | 
| 
         @@ -50,7 +50,8 @@ module Google 
     | 
|
| 
       50 
50 
     | 
    
         
             
                      "client_secret" => ENV[CredentialsLoader::CLIENT_SECRET_VAR],
         
     | 
| 
       51 
51 
     | 
    
         
             
                      "refresh_token" => ENV[CredentialsLoader::REFRESH_TOKEN_VAR],
         
     | 
| 
       52 
52 
     | 
    
         
             
                      "project_id"    => ENV[CredentialsLoader::PROJECT_ID_VAR],
         
     | 
| 
       53 
     | 
    
         
            -
                      "quota_project_id" => nil
         
     | 
| 
      
 53 
     | 
    
         
            +
                      "quota_project_id" => nil,
         
     | 
| 
      
 54 
     | 
    
         
            +
                      "universe_domain" => nil
         
     | 
| 
       54 
55 
     | 
    
         
             
                    }
         
     | 
| 
       55 
56 
     | 
    
         
             
                    new(token_credential_uri: TOKEN_CRED_URI,
         
     | 
| 
       56 
57 
     | 
    
         
             
                        client_id:            user_creds["client_id"],
         
     | 
| 
         @@ -58,7 +59,8 @@ module Google 
     | 
|
| 
       58 
59 
     | 
    
         
             
                        refresh_token:        user_creds["refresh_token"],
         
     | 
| 
       59 
60 
     | 
    
         
             
                        project_id:           user_creds["project_id"],
         
     | 
| 
       60 
61 
     | 
    
         
             
                        quota_project_id:     user_creds["quota_project_id"],
         
     | 
| 
       61 
     | 
    
         
            -
                        scope:                scope 
     | 
| 
      
 62 
     | 
    
         
            +
                        scope:                scope,
         
     | 
| 
      
 63 
     | 
    
         
            +
                        universe_domain:      user_creds["universe_domain"] || "googleapis.com")
         
     | 
| 
       62 
64 
     | 
    
         
             
                      .configure_connection(options)
         
     | 
| 
       63 
65 
     | 
    
         
             
                  end
         
     | 
| 
       64 
66 
     | 
    
         | 
    
        data/lib/googleauth/version.rb
    CHANGED
    
    
| 
         @@ -93,11 +93,22 @@ module Google 
     | 
|
| 
       93 
93 
     | 
    
         
             
                  #  Authorization scope to request
         
     | 
| 
       94 
94 
     | 
    
         
             
                  # @param [Google::Auth::Stores::TokenStore] token_store
         
     | 
| 
       95 
95 
     | 
    
         
             
                  #  Backing storage for persisting user credentials
         
     | 
| 
       96 
     | 
    
         
            -
                  # @param [String]  
     | 
| 
      
 96 
     | 
    
         
            +
                  # @param [String] legacy_callback_uri
         
     | 
| 
       97 
97 
     | 
    
         
             
                  #  URL (either absolute or relative) of the auth callback. Defaults
         
     | 
| 
       98 
     | 
    
         
            -
                  #  to '/oauth2callback'
         
     | 
| 
       99 
     | 
    
         
            -
                   
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
      
 98 
     | 
    
         
            +
                  #  to '/oauth2callback'.
         
     | 
| 
      
 99 
     | 
    
         
            +
                  #  @deprecated This field is deprecated. Instead, use the keyword
         
     | 
| 
      
 100 
     | 
    
         
            +
                  #   argument callback_uri.
         
     | 
| 
      
 101 
     | 
    
         
            +
                  # @param [String] code_verifier
         
     | 
| 
      
 102 
     | 
    
         
            +
                  #  Random string of 43-128 chars used to verify the key exchange using
         
     | 
| 
      
 103 
     | 
    
         
            +
                  #  PKCE.
         
     | 
| 
      
 104 
     | 
    
         
            +
                  def initialize client_id, scope, token_store,
         
     | 
| 
      
 105 
     | 
    
         
            +
                                 legacy_callback_uri = nil,
         
     | 
| 
      
 106 
     | 
    
         
            +
                                 callback_uri: nil,
         
     | 
| 
      
 107 
     | 
    
         
            +
                                 code_verifier: nil
         
     | 
| 
      
 108 
     | 
    
         
            +
                    super client_id, scope, token_store,
         
     | 
| 
      
 109 
     | 
    
         
            +
                          legacy_callback_uri,
         
     | 
| 
      
 110 
     | 
    
         
            +
                          code_verifier: code_verifier,
         
     | 
| 
      
 111 
     | 
    
         
            +
                          callback_uri: callback_uri
         
     | 
| 
       101 
112 
     | 
    
         
             
                  end
         
     | 
| 
       102 
113 
     | 
    
         | 
| 
       103 
114 
     | 
    
         
             
                  # Handle the result of the oauth callback. Exchanges the authorization
         
     | 
| 
         @@ -192,13 +203,13 @@ module Google 
     | 
|
| 
       192 
203 
     | 
    
         
             
                  end
         
     | 
| 
       193 
204 
     | 
    
         | 
| 
       194 
205 
     | 
    
         
             
                  def self.extract_callback_state request
         
     | 
| 
       195 
     | 
    
         
            -
                    state = MultiJson.load(request[STATE_PARAM] || "{}")
         
     | 
| 
      
 206 
     | 
    
         
            +
                    state = MultiJson.load(request.params[STATE_PARAM] || "{}")
         
     | 
| 
       196 
207 
     | 
    
         
             
                    redirect_uri = state[CURRENT_URI_KEY]
         
     | 
| 
       197 
208 
     | 
    
         
             
                    callback_state = {
         
     | 
| 
       198 
     | 
    
         
            -
                      AUTH_CODE_KEY  => request[AUTH_CODE_KEY],
         
     | 
| 
       199 
     | 
    
         
            -
                      ERROR_CODE_KEY => request[ERROR_CODE_KEY],
         
     | 
| 
      
 209 
     | 
    
         
            +
                      AUTH_CODE_KEY  => request.params[AUTH_CODE_KEY],
         
     | 
| 
      
 210 
     | 
    
         
            +
                      ERROR_CODE_KEY => request.params[ERROR_CODE_KEY],
         
     | 
| 
       200 
211 
     | 
    
         
             
                      SESSION_ID_KEY => state[SESSION_ID_KEY],
         
     | 
| 
       201 
     | 
    
         
            -
                      SCOPE_KEY      => request[SCOPE_KEY]
         
     | 
| 
      
 212 
     | 
    
         
            +
                      SCOPE_KEY      => request.params[SCOPE_KEY]
         
     | 
| 
       202 
213 
     | 
    
         
             
                    }
         
     | 
| 
       203 
214 
     | 
    
         
             
                    [callback_state, redirect_uri]
         
     | 
| 
       204 
215 
     | 
    
         
             
                  end
         
     |