signet 0.11.0 → 0.14.1
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 +51 -15
- data/Gemfile +5 -4
- data/README.md +4 -6
- data/Rakefile +107 -37
- data/lib/signet.rb +17 -14
- data/lib/signet/errors.rb +4 -4
- data/lib/signet/oauth_1.rb +129 -154
- data/lib/signet/oauth_1/client.rb +309 -343
- data/lib/signet/oauth_1/credential.rb +40 -37
- data/lib/signet/oauth_1/server.rb +197 -203
- data/lib/signet/oauth_1/signature_methods/hmac_sha1.rb +11 -10
- data/lib/signet/oauth_1/signature_methods/plaintext.rb +8 -7
- data/lib/signet/oauth_1/signature_methods/rsa_sha1.rb +11 -11
- data/lib/signet/oauth_2.rb +41 -43
- data/lib/signet/oauth_2/client.rb +328 -313
- data/lib/signet/version.rb +2 -73
- data/signet.gemspec +37 -39
- data/spec/signet/oauth_1/client_spec.rb +313 -315
- data/spec/signet/oauth_1/credential_spec.rb +64 -56
- data/spec/signet/oauth_1/server_spec.rb +362 -362
- data/spec/signet/oauth_1/signature_methods/hmac_sha1_spec.rb +26 -26
- data/spec/signet/oauth_1/signature_methods/plaintext_spec.rb +28 -28
- data/spec/signet/oauth_1/signature_methods/rsa_sha1_spec.rb +34 -35
- data/spec/signet/oauth_1_spec.rb +553 -524
- data/spec/signet/oauth_2/client_spec.rb +652 -576
- data/spec/signet/oauth_2_spec.rb +88 -89
- data/spec/signet_spec.rb +41 -41
- data/spec/spec_helper.rb +7 -7
- data/spec/spec_helper_spec.rb +8 -8
- metadata +64 -52
- data/tasks/clobber.rake +0 -2
- data/tasks/gem.rake +0 -34
- data/tasks/git.rake +0 -40
- data/tasks/metrics.rake +0 -41
- data/tasks/spec.rake +0 -34
- data/tasks/wiki.rake +0 -38
- data/tasks/yard.rake +0 -21
| @@ -1,23 +1,24 @@ | |
| 1 | 
            -
            require  | 
| 2 | 
            -
            require  | 
| 1 | 
            +
            require "openssl"
         | 
| 2 | 
            +
            require "signet"
         | 
| 3 3 |  | 
| 4 4 | 
             
            module Signet #:nodoc:
         | 
| 5 5 | 
             
              module OAuth1
         | 
| 6 6 | 
             
                module HMACSHA1
         | 
| 7 | 
            -
                  def self.generate_signature | 
| 8 | 
            -
                      base_string, client_credential_secret, token_credential_secret | 
| 7 | 
            +
                  def self.generate_signature \
         | 
| 8 | 
            +
                      base_string, client_credential_secret, token_credential_secret
         | 
| 9 | 
            +
             | 
| 9 10 | 
             
                    # Both the client secret and token secret must be escaped
         | 
| 10 11 | 
             
                    client_credential_secret =
         | 
| 11 | 
            -
                      Signet::OAuth1.encode | 
| 12 | 
            +
                      Signet::OAuth1.encode client_credential_secret
         | 
| 12 13 | 
             
                    token_credential_secret =
         | 
| 13 | 
            -
                      Signet::OAuth1.encode | 
| 14 | 
            +
                      Signet::OAuth1.encode token_credential_secret
         | 
| 14 15 | 
             
                    # The key for the signature is just the client secret and token
         | 
| 15 16 | 
             
                    # secret joined by the '&' character.  If the token secret is omitted,
         | 
| 16 17 | 
             
                    # the '&' must still be present.
         | 
| 17 | 
            -
                    key = [client_credential_secret, token_credential_secret].join | 
| 18 | 
            -
                     | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 18 | 
            +
                    key = [client_credential_secret, token_credential_secret].join "&"
         | 
| 19 | 
            +
                    Base64.encode64(OpenSSL::HMAC.digest(
         | 
| 20 | 
            +
                                      OpenSSL::Digest.new("sha1"), key, base_string
         | 
| 21 | 
            +
                                    )).strip
         | 
| 21 22 | 
             
                  end
         | 
| 22 23 | 
             
                end
         | 
| 23 24 | 
             
              end
         | 
| @@ -1,20 +1,21 @@ | |
| 1 | 
            -
            require  | 
| 1 | 
            +
            require "signet"
         | 
| 2 2 |  | 
| 3 3 | 
             
            module Signet #:nodoc:
         | 
| 4 4 | 
             
              module OAuth1
         | 
| 5 5 | 
             
                module PLAINTEXT
         | 
| 6 | 
            -
                  def self.generate_signature | 
| 7 | 
            -
                       | 
| 6 | 
            +
                  def self.generate_signature \
         | 
| 7 | 
            +
                      _base_string, client_credential_secret, token_credential_secret
         | 
| 8 | 
            +
             | 
| 8 9 | 
             
                    # Both the client secret and token secret must be escaped
         | 
| 9 10 | 
             
                    client_credential_secret =
         | 
| 10 | 
            -
             | 
| 11 | 
            +
                      Signet::OAuth1.encode client_credential_secret
         | 
| 11 12 | 
             
                    token_credential_secret =
         | 
| 12 | 
            -
             | 
| 13 | 
            +
                      Signet::OAuth1.encode token_credential_secret
         | 
| 13 14 | 
             
                    # The key for the signature is just the client secret and token
         | 
| 14 15 | 
             
                    # secret joined by the '&' character.  If the token secret is omitted,
         | 
| 15 16 | 
             
                    # the '&' must still be present.
         | 
| 16 | 
            -
                    key = [client_credential_secret, token_credential_secret].join | 
| 17 | 
            -
                     | 
| 17 | 
            +
                    key = [client_credential_secret, token_credential_secret].join "&"
         | 
| 18 | 
            +
                    Signet::OAuth1.encode(key).strip
         | 
| 18 19 | 
             
                  end
         | 
| 19 20 | 
             
                end
         | 
| 20 21 | 
             
              end
         | 
| @@ -1,20 +1,20 @@ | |
| 1 | 
            -
            require  | 
| 2 | 
            -
            require  | 
| 3 | 
            -
            require  | 
| 4 | 
            -
            require  | 
| 1 | 
            +
            require "digest/sha1"
         | 
| 2 | 
            +
            require "base64"
         | 
| 3 | 
            +
            require "openssl"
         | 
| 4 | 
            +
            require "signet"
         | 
| 5 5 |  | 
| 6 6 | 
             
            module Signet #:nodoc:
         | 
| 7 7 | 
             
              module OAuth1
         | 
| 8 8 | 
             
                module RSASHA1
         | 
| 9 | 
            -
                  def self.generate_signature | 
| 10 | 
            -
                      base_string, client_credential_secret,  | 
| 9 | 
            +
                  def self.generate_signature \
         | 
| 10 | 
            +
                      base_string, client_credential_secret, _token_credential_secret
         | 
| 11 11 |  | 
| 12 | 
            -
                    private_key = OpenSSL::PKey::RSA.new(client_credential_secret)
         | 
| 13 | 
            -
                    signature = private_key.sign(OpenSSL::Digest::SHA1.new, base_string)
         | 
| 14 | 
            -
                    #using strict_encode64 because the encode64 method adds newline characters after ever 60 chars
         | 
| 15 | 
            -
                    return Base64.strict_encode64(signature).strip
         | 
| 16 | 
            -
                  end
         | 
| 17 12 |  | 
| 13 | 
            +
                    private_key = OpenSSL::PKey::RSA.new client_credential_secret
         | 
| 14 | 
            +
                    signature = private_key.sign OpenSSL::Digest::SHA1.new, base_string
         | 
| 15 | 
            +
                    # using strict_encode64 because the encode64 method adds newline characters after ever 60 chars
         | 
| 16 | 
            +
                    Base64.strict_encode64(signature).strip
         | 
| 17 | 
            +
                  end
         | 
| 18 18 | 
             
                end
         | 
| 19 19 | 
             
              end
         | 
| 20 20 | 
             
            end
         | 
    
        data/lib/signet/oauth_2.rb
    CHANGED
    
    | @@ -12,9 +12,9 @@ | |
| 12 12 | 
             
            #    See the License for the specific language governing permissions and
         | 
| 13 13 | 
             
            #    limitations under the License.
         | 
| 14 14 |  | 
| 15 | 
            -
            require  | 
| 16 | 
            -
            require  | 
| 17 | 
            -
            require  | 
| 15 | 
            +
            require "base64"
         | 
| 16 | 
            +
            require "signet"
         | 
| 17 | 
            +
            require "multi_json"
         | 
| 18 18 |  | 
| 19 19 | 
             
            module Signet #:nodoc:
         | 
| 20 20 | 
             
              ##
         | 
| @@ -23,63 +23,61 @@ module Signet #:nodoc: | |
| 23 23 | 
             
              # This module will be updated periodically to support newer drafts of the
         | 
| 24 24 | 
             
              # specification, as they become widely deployed.
         | 
| 25 25 | 
             
              module OAuth2
         | 
| 26 | 
            -
                def self.parse_authorization_header | 
| 26 | 
            +
                def self.parse_authorization_header field_value
         | 
| 27 27 | 
             
                  auth_scheme = field_value[/^([-._0-9a-zA-Z]+)/, 1]
         | 
| 28 28 | 
             
                  case auth_scheme
         | 
| 29 29 | 
             
                  when /^Basic$/i
         | 
| 30 30 | 
             
                    # HTTP Basic is allowed in OAuth 2
         | 
| 31 | 
            -
                    return  | 
| 31 | 
            +
                    return parse_basic_credentials(field_value[/^Basic\s+(.*)$/i, 1])
         | 
| 32 32 | 
             
                  when /^OAuth$/i
         | 
| 33 33 | 
             
                    # Other token types may be supported eventually
         | 
| 34 | 
            -
                    return  | 
| 34 | 
            +
                    return parse_bearer_credentials(field_value[/^OAuth\s+(.*)$/i, 1])
         | 
| 35 35 | 
             
                  else
         | 
| 36 36 | 
             
                    raise ParseError,
         | 
| 37 | 
            -
             | 
| 37 | 
            +
                          "Parsing non-OAuth Authorization headers is out of scope."
         | 
| 38 38 | 
             
                  end
         | 
| 39 39 | 
             
                end
         | 
| 40 40 |  | 
| 41 | 
            -
                def self.parse_www_authenticate_header | 
| 41 | 
            +
                def self.parse_www_authenticate_header field_value
         | 
| 42 42 | 
             
                  auth_scheme = field_value[/^([-._0-9a-zA-Z]+)/, 1]
         | 
| 43 43 | 
             
                  case auth_scheme
         | 
| 44 44 | 
             
                  when /^OAuth$/i
         | 
| 45 45 | 
             
                    # Other token types may be supported eventually
         | 
| 46 | 
            -
                    return  | 
| 46 | 
            +
                    return parse_oauth_challenge(field_value[/^OAuth\s+(.*)$/i, 1])
         | 
| 47 47 | 
             
                  else
         | 
| 48 48 | 
             
                    raise ParseError,
         | 
| 49 | 
            -
             | 
| 49 | 
            +
                          "Parsing non-OAuth WWW-Authenticate headers is out of scope."
         | 
| 50 50 | 
             
                  end
         | 
| 51 51 | 
             
                end
         | 
| 52 52 |  | 
| 53 | 
            -
                def self.parse_basic_credentials | 
| 54 | 
            -
                  decoded = Base64.decode64 | 
| 55 | 
            -
                  client_id, client_secret = decoded.split | 
| 56 | 
            -
                   | 
| 53 | 
            +
                def self.parse_basic_credentials credential_string
         | 
| 54 | 
            +
                  decoded = Base64.decode64 credential_string
         | 
| 55 | 
            +
                  client_id, client_secret = decoded.split ":", 2
         | 
| 56 | 
            +
                  [["client_id", client_id], ["client_secret", client_secret]]
         | 
| 57 57 | 
             
                end
         | 
| 58 58 |  | 
| 59 | 
            -
                def self.parse_bearer_credentials | 
| 59 | 
            +
                def self.parse_bearer_credentials credential_string
         | 
| 60 60 | 
             
                  access_token = credential_string[/^([^,\s]+)(?:\s|,|$)/i, 1]
         | 
| 61 61 | 
             
                  parameters = []
         | 
| 62 | 
            -
                  parameters << [ | 
| 62 | 
            +
                  parameters << ["access_token", access_token]
         | 
| 63 63 | 
             
                  auth_param_string = credential_string[/^(?:[^,\s]+)\s*,\s*(.*)$/i, 1]
         | 
| 64 64 | 
             
                  if auth_param_string
         | 
| 65 65 | 
             
                    # This code will rarely get called, but is included for completeness
         | 
| 66 | 
            -
                    parameters.concat | 
| 66 | 
            +
                    parameters.concat Signet.parse_auth_param_list(auth_param_string)
         | 
| 67 67 | 
             
                  end
         | 
| 68 | 
            -
                   | 
| 68 | 
            +
                  parameters
         | 
| 69 69 | 
             
                end
         | 
| 70 70 |  | 
| 71 | 
            -
                def self.parse_oauth_challenge | 
| 72 | 
            -
                   | 
| 71 | 
            +
                def self.parse_oauth_challenge challenge_string
         | 
| 72 | 
            +
                  Signet.parse_auth_param_list challenge_string
         | 
| 73 73 | 
             
                end
         | 
| 74 74 |  | 
| 75 | 
            -
                def self.parse_credentials | 
| 76 | 
            -
                   | 
| 77 | 
            -
                    raise TypeError, "Expected String, got #{body.class}."
         | 
| 78 | 
            -
                  end
         | 
| 75 | 
            +
                def self.parse_credentials body, content_type
         | 
| 76 | 
            +
                  raise TypeError, "Expected String, got #{body.class}." unless body.is_a? String
         | 
| 79 77 | 
             
                  case content_type
         | 
| 80 | 
            -
                  when  | 
| 81 | 
            -
                    return MultiJson.load | 
| 82 | 
            -
                  when  | 
| 78 | 
            +
                  when %r{^application/json.*}
         | 
| 79 | 
            +
                    return MultiJson.load body
         | 
| 80 | 
            +
                  when %r{^application/x-www-form-urlencoded.*}
         | 
| 83 81 | 
             
                    return Hash[Addressable::URI.form_unencode(body)]
         | 
| 84 82 | 
             
                  else
         | 
| 85 83 | 
             
                    raise ArgumentError, "Invalid content type '#{content_type}'"
         | 
| @@ -97,14 +95,14 @@ module Signet #:nodoc: | |
| 97 95 | 
             
                #
         | 
| 98 96 | 
             
                # @return [String]
         | 
| 99 97 | 
             
                #   The value for the HTTP Basic Authorization header.
         | 
| 100 | 
            -
                def self.generate_basic_authorization_header | 
| 98 | 
            +
                def self.generate_basic_authorization_header client_id, client_password
         | 
| 101 99 | 
             
                  if client_id =~ /:/
         | 
| 102 100 | 
             
                    raise ArgumentError,
         | 
| 103 | 
            -
             | 
| 101 | 
            +
                          "A client identifier may not contain a ':' character."
         | 
| 104 102 | 
             
                  end
         | 
| 105 | 
            -
                   | 
| 106 | 
            -
                    client_id +  | 
| 107 | 
            -
                  ). | 
| 103 | 
            +
                  "Basic " + Base64.encode64(
         | 
| 104 | 
            +
                    client_id + ":" + client_password
         | 
| 105 | 
            +
                  ).delete("\n")
         | 
| 108 106 | 
             
                end
         | 
| 109 107 |  | 
| 110 108 | 
             
                ##
         | 
| @@ -117,19 +115,19 @@ module Signet #:nodoc: | |
| 117 115 | 
             
                #
         | 
| 118 116 | 
             
                # @return [String]
         | 
| 119 117 | 
             
                #   The value for the HTTP Basic Authorization header.
         | 
| 120 | 
            -
                def self.generate_bearer_authorization_header | 
| 121 | 
            -
             | 
| 118 | 
            +
                def self.generate_bearer_authorization_header \
         | 
| 119 | 
            +
                  access_token, auth_params = nil
         | 
| 120 | 
            +
             | 
| 122 121 | 
             
                  # TODO: escaping?
         | 
| 123 122 | 
             
                  header = "Bearer #{access_token}"
         | 
| 124 123 | 
             
                  if auth_params && !auth_params.empty?
         | 
| 125 124 | 
             
                    header += (", " +
         | 
| 126 | 
            -
                      (auth_params. | 
| 125 | 
            +
                      (auth_params.each_with_object [] do |(key, value), accu|
         | 
| 127 126 | 
             
                        accu << "#{key}=\"#{value}\""
         | 
| 128 | 
            -
                        accu
         | 
| 129 127 | 
             
                      end).join(", ")
         | 
| 130 | 
            -
             | 
| 128 | 
            +
                              )
         | 
| 131 129 | 
             
                  end
         | 
| 132 | 
            -
                   | 
| 130 | 
            +
                  header
         | 
| 133 131 | 
             
                end
         | 
| 134 132 |  | 
| 135 133 | 
             
                ##
         | 
| @@ -140,15 +138,15 @@ module Signet #:nodoc: | |
| 140 138 | 
             
                #   The base authorization endpoint URI.
         | 
| 141 139 | 
             
                #
         | 
| 142 140 | 
             
                # @return [String] The authorization URI to redirect the user to.
         | 
| 143 | 
            -
                def self.generate_authorization_uri | 
| 144 | 
            -
                   | 
| 145 | 
            -
                    parameters.delete | 
| 141 | 
            +
                def self.generate_authorization_uri authorization_uri, parameters = {}
         | 
| 142 | 
            +
                  parameters.each do |key, value|
         | 
| 143 | 
            +
                    parameters.delete key if value.nil?
         | 
| 146 144 | 
             
                  end
         | 
| 147 145 | 
             
                  parsed_uri = Addressable::URI.parse(authorization_uri).dup
         | 
| 148 146 | 
             
                  query_values = parsed_uri.query_values || {}
         | 
| 149 | 
            -
                  query_values = query_values.merge | 
| 147 | 
            +
                  query_values = query_values.merge parameters
         | 
| 150 148 | 
             
                  parsed_uri.query_values = query_values
         | 
| 151 | 
            -
                   | 
| 149 | 
            +
                  parsed_uri.normalize.to_s
         | 
| 152 150 | 
             
                end
         | 
| 153 151 | 
             
              end
         | 
| 154 152 | 
             
            end
         | 
| @@ -12,20 +12,19 @@ | |
| 12 12 | 
             
            #    See the License for the specific language governing permissions and
         | 
| 13 13 | 
             
            #    limitations under the License.
         | 
| 14 14 |  | 
| 15 | 
            -
            require  | 
| 16 | 
            -
            require  | 
| 17 | 
            -
            require  | 
| 18 | 
            -
            require  | 
| 19 | 
            -
            require  | 
| 20 | 
            -
            require  | 
| 21 | 
            -
            require  | 
| 22 | 
            -
            require  | 
| 15 | 
            +
            require "faraday"
         | 
| 16 | 
            +
            require "stringio"
         | 
| 17 | 
            +
            require "addressable/uri"
         | 
| 18 | 
            +
            require "signet"
         | 
| 19 | 
            +
            require "signet/errors"
         | 
| 20 | 
            +
            require "signet/oauth_2"
         | 
| 21 | 
            +
            require "jwt"
         | 
| 22 | 
            +
            require "date"
         | 
| 23 23 |  | 
| 24 24 | 
             
            module Signet
         | 
| 25 25 | 
             
              module OAuth2
         | 
| 26 26 | 
             
                class Client
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                  OOB_MODES = %w(urn:ietf:wg:oauth:2.0:oob:auto urn:ietf:wg:oauth:2.0:oob oob)
         | 
| 27 | 
            +
                  OOB_MODES = ["urn:ietf:wg:oauth:2.0:oob:auto", "urn:ietf:wg:oauth:2.0:oob", "oob"].freeze
         | 
| 29 28 |  | 
| 30 29 | 
             
                  ##
         | 
| 31 30 | 
             
                  # Creates an OAuth 2.0 client.
         | 
| @@ -47,6 +46,9 @@ module Signet | |
| 47 46 | 
             
                  #   - <code>:scope</code> -
         | 
| 48 47 | 
             
                  #     The scope of the access request, expressed either as an Array
         | 
| 49 48 | 
             
                  #     or as a space-delimited String.
         | 
| 49 | 
            +
                  #   - <code>:target_audience</code> -
         | 
| 50 | 
            +
                  #     The final target audience for ID tokens fetched by this client,
         | 
| 51 | 
            +
                  #     as a String.
         | 
| 50 52 | 
             
                  #   - <code>:state</code> -
         | 
| 51 53 | 
             
                  #     An arbitrary string designed to allow the client to maintain state.
         | 
| 52 54 | 
             
                  #   - <code>:code</code> -
         | 
| @@ -89,7 +91,7 @@ module Signet | |
| 89 91 | 
             
                  #   )
         | 
| 90 92 | 
             
                  #
         | 
| 91 93 | 
             
                  # @see Signet::OAuth2::Client#update!
         | 
| 92 | 
            -
                  def initialize options={}
         | 
| 94 | 
            +
                  def initialize options = {}
         | 
| 93 95 | 
             
                    @authorization_uri    = nil
         | 
| 94 96 | 
             
                    @token_credential_uri = nil
         | 
| 95 97 | 
             
                    @client_id            = nil
         | 
| @@ -102,11 +104,15 @@ module Signet | |
| 102 104 | 
             
                    @principal            = nil
         | 
| 103 105 | 
             
                    @redirect_uri         = nil
         | 
| 104 106 | 
             
                    @scope                = nil
         | 
| 107 | 
            +
                    @target_audience      = nil
         | 
| 105 108 | 
             
                    @state                = nil
         | 
| 106 109 | 
             
                    @username             = nil
         | 
| 107 110 | 
             
                    @access_type          = nil
         | 
| 108 | 
            -
                     | 
| 111 | 
            +
                    update! options
         | 
| 109 112 | 
             
                  end
         | 
| 113 | 
            +
                  # rubocop:disable Metrics/AbcSize
         | 
| 114 | 
            +
                  # rubocop:disable Metrics/CyclomaticComplexity
         | 
| 115 | 
            +
                  # rubocop:disable Metrics/PerceivedComplexity
         | 
| 110 116 |  | 
| 111 117 | 
             
                  ##
         | 
| 112 118 | 
             
                  # Updates an OAuth 2.0 client.
         | 
| @@ -128,6 +134,9 @@ module Signet | |
| 128 134 | 
             
                  #   - <code>:scope</code> -
         | 
| 129 135 | 
             
                  #     The scope of the access request, expressed either as an Array
         | 
| 130 136 | 
             
                  #     or as a space-delimited String.
         | 
| 137 | 
            +
                  #   - <code>:target_audience</code> -
         | 
| 138 | 
            +
                  #     The final target audience for ID tokens fetched by this client,
         | 
| 139 | 
            +
                  #     as a String.
         | 
| 131 140 | 
             
                  #   - <code>:state</code> -
         | 
| 132 141 | 
             
                  #     An arbitrary string designed to allow the client to maintain state.
         | 
| 133 142 | 
             
                  #   - <code>:code</code> -
         | 
| @@ -170,32 +179,36 @@ module Signet | |
| 170 179 | 
             
                  #
         | 
| 171 180 | 
             
                  # @see Signet::OAuth2::Client#initialize
         | 
| 172 181 | 
             
                  # @see Signet::OAuth2::Client#update_token!
         | 
| 173 | 
            -
                  def update! | 
| 182 | 
            +
                  def update! options = {}
         | 
| 174 183 | 
             
                    # Normalize all keys to symbols to allow indifferent access.
         | 
| 175 | 
            -
                    options = deep_hash_normalize | 
| 176 | 
            -
             | 
| 177 | 
            -
                    self.authorization_uri = options[:authorization_uri] if options. | 
| 178 | 
            -
                    self.token_credential_uri = options[:token_credential_uri] if options. | 
| 179 | 
            -
                    self.client_id = options[:client_id] if options. | 
| 180 | 
            -
                    self.client_secret = options[:client_secret] if options. | 
| 181 | 
            -
                    self.scope = options[:scope] if options. | 
| 182 | 
            -
                    self. | 
| 183 | 
            -
                    self. | 
| 184 | 
            -
                    self. | 
| 185 | 
            -
                    self. | 
| 186 | 
            -
                    self. | 
| 187 | 
            -
                    self. | 
| 188 | 
            -
                    self. | 
| 189 | 
            -
                    self. | 
| 184 | 
            +
                    options = deep_hash_normalize options
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                    self.authorization_uri = options[:authorization_uri] if options.key? :authorization_uri
         | 
| 187 | 
            +
                    self.token_credential_uri = options[:token_credential_uri] if options.key? :token_credential_uri
         | 
| 188 | 
            +
                    self.client_id = options[:client_id] if options.key? :client_id
         | 
| 189 | 
            +
                    self.client_secret = options[:client_secret] if options.key? :client_secret
         | 
| 190 | 
            +
                    self.scope = options[:scope] if options.key? :scope
         | 
| 191 | 
            +
                    self.target_audience = options[:target_audience] if options.key? :target_audience
         | 
| 192 | 
            +
                    self.state = options[:state] if options.key? :state
         | 
| 193 | 
            +
                    self.code = options[:code] if options.key? :code
         | 
| 194 | 
            +
                    self.redirect_uri = options[:redirect_uri] if options.key? :redirect_uri
         | 
| 195 | 
            +
                    self.username = options[:username] if options.key? :username
         | 
| 196 | 
            +
                    self.password = options[:password] if options.key? :password
         | 
| 197 | 
            +
                    self.issuer = options[:issuer] if options.key? :issuer
         | 
| 198 | 
            +
                    self.person = options[:person] if options.key? :person
         | 
| 199 | 
            +
                    self.sub = options[:sub] if options.key? :sub
         | 
| 190 200 | 
             
                    self.expiry = options[:expiry] || 60
         | 
| 191 | 
            -
                    self.audience = options[:audience] if options. | 
| 192 | 
            -
                    self.signing_key = options[:signing_key] if options. | 
| 201 | 
            +
                    self.audience = options[:audience] if options.key? :audience
         | 
| 202 | 
            +
                    self.signing_key = options[:signing_key] if options.key? :signing_key
         | 
| 193 203 | 
             
                    self.extension_parameters = options[:extension_parameters] || {}
         | 
| 194 204 | 
             
                    self.additional_parameters = options[:additional_parameters] || {}
         | 
| 195 205 | 
             
                    self.access_type = options.fetch(:access_type) { :offline }
         | 
| 196 | 
            -
                     | 
| 197 | 
            -
                     | 
| 206 | 
            +
                    update_token! options
         | 
| 207 | 
            +
                    self
         | 
| 198 208 | 
             
                  end
         | 
| 209 | 
            +
                  # rubocop:enable Metrics/AbcSize
         | 
| 210 | 
            +
                  # rubocop:enable Metrics/CyclomaticComplexity
         | 
| 211 | 
            +
                  # rubocop:enable Metrics/PerceivedComplexity
         | 
| 199 212 |  | 
| 200 213 | 
             
                  ##
         | 
| 201 214 | 
             
                  # Updates an OAuth 2.0 client.
         | 
| @@ -225,29 +238,33 @@ module Signet | |
| 225 238 | 
             
                  #
         | 
| 226 239 | 
             
                  # @see Signet::OAuth2::Client#initialize
         | 
| 227 240 | 
             
                  # @see Signet::OAuth2::Client#update!
         | 
| 228 | 
            -
                  def update_token! | 
| 241 | 
            +
                  def update_token! options = {}
         | 
| 229 242 | 
             
                    # Normalize all keys to symbols to allow indifferent access internally
         | 
| 230 | 
            -
                    options = deep_hash_normalize | 
| 243 | 
            +
                    options = deep_hash_normalize options
         | 
| 231 244 |  | 
| 232 | 
            -
                    self.expires_in = options[:expires] if options. | 
| 233 | 
            -
                    self.expires_in = options[:expires_in] if options. | 
| 234 | 
            -
                    self.expires_at = options[:expires_at] if options. | 
| 245 | 
            +
                    self.expires_in = options[:expires] if options.key? :expires
         | 
| 246 | 
            +
                    self.expires_in = options[:expires_in] if options.key? :expires_in
         | 
| 247 | 
            +
                    self.expires_at = options[:expires_at] if options.key? :expires_at
         | 
| 235 248 |  | 
| 236 249 | 
             
                    # By default, the token is issued at `Time.now` when `expires_in` is
         | 
| 237 250 | 
             
                    # set, but this can be used to supply a more precise time.
         | 
| 238 | 
            -
                    self.issued_at = options[:issued_at] if options. | 
| 251 | 
            +
                    self.issued_at = options[:issued_at] if options.key? :issued_at
         | 
| 239 252 |  | 
| 240 253 | 
             
                    # Special case where we want expires_at to be relative to issued_at
         | 
| 241 | 
            -
                    if options. | 
| 254 | 
            +
                    if options.key?(:issued_at) && options.key?(:expires_in)
         | 
| 242 255 | 
             
                      set_relative_expires_at options[:issued_at], options[:expires_in]
         | 
| 243 256 | 
             
                    end
         | 
| 244 257 |  | 
| 245 | 
            -
                    self.access_token = options[:access_token] if options. | 
| 246 | 
            -
                    self.refresh_token = options[:refresh_token] if options. | 
| 247 | 
            -
                    self.id_token = options[:id_token] if options. | 
| 258 | 
            +
                    self.access_token = options[:access_token] if options.key? :access_token
         | 
| 259 | 
            +
                    self.refresh_token = options[:refresh_token] if options.key? :refresh_token
         | 
| 260 | 
            +
                    self.id_token = options[:id_token] if options.key? :id_token
         | 
| 248 261 |  | 
| 249 | 
            -
                     | 
| 262 | 
            +
                    self
         | 
| 250 263 | 
             
                  end
         | 
| 264 | 
            +
                  # rubocop:disable Metrics/AbcSize
         | 
| 265 | 
            +
                  # rubocop:disable Metrics/CyclomaticComplexity
         | 
| 266 | 
            +
                  # rubocop:disable Metrics/MethodLength
         | 
| 267 | 
            +
                  # rubocop:disable Metrics/PerceivedComplexity
         | 
| 251 268 |  | 
| 252 269 | 
             
                  ##
         | 
| 253 270 | 
             
                  # Returns the authorization URI that the user should be redirected to.
         | 
| @@ -255,34 +272,24 @@ module Signet | |
| 255 272 | 
             
                  # @return [Addressable::URI] The authorization URI.
         | 
| 256 273 | 
             
                  #
         | 
| 257 274 | 
             
                  # @see Signet::OAuth2.generate_authorization_uri
         | 
| 258 | 
            -
                  def authorization_uri | 
| 275 | 
            +
                  def authorization_uri options = {}
         | 
| 259 276 | 
             
                    # Normalize external input
         | 
| 260 | 
            -
                    options = deep_hash_normalize | 
| 277 | 
            +
                    options = deep_hash_normalize options
         | 
| 261 278 |  | 
| 262 | 
            -
                    return nil if @authorization_uri | 
| 263 | 
            -
                    unless options[:response_type]
         | 
| 264 | 
            -
             | 
| 265 | 
            -
                     | 
| 266 | 
            -
                     | 
| 267 | 
            -
                      options[:access_type] = access_type
         | 
| 268 | 
            -
                    end
         | 
| 269 | 
            -
                    options[:client_id] ||= self.client_id
         | 
| 270 | 
            -
                    options[:redirect_uri] ||= self.redirect_uri
         | 
| 279 | 
            +
                    return nil if @authorization_uri.nil?
         | 
| 280 | 
            +
                    options[:response_type] = :code unless options[:response_type]
         | 
| 281 | 
            +
                    options[:access_type] = access_type if !options[:access_type] && access_type
         | 
| 282 | 
            +
                    options[:client_id] ||= client_id
         | 
| 283 | 
            +
                    options[:redirect_uri] ||= redirect_uri
         | 
| 271 284 | 
             
                    if options[:prompt] && options[:approval_prompt]
         | 
| 272 285 | 
             
                      raise ArgumentError, "prompt and approval_prompt are mutually exclusive parameters"
         | 
| 273 286 | 
             
                    end
         | 
| 274 | 
            -
                     | 
| 275 | 
            -
             | 
| 276 | 
            -
                     | 
| 277 | 
            -
                    unless options[: | 
| 278 | 
            -
             | 
| 279 | 
            -
                     | 
| 280 | 
            -
                    if !options[:scope] && self.scope
         | 
| 281 | 
            -
                      options[:scope] = self.scope.join(' ')
         | 
| 282 | 
            -
                    end
         | 
| 283 | 
            -
                    options[:state] = self.state unless options[:state]
         | 
| 284 | 
            -
                    options.merge!(self.additional_parameters.merge(options[:additional_parameters] || {}))
         | 
| 285 | 
            -
                    options.delete(:additional_parameters)
         | 
| 287 | 
            +
                    raise ArgumentError, "Missing required client identifier." unless options[:client_id]
         | 
| 288 | 
            +
                    raise ArgumentError, "Missing required redirect URI." unless options[:redirect_uri]
         | 
| 289 | 
            +
                    options[:scope] = scope.join " " if !options[:scope] && scope
         | 
| 290 | 
            +
                    options[:state] = state unless options[:state]
         | 
| 291 | 
            +
                    options.merge!(additional_parameters.merge(options[:additional_parameters] || {}))
         | 
| 292 | 
            +
                    options.delete :additional_parameters
         | 
| 286 293 | 
             
                    options = Hash[options.map do |key, option|
         | 
| 287 294 | 
             
                      [key.to_s, option]
         | 
| 288 295 | 
             
                    end]
         | 
| @@ -291,20 +298,24 @@ module Signet | |
| 291 298 | 
             
                        @authorization_uri, options
         | 
| 292 299 | 
             
                      )
         | 
| 293 300 | 
             
                    )
         | 
| 294 | 
            -
                    if uri.normalized_scheme !=  | 
| 301 | 
            +
                    if uri.normalized_scheme != "https"
         | 
| 295 302 | 
             
                      raise Signet::UnsafeOperationError,
         | 
| 296 | 
            -
             | 
| 303 | 
            +
                            "Authorization endpoint must be protected by TLS."
         | 
| 297 304 | 
             
                    end
         | 
| 298 | 
            -
                     | 
| 305 | 
            +
                    uri
         | 
| 299 306 | 
             
                  end
         | 
| 307 | 
            +
                  # rubocop:enable Metrics/AbcSize
         | 
| 308 | 
            +
                  # rubocop:enable Metrics/CyclomaticComplexity
         | 
| 309 | 
            +
                  # rubocop:enable Metrics/MethodLength
         | 
| 310 | 
            +
                  # rubocop:enable Metrics/PerceivedComplexity
         | 
| 300 311 |  | 
| 301 312 | 
             
                  ##
         | 
| 302 313 | 
             
                  # Sets the authorization URI for this client.
         | 
| 303 314 | 
             
                  #
         | 
| 304 315 | 
             
                  # @param [Addressable::URI, Hash, String, #to_str] new_authorization_uri
         | 
| 305 316 | 
             
                  #   The authorization URI.
         | 
| 306 | 
            -
                  def authorization_uri= | 
| 307 | 
            -
                    @authorization_uri = coerce_uri | 
| 317 | 
            +
                  def authorization_uri= new_authorization_uri
         | 
| 318 | 
            +
                    @authorization_uri = coerce_uri new_authorization_uri
         | 
| 308 319 | 
             
                  end
         | 
| 309 320 |  | 
| 310 321 | 
             
                  ##
         | 
| @@ -312,7 +323,7 @@ module Signet | |
| 312 323 | 
             
                  #
         | 
| 313 324 | 
             
                  # @return [Addressable::URI] The token credential URI.
         | 
| 314 325 | 
             
                  def token_credential_uri
         | 
| 315 | 
            -
                     | 
| 326 | 
            +
                    @token_credential_uri
         | 
| 316 327 | 
             
                  end
         | 
| 317 328 |  | 
| 318 329 | 
             
                  ##
         | 
| @@ -320,17 +331,17 @@ module Signet | |
| 320 331 | 
             
                  #
         | 
| 321 332 | 
             
                  # @param [Addressable::URI, Hash, String, #to_str] new_token_credential_uri
         | 
| 322 333 | 
             
                  #   The token credential URI.
         | 
| 323 | 
            -
                  def token_credential_uri= | 
| 324 | 
            -
                    @token_credential_uri = coerce_uri | 
| 334 | 
            +
                  def token_credential_uri= new_token_credential_uri
         | 
| 335 | 
            +
                    @token_credential_uri = coerce_uri new_token_credential_uri
         | 
| 325 336 | 
             
                  end
         | 
| 326 337 |  | 
| 327 338 | 
             
                  # Addressable expects URIs formatted as hashes to come in with symbols as keys.
         | 
| 328 339 | 
             
                  # Returns nil implicitly for the nil case.
         | 
| 329 | 
            -
                  def coerce_uri | 
| 340 | 
            +
                  def coerce_uri incoming_uri
         | 
| 330 341 | 
             
                    if incoming_uri.is_a? Hash
         | 
| 331 | 
            -
                      Addressable::URI.new | 
| 342 | 
            +
                      Addressable::URI.new deep_hash_normalize(incoming_uri)
         | 
| 332 343 | 
             
                    elsif incoming_uri
         | 
| 333 | 
            -
                      Addressable::URI.parse | 
| 344 | 
            +
                      Addressable::URI.parse incoming_uri
         | 
| 334 345 | 
             
                    end
         | 
| 335 346 | 
             
                  end
         | 
| 336 347 |  | 
| @@ -339,7 +350,7 @@ module Signet | |
| 339 350 | 
             
                  #
         | 
| 340 351 | 
             
                  # @return [String, Symbol] The current access type.
         | 
| 341 352 | 
             
                  def access_type
         | 
| 342 | 
            -
                     | 
| 353 | 
            +
                    @access_type
         | 
| 343 354 | 
             
                  end
         | 
| 344 355 |  | 
| 345 356 | 
             
                  ##
         | 
| @@ -347,7 +358,7 @@ module Signet | |
| 347 358 | 
             
                  #
         | 
| 348 359 | 
             
                  # @param [String, Symbol] new_access_type
         | 
| 349 360 | 
             
                  #   The current access type.
         | 
| 350 | 
            -
                  def access_type= | 
| 361 | 
            +
                  def access_type= new_access_type
         | 
| 351 362 | 
             
                    @access_type = new_access_type
         | 
| 352 363 | 
             
                  end
         | 
| 353 364 |  | 
| @@ -356,7 +367,7 @@ module Signet | |
| 356 367 | 
             
                  #
         | 
| 357 368 | 
             
                  # @return [String] The client identifier.
         | 
| 358 369 | 
             
                  def client_id
         | 
| 359 | 
            -
                     | 
| 370 | 
            +
                    @client_id
         | 
| 360 371 | 
             
                  end
         | 
| 361 372 |  | 
| 362 373 | 
             
                  ##
         | 
| @@ -364,7 +375,7 @@ module Signet | |
| 364 375 | 
             
                  #
         | 
| 365 376 | 
             
                  # @param [String] new_client_id
         | 
| 366 377 | 
             
                  #   The client identifier.
         | 
| 367 | 
            -
                  def client_id= | 
| 378 | 
            +
                  def client_id= new_client_id
         | 
| 368 379 | 
             
                    @client_id = new_client_id
         | 
| 369 380 | 
             
                  end
         | 
| 370 381 |  | 
| @@ -373,7 +384,7 @@ module Signet | |
| 373 384 | 
             
                  #
         | 
| 374 385 | 
             
                  # @return [String] The client secret.
         | 
| 375 386 | 
             
                  def client_secret
         | 
| 376 | 
            -
                     | 
| 387 | 
            +
                    @client_secret
         | 
| 377 388 | 
             
                  end
         | 
| 378 389 |  | 
| 379 390 | 
             
                  ##
         | 
| @@ -381,7 +392,7 @@ module Signet | |
| 381 392 | 
             
                  #
         | 
| 382 393 | 
             
                  # @param [String] new_client_secret
         | 
| 383 394 | 
             
                  #   The client secret.
         | 
| 384 | 
            -
                  def client_secret= | 
| 395 | 
            +
                  def client_secret= new_client_secret
         | 
| 385 396 | 
             
                    @client_secret = new_client_secret
         | 
| 386 397 | 
             
                  end
         | 
| 387 398 |  | 
| @@ -391,7 +402,7 @@ module Signet | |
| 391 402 | 
             
                  #
         | 
| 392 403 | 
             
                  # @return [Array] The scope of access the client is requesting.
         | 
| 393 404 | 
             
                  def scope
         | 
| 394 | 
            -
                     | 
| 405 | 
            +
                    @scope
         | 
| 395 406 | 
             
                  end
         | 
| 396 407 |  | 
| 397 408 | 
             
                  ##
         | 
| @@ -401,18 +412,18 @@ module Signet | |
| 401 412 | 
             
                  #   The scope of access the client is requesting.  This may be
         | 
| 402 413 | 
             
                  #   expressed as either an Array of String objects or as a
         | 
| 403 414 | 
             
                  #   space-delimited String.
         | 
| 404 | 
            -
                  def scope= | 
| 415 | 
            +
                  def scope= new_scope
         | 
| 405 416 | 
             
                    case new_scope
         | 
| 406 417 | 
             
                    when Array
         | 
| 407 418 | 
             
                      new_scope.each do |scope|
         | 
| 408 | 
            -
                        if scope.include? | 
| 419 | 
            +
                        if scope.include? " "
         | 
| 409 420 | 
             
                          raise ArgumentError,
         | 
| 410 | 
            -
             | 
| 421 | 
            +
                                "Individual scopes cannot contain the space character."
         | 
| 411 422 | 
             
                        end
         | 
| 412 423 | 
             
                      end
         | 
| 413 424 | 
             
                      @scope = new_scope
         | 
| 414 425 | 
             
                    when String
         | 
| 415 | 
            -
                      @scope = new_scope.split | 
| 426 | 
            +
                      @scope = new_scope.split " "
         | 
| 416 427 | 
             
                    when nil
         | 
| 417 428 | 
             
                      @scope = nil
         | 
| 418 429 | 
             
                    else
         | 
| @@ -420,12 +431,28 @@ module Signet | |
| 420 431 | 
             
                    end
         | 
| 421 432 | 
             
                  end
         | 
| 422 433 |  | 
| 434 | 
            +
                  ##
         | 
| 435 | 
            +
                  # Returns the final target audience for ID tokens fetched by this client.
         | 
| 436 | 
            +
                  #
         | 
| 437 | 
            +
                  # @return [String] The target audience.
         | 
| 438 | 
            +
                  def target_audience
         | 
| 439 | 
            +
                    @target_audience
         | 
| 440 | 
            +
                  end
         | 
| 441 | 
            +
             | 
| 442 | 
            +
                  ##
         | 
| 443 | 
            +
                  # Sets the final target audience for ID tokens fetched by this client.
         | 
| 444 | 
            +
                  #
         | 
| 445 | 
            +
                  # @param [String] new_target_audience The new target audience.
         | 
| 446 | 
            +
                  def target_audience= new_target_audience
         | 
| 447 | 
            +
                    @target_audience = new_target_audience
         | 
| 448 | 
            +
                  end
         | 
| 449 | 
            +
             | 
| 423 450 | 
             
                  ##
         | 
| 424 451 | 
             
                  # Returns the client's current state value.
         | 
| 425 452 | 
             
                  #
         | 
| 426 453 | 
             
                  # @return [String] The state value.
         | 
| 427 454 | 
             
                  def state
         | 
| 428 | 
            -
                     | 
| 455 | 
            +
                    @state
         | 
| 429 456 | 
             
                  end
         | 
| 430 457 |  | 
| 431 458 | 
             
                  ##
         | 
| @@ -433,7 +460,7 @@ module Signet | |
| 433 460 | 
             
                  #
         | 
| 434 461 | 
             
                  # @param [String] new_state
         | 
| 435 462 | 
             
                  #   The state value.
         | 
| 436 | 
            -
                  def state= | 
| 463 | 
            +
                  def state= new_state
         | 
| 437 464 | 
             
                    @state = new_state
         | 
| 438 465 | 
             
                  end
         | 
| 439 466 |  | 
| @@ -443,7 +470,7 @@ module Signet | |
| 443 470 | 
             
                  #
         | 
| 444 471 | 
             
                  # @return [String] The authorization code.
         | 
| 445 472 | 
             
                  def code
         | 
| 446 | 
            -
                     | 
| 473 | 
            +
                    @code
         | 
| 447 474 | 
             
                  end
         | 
| 448 475 |  | 
| 449 476 | 
             
                  ##
         | 
| @@ -452,7 +479,7 @@ module Signet | |
| 452 479 | 
             
                  #
         | 
| 453 480 | 
             
                  # @param [String] new_code
         | 
| 454 481 | 
             
                  #   The authorization code.
         | 
| 455 | 
            -
                  def code= | 
| 482 | 
            +
                  def code= new_code
         | 
| 456 483 | 
             
                    @code = new_code
         | 
| 457 484 | 
             
                  end
         | 
| 458 485 |  | 
| @@ -461,7 +488,7 @@ module Signet | |
| 461 488 | 
             
                  #
         | 
| 462 489 | 
             
                  # @return [String] The redirect URI.
         | 
| 463 490 | 
             
                  def redirect_uri
         | 
| 464 | 
            -
                     | 
| 491 | 
            +
                    @redirect_uri
         | 
| 465 492 | 
             
                  end
         | 
| 466 493 |  | 
| 467 494 | 
             
                  ##
         | 
| @@ -469,14 +496,14 @@ module Signet | |
| 469 496 | 
             
                  #
         | 
| 470 497 | 
             
                  # @param [String] new_redirect_uri
         | 
| 471 498 | 
             
                  #   The redirect URI.
         | 
| 472 | 
            -
                  def redirect_uri= | 
| 473 | 
            -
                    new_redirect_uri = Addressable::URI.parse | 
| 474 | 
            -
                    #TODO - Better solution to allow google postmessage flow. For now, make an exception to the spec.
         | 
| 475 | 
            -
                     | 
| 476 | 
            -
             | 
| 477 | 
            -
                    else
         | 
| 499 | 
            +
                  def redirect_uri= new_redirect_uri
         | 
| 500 | 
            +
                    new_redirect_uri = Addressable::URI.parse new_redirect_uri
         | 
| 501 | 
            +
                    # TODO: - Better solution to allow google postmessage flow. For now, make an exception to the spec.
         | 
| 502 | 
            +
                    unless new_redirect_uri.nil? || new_redirect_uri.absolute? || uri_is_postmessage?(new_redirect_uri) ||
         | 
| 503 | 
            +
                           uri_is_oob?(new_redirect_uri)
         | 
| 478 504 | 
             
                      raise ArgumentError, "Redirect URI must be an absolute URI."
         | 
| 479 505 | 
             
                    end
         | 
| 506 | 
            +
                    @redirect_uri = new_redirect_uri
         | 
| 480 507 | 
             
                  end
         | 
| 481 508 |  | 
| 482 509 | 
             
                  ##
         | 
| @@ -485,7 +512,7 @@ module Signet | |
| 485 512 | 
             
                  #
         | 
| 486 513 | 
             
                  # @return [String] The username.
         | 
| 487 514 | 
             
                  def username
         | 
| 488 | 
            -
                     | 
| 515 | 
            +
                    @username
         | 
| 489 516 | 
             
                  end
         | 
| 490 517 |  | 
| 491 518 | 
             
                  ##
         | 
| @@ -494,7 +521,7 @@ module Signet | |
| 494 521 | 
             
                  #
         | 
| 495 522 | 
             
                  # @param [String] new_username
         | 
| 496 523 | 
             
                  #   The username.
         | 
| 497 | 
            -
                  def username= | 
| 524 | 
            +
                  def username= new_username
         | 
| 498 525 | 
             
                    @username = new_username
         | 
| 499 526 | 
             
                  end
         | 
| 500 527 |  | 
| @@ -504,7 +531,7 @@ module Signet | |
| 504 531 | 
             
                  #
         | 
| 505 532 | 
             
                  # @return [String] The password.
         | 
| 506 533 | 
             
                  def password
         | 
| 507 | 
            -
                     | 
| 534 | 
            +
                    @password
         | 
| 508 535 | 
             
                  end
         | 
| 509 536 |  | 
| 510 537 | 
             
                  ##
         | 
| @@ -513,7 +540,7 @@ module Signet | |
| 513 540 | 
             
                  #
         | 
| 514 541 | 
             
                  # @param [String] new_password
         | 
| 515 542 | 
             
                  #   The password.
         | 
| 516 | 
            -
                  def password= | 
| 543 | 
            +
                  def password= new_password
         | 
| 517 544 | 
             
                    @password = new_password
         | 
| 518 545 | 
             
                  end
         | 
| 519 546 |  | 
| @@ -523,7 +550,7 @@ module Signet | |
| 523 550 | 
             
                  #
         | 
| 524 551 | 
             
                  # @return [String] Issuer id.
         | 
| 525 552 | 
             
                  def issuer
         | 
| 526 | 
            -
                     | 
| 553 | 
            +
                    @issuer
         | 
| 527 554 | 
             
                  end
         | 
| 528 555 |  | 
| 529 556 | 
             
                  ##
         | 
| @@ -532,17 +559,17 @@ module Signet | |
| 532 559 | 
             
                  #
         | 
| 533 560 | 
             
                  # @param [String] new_issuer
         | 
| 534 561 | 
             
                  #   Issuer ID (typical in email adddress form).
         | 
| 535 | 
            -
                  def issuer= | 
| 562 | 
            +
                  def issuer= new_issuer
         | 
| 536 563 | 
             
                    @issuer = new_issuer
         | 
| 537 564 | 
             
                  end
         | 
| 538 565 |  | 
| 539 566 | 
             
                  ##
         | 
| 540 | 
            -
                  # Returns the  | 
| 567 | 
            +
                  # Returns the target audience ID when issuing assertions.
         | 
| 541 568 | 
             
                  # Used only by the assertion grant type.
         | 
| 542 569 | 
             
                  #
         | 
| 543 570 | 
             
                  # @return [String] Target audience ID.
         | 
| 544 571 | 
             
                  def audience
         | 
| 545 | 
            -
                     | 
| 572 | 
            +
                    @audience
         | 
| 546 573 | 
             
                  end
         | 
| 547 574 |  | 
| 548 575 | 
             
                  ##
         | 
| @@ -551,7 +578,7 @@ module Signet | |
| 551 578 | 
             
                  #
         | 
| 552 579 | 
             
                  # @param [String] new_audience
         | 
| 553 580 | 
             
                  #   Target audience ID
         | 
| 554 | 
            -
                  def audience= | 
| 581 | 
            +
                  def audience= new_audience
         | 
| 555 582 | 
             
                    @audience = new_audience
         | 
| 556 583 | 
             
                  end
         | 
| 557 584 |  | 
| @@ -561,7 +588,7 @@ module Signet | |
| 561 588 | 
             
                  #
         | 
| 562 589 | 
             
                  # @return [String] Target user for impersonation.
         | 
| 563 590 | 
             
                  def principal
         | 
| 564 | 
            -
                     | 
| 591 | 
            +
                    @principal
         | 
| 565 592 | 
             
                  end
         | 
| 566 593 |  | 
| 567 594 | 
             
                  ##
         | 
| @@ -570,12 +597,12 @@ module Signet | |
| 570 597 | 
             
                  #
         | 
| 571 598 | 
             
                  # @param [String] new_person
         | 
| 572 599 | 
             
                  #   Target user for impersonation
         | 
| 573 | 
            -
                  def principal= | 
| 600 | 
            +
                  def principal= new_person
         | 
| 574 601 | 
             
                    @principal = new_person
         | 
| 575 602 | 
             
                  end
         | 
| 576 603 |  | 
| 577 | 
            -
                   | 
| 578 | 
            -
                   | 
| 604 | 
            +
                  alias person principal
         | 
| 605 | 
            +
                  alias person= principal=
         | 
| 579 606 |  | 
| 580 607 | 
             
                  ##
         | 
| 581 608 | 
             
                  # The target "sub" when issuing assertions.
         | 
| @@ -589,7 +616,7 @@ module Signet | |
| 589 616 | 
             
                  #
         | 
| 590 617 | 
             
                  # @return [Integer] Assertion expiry, in seconds
         | 
| 591 618 | 
             
                  def expiry
         | 
| 592 | 
            -
                     | 
| 619 | 
            +
                    @expiry
         | 
| 593 620 | 
             
                  end
         | 
| 594 621 |  | 
| 595 622 | 
             
                  ##
         | 
| @@ -598,18 +625,17 @@ module Signet | |
| 598 625 | 
             
                  #
         | 
| 599 626 | 
             
                  # @param [Integer, String] new_expiry
         | 
| 600 627 | 
             
                  #   Assertion expiry, in seconds
         | 
| 601 | 
            -
                  def expiry= | 
| 628 | 
            +
                  def expiry= new_expiry
         | 
| 602 629 | 
             
                    @expiry = new_expiry ? new_expiry.to_i : nil
         | 
| 603 630 | 
             
                  end
         | 
| 604 631 |  | 
| 605 | 
            -
             | 
| 606 632 | 
             
                  ##
         | 
| 607 633 | 
             
                  # Returns the signing key associated with this client.
         | 
| 608 634 | 
             
                  # Used only by the assertion grant type.
         | 
| 609 635 | 
             
                  #
         | 
| 610 636 | 
             
                  # @return [String,OpenSSL::PKey] Signing key
         | 
| 611 637 | 
             
                  def signing_key
         | 
| 612 | 
            -
                     | 
| 638 | 
            +
                    @signing_key
         | 
| 613 639 | 
             
                  end
         | 
| 614 640 |  | 
| 615 641 | 
             
                  ##
         | 
| @@ -618,7 +644,7 @@ module Signet | |
| 618 644 | 
             
                  #
         | 
| 619 645 | 
             
                  # @param [String, OpenSSL::Pkey] new_key
         | 
| 620 646 | 
             
                  #   Signing key. Either private key for RSA or string for HMAC algorithm
         | 
| 621 | 
            -
                  def signing_key= | 
| 647 | 
            +
                  def signing_key= new_key
         | 
| 622 648 | 
             
                    @signing_key = new_key
         | 
| 623 649 | 
             
                  end
         | 
| 624 650 |  | 
| @@ -626,7 +652,7 @@ module Signet | |
| 626 652 | 
             
                  # Algorithm used for signing JWTs
         | 
| 627 653 | 
             
                  # @return [String] Signing algorithm
         | 
| 628 654 | 
             
                  def signing_algorithm
         | 
| 629 | 
            -
                     | 
| 655 | 
            +
                    signing_key.is_a?(String) ? "HS256" : "RS256"
         | 
| 630 656 | 
             
                  end
         | 
| 631 657 |  | 
| 632 658 | 
             
                  ##
         | 
| @@ -635,7 +661,7 @@ module Signet | |
| 635 661 | 
             
                  #
         | 
| 636 662 | 
             
                  # @return [Hash] The extension parameters.
         | 
| 637 663 | 
             
                  def extension_parameters
         | 
| 638 | 
            -
                     | 
| 664 | 
            +
                    @extension_parameters ||= {}
         | 
| 639 665 | 
             
                  end
         | 
| 640 666 |  | 
| 641 667 | 
             
                  ##
         | 
| @@ -644,12 +670,12 @@ module Signet | |
| 644 670 | 
             
                  #
         | 
| 645 671 | 
             
                  # @param [Hash] new_extension_parameters
         | 
| 646 672 | 
             
                  #   The parameters.
         | 
| 647 | 
            -
                  def extension_parameters= | 
| 648 | 
            -
                    if new_extension_parameters.respond_to? | 
| 673 | 
            +
                  def extension_parameters= new_extension_parameters
         | 
| 674 | 
            +
                    if new_extension_parameters.respond_to? :to_hash
         | 
| 649 675 | 
             
                      @extension_parameters = new_extension_parameters.to_hash
         | 
| 650 676 | 
             
                    else
         | 
| 651 677 | 
             
                      raise TypeError,
         | 
| 652 | 
            -
             | 
| 678 | 
            +
                            "Expected Hash, got #{new_extension_parameters.class}."
         | 
| 653 679 | 
             
                    end
         | 
| 654 680 | 
             
                  end
         | 
| 655 681 |  | 
| @@ -658,7 +684,7 @@ module Signet | |
| 658 684 | 
             
                  #
         | 
| 659 685 | 
             
                  # @return [Hash] The pass through parameters.
         | 
| 660 686 | 
             
                  def additional_parameters
         | 
| 661 | 
            -
                     | 
| 687 | 
            +
                    @additional_parameters ||= {}
         | 
| 662 688 | 
             
                  end
         | 
| 663 689 |  | 
| 664 690 | 
             
                  ##
         | 
| @@ -666,8 +692,8 @@ module Signet | |
| 666 692 | 
             
                  #
         | 
| 667 693 | 
             
                  # @param [Hash] new_additional_parameters
         | 
| 668 694 | 
             
                  #   The parameters.
         | 
| 669 | 
            -
                  def additional_parameters= | 
| 670 | 
            -
                    if new_additional_parameters.respond_to? | 
| 695 | 
            +
                  def additional_parameters= new_additional_parameters
         | 
| 696 | 
            +
                    if new_additional_parameters.respond_to? :to_hash
         | 
| 671 697 | 
             
                      @additional_parameters = new_additional_parameters.to_hash
         | 
| 672 698 | 
             
                    else
         | 
| 673 699 | 
             
                      raise TypeError,
         | 
| @@ -680,7 +706,7 @@ module Signet | |
| 680 706 | 
             
                  #
         | 
| 681 707 | 
             
                  # @return [String] The refresh token.
         | 
| 682 708 | 
             
                  def refresh_token
         | 
| 683 | 
            -
                     | 
| 709 | 
            +
                    @refresh_token ||= nil
         | 
| 684 710 | 
             
                  end
         | 
| 685 711 |  | 
| 686 712 | 
             
                  ##
         | 
| @@ -688,7 +714,7 @@ module Signet | |
| 688 714 | 
             
                  #
         | 
| 689 715 | 
             
                  # @param [String] new_refresh_token
         | 
| 690 716 | 
             
                  #   The refresh token.
         | 
| 691 | 
            -
                  def refresh_token= | 
| 717 | 
            +
                  def refresh_token= new_refresh_token
         | 
| 692 718 | 
             
                    @refresh_token = new_refresh_token
         | 
| 693 719 | 
             
                  end
         | 
| 694 720 |  | 
| @@ -697,7 +723,7 @@ module Signet | |
| 697 723 | 
             
                  #
         | 
| 698 724 | 
             
                  # @return [String] The access token.
         | 
| 699 725 | 
             
                  def access_token
         | 
| 700 | 
            -
                     | 
| 726 | 
            +
                    @access_token ||= nil
         | 
| 701 727 | 
             
                  end
         | 
| 702 728 |  | 
| 703 729 | 
             
                  ##
         | 
| @@ -705,7 +731,7 @@ module Signet | |
| 705 731 | 
             
                  #
         | 
| 706 732 | 
             
                  # @param [String] new_access_token
         | 
| 707 733 | 
             
                  #   The access token.
         | 
| 708 | 
            -
                  def access_token= | 
| 734 | 
            +
                  def access_token= new_access_token
         | 
| 709 735 | 
             
                    @access_token = new_access_token
         | 
| 710 736 | 
             
                  end
         | 
| 711 737 |  | 
| @@ -714,7 +740,7 @@ module Signet | |
| 714 740 | 
             
                  #
         | 
| 715 741 | 
             
                  # @return [String] The ID token.
         | 
| 716 742 | 
             
                  def id_token
         | 
| 717 | 
            -
                     | 
| 743 | 
            +
                    @id_token ||= nil
         | 
| 718 744 | 
             
                  end
         | 
| 719 745 |  | 
| 720 746 | 
             
                  ##
         | 
| @@ -722,7 +748,7 @@ module Signet | |
| 722 748 | 
             
                  #
         | 
| 723 749 | 
             
                  # @param [String] new_id_token
         | 
| 724 750 | 
             
                  #   The ID token.
         | 
| 725 | 
            -
                  def id_token= | 
| 751 | 
            +
                  def id_token= new_id_token
         | 
| 726 752 | 
             
                    @id_token = new_id_token
         | 
| 727 753 | 
             
                  end
         | 
| 728 754 |  | 
| @@ -734,17 +760,16 @@ module Signet | |
| 734 760 | 
             
                  #   omitted.
         | 
| 735 761 | 
             
                  #
         | 
| 736 762 | 
             
                  # @return [String] The decoded ID token.
         | 
| 737 | 
            -
                  def decoded_id_token public_key=nil, options = {}, &keyfinder
         | 
| 763 | 
            +
                  def decoded_id_token public_key = nil, options = {}, &keyfinder
         | 
| 738 764 | 
             
                    options[:algorithm] ||= signing_algorithm
         | 
| 739 | 
            -
                    verify =  | 
| 740 | 
            -
                    payload, _header = JWT.decode( | 
| 741 | 
            -
                     | 
| 742 | 
            -
             | 
| 743 | 
            -
                    elsif payload['aud'] != self.client_id
         | 
| 765 | 
            +
                    verify = !public_key.nil? || block_given?
         | 
| 766 | 
            +
                    payload, _header = JWT.decode(id_token, public_key, verify, options, &keyfinder)
         | 
| 767 | 
            +
                    raise Signet::UnsafeOperationError, "No ID token audience declared." unless payload.key? "aud"
         | 
| 768 | 
            +
                    unless Array(payload["aud"]).include?(client_id)
         | 
| 744 769 | 
             
                      raise Signet::UnsafeOperationError,
         | 
| 745 | 
            -
             | 
| 770 | 
            +
                            "ID token audience did not match Client ID."
         | 
| 746 771 | 
             
                    end
         | 
| 747 | 
            -
                     | 
| 772 | 
            +
                    payload
         | 
| 748 773 | 
             
                  end
         | 
| 749 774 |  | 
| 750 775 | 
             
                  ##
         | 
| @@ -790,8 +815,8 @@ module Signet | |
| 790 815 | 
             
                  #
         | 
| 791 816 | 
             
                  # @param [String,Integer,Time] new_issued_at
         | 
| 792 817 | 
             
                  #    The access token issuance time.
         | 
| 793 | 
            -
                  def issued_at= | 
| 794 | 
            -
                    @issued_at = normalize_timestamp | 
| 818 | 
            +
                  def issued_at= new_issued_at
         | 
| 819 | 
            +
                    @issued_at = normalize_timestamp new_issued_at
         | 
| 795 820 | 
             
                  end
         | 
| 796 821 |  | 
| 797 822 | 
             
                  ##
         | 
| @@ -809,7 +834,7 @@ module Signet | |
| 809 834 | 
             
                  # not expire.
         | 
| 810 835 | 
             
                  # @param [String,Integer,Time, nil] new_expires_at
         | 
| 811 836 | 
             
                  #    The access token expiration time.
         | 
| 812 | 
            -
                  def expires_at= | 
| 837 | 
            +
                  def expires_at= new_expires_at
         | 
| 813 838 | 
             
                    @expires_at = normalize_timestamp new_expires_at
         | 
| 814 839 | 
             
                  end
         | 
| 815 840 |  | 
| @@ -820,7 +845,7 @@ module Signet | |
| 820 845 | 
             
                  # @return [TrueClass, FalseClass]
         | 
| 821 846 | 
             
                  #   The expiration state of the access token.
         | 
| 822 847 | 
             
                  def expired?
         | 
| 823 | 
            -
                     | 
| 848 | 
            +
                    !expires_at.nil? && Time.now >= expires_at
         | 
| 824 849 | 
             
                  end
         | 
| 825 850 |  | 
| 826 851 | 
             
                  ##
         | 
| @@ -832,8 +857,8 @@ module Signet | |
| 832 857 | 
             
                  #  expired.
         | 
| 833 858 | 
             
                  # @return [TrueClass, FalseClass]
         | 
| 834 859 | 
             
                  #   The expiration state of the access token.
         | 
| 835 | 
            -
                  def expires_within? | 
| 836 | 
            -
                     | 
| 860 | 
            +
                  def expires_within? sec
         | 
| 861 | 
            +
                    !expires_at.nil? && Time.now >= (expires_at - sec)
         | 
| 837 862 | 
             
                  end
         | 
| 838 863 |  | 
| 839 864 | 
             
                  ##
         | 
| @@ -849,7 +874,6 @@ module Signet | |
| 849 874 | 
             
                    @expires_at = nil
         | 
| 850 875 | 
             
                  end
         | 
| 851 876 |  | 
| 852 | 
            -
             | 
| 853 877 | 
             
                  ##
         | 
| 854 878 | 
             
                  # Returns the inferred grant type, based on the current state of the
         | 
| 855 879 | 
             
                  # client object.  Returns `"none"` if the client has insufficient
         | 
| @@ -859,52 +883,47 @@ module Signet | |
| 859 883 | 
             
                  #   The inferred grant type.
         | 
| 860 884 | 
             
                  def grant_type
         | 
| 861 885 | 
             
                    @grant_type ||= nil
         | 
| 862 | 
            -
                    if @grant_type
         | 
| 863 | 
            -
             | 
| 864 | 
            -
             | 
| 865 | 
            -
             | 
| 866 | 
            -
             | 
| 867 | 
            -
             | 
| 868 | 
            -
             | 
| 869 | 
            -
             | 
| 870 | 
            -
             | 
| 871 | 
            -
                      elsif self.issuer && self.signing_key
         | 
| 872 | 
            -
                        'urn:ietf:params:oauth:grant-type:jwt-bearer'
         | 
| 873 | 
            -
                      else
         | 
| 874 | 
            -
                        # We don't have sufficient auth information, assume an out-of-band
         | 
| 875 | 
            -
                        # authorization arrangement between the client and server, or an
         | 
| 876 | 
            -
                        # extension grant type.
         | 
| 877 | 
            -
                        nil
         | 
| 878 | 
            -
                      end
         | 
| 886 | 
            +
                    return @grant_type if @grant_type
         | 
| 887 | 
            +
                    if code && redirect_uri
         | 
| 888 | 
            +
                      "authorization_code"
         | 
| 889 | 
            +
                    elsif refresh_token
         | 
| 890 | 
            +
                      "refresh_token"
         | 
| 891 | 
            +
                    elsif username && password
         | 
| 892 | 
            +
                      "password"
         | 
| 893 | 
            +
                    elsif issuer && signing_key
         | 
| 894 | 
            +
                      "urn:ietf:params:oauth:grant-type:jwt-bearer"
         | 
| 879 895 | 
             
                    end
         | 
| 880 896 | 
             
                  end
         | 
| 881 897 |  | 
| 882 | 
            -
                  def grant_type= | 
| 898 | 
            +
                  def grant_type= new_grant_type
         | 
| 883 899 | 
             
                    case new_grant_type
         | 
| 884 | 
            -
                    when  | 
| 885 | 
            -
                         | 
| 900 | 
            +
                    when "authorization_code", "refresh_token",
         | 
| 901 | 
            +
                        "password", "client_credentials"
         | 
| 886 902 | 
             
                      @grant_type = new_grant_type
         | 
| 887 903 | 
             
                    else
         | 
| 888 | 
            -
                      @grant_type = Addressable::URI.parse | 
| 904 | 
            +
                      @grant_type = Addressable::URI.parse new_grant_type
         | 
| 889 905 | 
             
                    end
         | 
| 890 906 | 
             
                  end
         | 
| 891 907 |  | 
| 892 | 
            -
                  def to_jwt | 
| 893 | 
            -
                    options = deep_hash_normalize | 
| 908 | 
            +
                  def to_jwt options = {}
         | 
| 909 | 
            +
                    options = deep_hash_normalize options
         | 
| 894 910 |  | 
| 895 911 | 
             
                    now = Time.new
         | 
| 896 912 | 
             
                    skew = options[:skew] || 60
         | 
| 897 913 | 
             
                    assertion = {
         | 
| 898 | 
            -
                      "iss" =>  | 
| 899 | 
            -
                      "aud" =>  | 
| 900 | 
            -
                      "exp" => (now +  | 
| 914 | 
            +
                      "iss" => issuer,
         | 
| 915 | 
            +
                      "aud" => audience,
         | 
| 916 | 
            +
                      "exp" => (now + expiry).to_i,
         | 
| 901 917 | 
             
                      "iat" => (now - skew).to_i
         | 
| 902 918 | 
             
                    }
         | 
| 903 | 
            -
                    assertion[ | 
| 904 | 
            -
                    assertion[ | 
| 905 | 
            -
                    assertion[ | 
| 906 | 
            -
                     | 
| 919 | 
            +
                    assertion["scope"] = scope.join " " unless scope.nil?
         | 
| 920 | 
            +
                    assertion["target_audience"] = target_audience unless target_audience.nil?
         | 
| 921 | 
            +
                    assertion["prn"] = person unless person.nil?
         | 
| 922 | 
            +
                    assertion["sub"] = sub unless sub.nil?
         | 
| 923 | 
            +
                    JWT.encode assertion, signing_key, signing_algorithm
         | 
| 907 924 | 
             
                  end
         | 
| 925 | 
            +
                  # rubocop:disable Style/MethodDefParentheses
         | 
| 926 | 
            +
                  # rubocop:disable Metrics/AbcSize
         | 
| 908 927 |  | 
| 909 928 | 
             
                  ##
         | 
| 910 929 | 
             
                  # Serialize the client object to JSON.
         | 
| @@ -913,29 +932,34 @@ module Signet | |
| 913 932 | 
             
                  #
         | 
| 914 933 | 
             
                  # @return [String] A serialized JSON representation of the client.
         | 
| 915 934 | 
             
                  def to_json(*)
         | 
| 916 | 
            -
                     | 
| 917 | 
            -
                       | 
| 918 | 
            -
                       | 
| 919 | 
            -
                       | 
| 920 | 
            -
                       | 
| 921 | 
            -
                       | 
| 922 | 
            -
                       | 
| 923 | 
            -
                       | 
| 924 | 
            -
                       | 
| 925 | 
            -
                       | 
| 926 | 
            -
                       | 
| 927 | 
            -
                       | 
| 928 | 
            -
                       | 
| 929 | 
            -
                       | 
| 930 | 
            -
                       | 
| 931 | 
            -
                       | 
| 932 | 
            -
                       | 
| 933 | 
            -
                       | 
| 934 | 
            -
                       | 
| 935 | 
            -
                       | 
| 936 | 
            -
                       | 
| 937 | 
            -
             | 
| 935 | 
            +
                    MultiJson.dump(
         | 
| 936 | 
            +
                      "authorization_uri"    => authorization_uri ? authorization_uri.to_s : nil,
         | 
| 937 | 
            +
                      "token_credential_uri" => token_credential_uri ? token_credential_uri.to_s : nil,
         | 
| 938 | 
            +
                      "client_id"            => client_id,
         | 
| 939 | 
            +
                      "client_secret"        => client_secret,
         | 
| 940 | 
            +
                      "scope"                => scope,
         | 
| 941 | 
            +
                      "target_audience"      => target_audience,
         | 
| 942 | 
            +
                      "state"                => state,
         | 
| 943 | 
            +
                      "code"                 => code,
         | 
| 944 | 
            +
                      "redirect_uri"         => redirect_uri ? redirect_uri.to_s : nil,
         | 
| 945 | 
            +
                      "username"             => username,
         | 
| 946 | 
            +
                      "password"             => password,
         | 
| 947 | 
            +
                      "issuer"               => issuer,
         | 
| 948 | 
            +
                      "audience"             => audience,
         | 
| 949 | 
            +
                      "person"               => person,
         | 
| 950 | 
            +
                      "expiry"               => expiry,
         | 
| 951 | 
            +
                      "expires_at"           => expires_at ? expires_at.to_i : nil,
         | 
| 952 | 
            +
                      "signing_key"          => signing_key,
         | 
| 953 | 
            +
                      "refresh_token"        => refresh_token,
         | 
| 954 | 
            +
                      "access_token"         => access_token,
         | 
| 955 | 
            +
                      "id_token"             => id_token,
         | 
| 956 | 
            +
                      "extension_parameters" => extension_parameters
         | 
| 957 | 
            +
                    )
         | 
| 938 958 | 
             
                  end
         | 
| 959 | 
            +
                  # rubocop:enable Style/MethodDefParentheses
         | 
| 960 | 
            +
                  # rubocop:disable Metrics/CyclomaticComplexity
         | 
| 961 | 
            +
                  # rubocop:disable Metrics/MethodLength
         | 
| 962 | 
            +
                  # rubocop:disable Metrics/PerceivedComplexity
         | 
| 939 963 |  | 
| 940 964 | 
             
                  ##
         | 
| 941 965 | 
             
                  # Generates a request for token credentials.
         | 
| @@ -947,58 +971,58 @@ module Signet | |
| 947 971 | 
             
                  #
         | 
| 948 972 | 
             
                  # @private
         | 
| 949 973 | 
             
                  # @return [Array] The request object.
         | 
| 950 | 
            -
                  def generate_access_token_request | 
| 951 | 
            -
                    options = deep_hash_normalize | 
| 952 | 
            -
             | 
| 953 | 
            -
                    parameters = {"grant_type" =>  | 
| 954 | 
            -
                    case  | 
| 955 | 
            -
                    when  | 
| 956 | 
            -
                      parameters[ | 
| 957 | 
            -
                      parameters[ | 
| 958 | 
            -
                    when  | 
| 959 | 
            -
                      parameters[ | 
| 960 | 
            -
                      parameters[ | 
| 961 | 
            -
                    when  | 
| 962 | 
            -
                      parameters[ | 
| 963 | 
            -
                    when  | 
| 964 | 
            -
                      parameters[ | 
| 974 | 
            +
                  def generate_access_token_request options = {}
         | 
| 975 | 
            +
                    options = deep_hash_normalize options
         | 
| 976 | 
            +
             | 
| 977 | 
            +
                    parameters = { "grant_type" => grant_type }
         | 
| 978 | 
            +
                    case grant_type
         | 
| 979 | 
            +
                    when "authorization_code"
         | 
| 980 | 
            +
                      parameters["code"] = code
         | 
| 981 | 
            +
                      parameters["redirect_uri"] = redirect_uri
         | 
| 982 | 
            +
                    when "password"
         | 
| 983 | 
            +
                      parameters["username"] = username
         | 
| 984 | 
            +
                      parameters["password"] = password
         | 
| 985 | 
            +
                    when "refresh_token"
         | 
| 986 | 
            +
                      parameters["refresh_token"] = refresh_token
         | 
| 987 | 
            +
                    when "urn:ietf:params:oauth:grant-type:jwt-bearer"
         | 
| 988 | 
            +
                      parameters["assertion"] = to_jwt options
         | 
| 965 989 | 
             
                    else
         | 
| 966 | 
            -
                      if  | 
| 990 | 
            +
                      if redirect_uri
         | 
| 967 991 | 
             
                        # Grant type was intended to be `authorization_code` because of
         | 
| 968 992 | 
             
                        # the presence of the redirect URI.
         | 
| 969 | 
            -
                        raise ArgumentError,  | 
| 993 | 
            +
                        raise ArgumentError, "Missing authorization code."
         | 
| 970 994 | 
             
                      end
         | 
| 971 | 
            -
                      parameters.merge! | 
| 995 | 
            +
                      parameters.merge! extension_parameters
         | 
| 972 996 | 
             
                    end
         | 
| 973 | 
            -
                    parameters[ | 
| 974 | 
            -
                    parameters[ | 
| 997 | 
            +
                    parameters["client_id"] = client_id unless client_id.nil?
         | 
| 998 | 
            +
                    parameters["client_secret"] = client_secret unless client_secret.nil?
         | 
| 975 999 | 
             
                    if options[:scope]
         | 
| 976 | 
            -
                      parameters[ | 
| 977 | 
            -
                    elsif options[:use_configured_scope] && ! | 
| 978 | 
            -
                      parameters[ | 
| 1000 | 
            +
                      parameters["scope"] = options[:scope]
         | 
| 1001 | 
            +
                    elsif options[:use_configured_scope] && !scope.nil?
         | 
| 1002 | 
            +
                      parameters["scope"] = scope
         | 
| 979 1003 | 
             
                    end
         | 
| 980 | 
            -
                    additional =  | 
| 1004 | 
            +
                    additional = additional_parameters.merge(options[:additional_parameters] || {})
         | 
| 981 1005 | 
             
                    additional.each { |k, v| parameters[k.to_s] = v }
         | 
| 982 1006 | 
             
                    parameters
         | 
| 983 1007 | 
             
                  end
         | 
| 1008 | 
            +
                  # rubocop:enable Metrics/CyclomaticComplexity
         | 
| 1009 | 
            +
                  # rubocop:enable Metrics/PerceivedComplexity
         | 
| 984 1010 |  | 
| 985 | 
            -
                  def fetch_access_token | 
| 986 | 
            -
                    if  | 
| 987 | 
            -
                      raise ArgumentError, 'Missing token endpoint URI.'
         | 
| 988 | 
            -
                    end
         | 
| 1011 | 
            +
                  def fetch_access_token options = {}
         | 
| 1012 | 
            +
                    raise ArgumentError, "Missing token endpoint URI." if token_credential_uri.nil?
         | 
| 989 1013 |  | 
| 990 | 
            -
                    options = deep_hash_normalize | 
| 1014 | 
            +
                    options = deep_hash_normalize options
         | 
| 991 1015 |  | 
| 992 1016 | 
             
                    client = options[:connection] ||= Faraday.default_connection
         | 
| 993 | 
            -
                    url = Addressable::URI.parse( | 
| 994 | 
            -
                    parameters =  | 
| 995 | 
            -
                    if client.is_a? | 
| 1017 | 
            +
                    url = Addressable::URI.parse(token_credential_uri).normalize.to_s
         | 
| 1018 | 
            +
                    parameters = generate_access_token_request options
         | 
| 1019 | 
            +
                    if client.is_a? Faraday::Connection
         | 
| 996 1020 | 
             
                      response = client.post url,
         | 
| 997 | 
            -
             | 
| 998 | 
            -
             | 
| 1021 | 
            +
                                             Addressable::URI.form_encode(parameters),
         | 
| 1022 | 
            +
                                             "Content-Type" => "application/x-www-form-urlencoded"
         | 
| 999 1023 | 
             
                      status = response.status.to_i
         | 
| 1000 1024 | 
             
                      body = response.body
         | 
| 1001 | 
            -
                      content_type = response.headers[ | 
| 1025 | 
            +
                      content_type = response.headers["Content-type"]
         | 
| 1002 1026 | 
             
                    else
         | 
| 1003 1027 | 
             
                      # Hurley
         | 
| 1004 1028 | 
             
                      response = client.post url, parameters
         | 
| @@ -1007,49 +1031,46 @@ module Signet | |
| 1007 1031 | 
             
                      content_type = response.header[:content_type]
         | 
| 1008 1032 | 
             
                    end
         | 
| 1009 1033 |  | 
| 1010 | 
            -
                    if status == 200
         | 
| 1011 | 
            -
             | 
| 1012 | 
            -
                     | 
| 1013 | 
            -
             | 
| 1014 | 
            -
                       | 
| 1015 | 
            -
                        message += "  Server message:\n#{response.body.to_s.strip}"
         | 
| 1016 | 
            -
                      end
         | 
| 1034 | 
            +
                    return ::Signet::OAuth2.parse_credentials body, content_type if status == 200
         | 
| 1035 | 
            +
             | 
| 1036 | 
            +
                    message = "  Server message:\n#{response.body.to_s.strip}" unless body.to_s.strip.empty?
         | 
| 1037 | 
            +
                    if [400, 401, 403].include? status
         | 
| 1038 | 
            +
                      message = "Authorization failed." + message
         | 
| 1017 1039 | 
             
                      raise ::Signet::AuthorizationError.new(
         | 
| 1018 | 
            -
                        message, : | 
| 1040 | 
            +
                        message, response: response
         | 
| 1019 1041 | 
             
                      )
         | 
| 1020 1042 | 
             
                    elsif status.to_s[0] == "5"
         | 
| 1021 | 
            -
                      message =  | 
| 1022 | 
            -
                       | 
| 1023 | 
            -
                        message += "  Server message:\n#{response.body.to_s.strip}"
         | 
| 1024 | 
            -
                      end
         | 
| 1025 | 
            -
                      raise ::Signet::RemoteServerError.new(message)
         | 
| 1043 | 
            +
                      message = "Remote server error." + message
         | 
| 1044 | 
            +
                      raise ::Signet::RemoteServerError, message
         | 
| 1026 1045 | 
             
                    else
         | 
| 1027 | 
            -
                      message = "Unexpected status code: #{response.status}."
         | 
| 1028 | 
            -
                       | 
| 1029 | 
            -
                        message += "  Server message:\n#{response.body.to_s.strip}"
         | 
| 1030 | 
            -
                      end
         | 
| 1031 | 
            -
                      raise ::Signet::UnexpectedStatusError.new(message)
         | 
| 1046 | 
            +
                      message = "Unexpected status code: #{response.status}." + message
         | 
| 1047 | 
            +
                      raise ::Signet::UnexpectedStatusError, message
         | 
| 1032 1048 | 
             
                    end
         | 
| 1033 1049 | 
             
                  end
         | 
| 1050 | 
            +
                  # rubocop:enable Metrics/AbcSize
         | 
| 1051 | 
            +
                  # rubocop:enable Metrics/MethodLength
         | 
| 1034 1052 |  | 
| 1035 | 
            -
                  def fetch_access_token! | 
| 1036 | 
            -
                    token_hash =  | 
| 1053 | 
            +
                  def fetch_access_token! options = {}
         | 
| 1054 | 
            +
                    token_hash = fetch_access_token options
         | 
| 1037 1055 | 
             
                    if token_hash
         | 
| 1038 1056 | 
             
                      # No-op for grant types other than `authorization_code`.
         | 
| 1039 1057 | 
             
                      # An authorization code is a one-time use token and is immediately
         | 
| 1040 1058 | 
             
                      # revoked after usage.
         | 
| 1041 1059 | 
             
                      self.code = nil
         | 
| 1042 1060 | 
             
                      self.issued_at = Time.now
         | 
| 1043 | 
            -
                       | 
| 1061 | 
            +
                      update_token! token_hash
         | 
| 1044 1062 | 
             
                    end
         | 
| 1045 | 
            -
                     | 
| 1063 | 
            +
                    token_hash
         | 
| 1046 1064 | 
             
                  end
         | 
| 1047 1065 |  | 
| 1048 1066 | 
             
                  ##
         | 
| 1049 1067 | 
             
                  # Refresh the access token, if possible
         | 
| 1050 | 
            -
                  def refresh! | 
| 1051 | 
            -
                     | 
| 1068 | 
            +
                  def refresh! options = {}
         | 
| 1069 | 
            +
                    fetch_access_token! options
         | 
| 1052 1070 | 
             
                  end
         | 
| 1071 | 
            +
                  # rubocop:disable Metrics/AbcSize
         | 
| 1072 | 
            +
                  # rubocop:disable Metrics/MethodLength
         | 
| 1073 | 
            +
                  # rubocop:disable Metrics/PerceivedComplexity
         | 
| 1053 1074 |  | 
| 1054 1075 | 
             
                  ##
         | 
| 1055 1076 | 
             
                  # Generates an authenticated request for protected resources.
         | 
| @@ -1071,55 +1092,54 @@ module Signet | |
| 1071 1092 | 
             
                  #   - <code>:realm</code> -
         | 
| 1072 1093 | 
             
                  #     The Authorization realm.  See RFC 2617.
         | 
| 1073 1094 | 
             
                  # @return [Faraday::Request] The request object.
         | 
| 1074 | 
            -
                  def generate_authenticated_request | 
| 1075 | 
            -
                    options = deep_hash_normalize | 
| 1095 | 
            +
                  def generate_authenticated_request options = {}
         | 
| 1096 | 
            +
                    options = deep_hash_normalize options
         | 
| 1076 1097 |  | 
| 1077 | 
            -
                     | 
| 1078 | 
            -
                      raise ArgumentError, 'Missing access token.'
         | 
| 1079 | 
            -
                    end
         | 
| 1098 | 
            +
                    raise ArgumentError, "Missing access token." if access_token.nil?
         | 
| 1080 1099 | 
             
                    options = {
         | 
| 1081 | 
            -
                      : | 
| 1100 | 
            +
                      realm: nil
         | 
| 1082 1101 | 
             
                    }.merge(options)
         | 
| 1083 1102 |  | 
| 1084 | 
            -
                    if options[:request]. | 
| 1103 | 
            +
                    if options[:request].is_a? Faraday::Request
         | 
| 1085 1104 | 
             
                      request = options[:request]
         | 
| 1086 1105 | 
             
                    else
         | 
| 1087 | 
            -
                      if options[:request]. | 
| 1106 | 
            +
                      if options[:request].is_a? Array
         | 
| 1088 1107 | 
             
                        method, uri, headers, body = options[:request]
         | 
| 1089 1108 | 
             
                      else
         | 
| 1090 1109 | 
             
                        method = options[:method] || :get
         | 
| 1091 1110 | 
             
                        uri = options[:uri]
         | 
| 1092 1111 | 
             
                        headers = options[:headers] || []
         | 
| 1093 | 
            -
                        body = options[:body] ||  | 
| 1112 | 
            +
                        body = options[:body] || ""
         | 
| 1094 1113 | 
             
                      end
         | 
| 1095 | 
            -
                      headers = headers.to_a if headers. | 
| 1114 | 
            +
                      headers = headers.to_a if headers.is_a? Hash
         | 
| 1096 1115 | 
             
                      request_components = {
         | 
| 1097 | 
            -
                        :method | 
| 1098 | 
            -
                        :uri | 
| 1099 | 
            -
                        : | 
| 1100 | 
            -
                        :body | 
| 1116 | 
            +
                        method:  method,
         | 
| 1117 | 
            +
                        uri:     uri,
         | 
| 1118 | 
            +
                        headers: headers,
         | 
| 1119 | 
            +
                        body:    body
         | 
| 1101 1120 | 
             
                      }
         | 
| 1102 1121 | 
             
                      # Verify that we have all pieces required to return an HTTP request
         | 
| 1103 1122 | 
             
                      request_components.each do |(key, value)|
         | 
| 1104 | 
            -
                        unless value
         | 
| 1105 | 
            -
                          raise ArgumentError, "Missing :#{key} parameter."
         | 
| 1106 | 
            -
                        end
         | 
| 1123 | 
            +
                        raise ArgumentError, "Missing :#{key} parameter." unless value
         | 
| 1107 1124 | 
             
                      end
         | 
| 1108 1125 | 
             
                      method = method.to_s.downcase.to_sym
         | 
| 1109 | 
            -
                      request = options[:connection].build_request | 
| 1110 | 
            -
                        req.url | 
| 1111 | 
            -
                        req.headers = Faraday::Utils::Headers.new | 
| 1126 | 
            +
                      request = options[:connection].build_request method.to_s.downcase.to_sym do |req|
         | 
| 1127 | 
            +
                        req.url Addressable::URI.parse(uri).normalize.to_s
         | 
| 1128 | 
            +
                        req.headers = Faraday::Utils::Headers.new headers
         | 
| 1112 1129 | 
             
                        req.body = body
         | 
| 1113 1130 | 
             
                      end
         | 
| 1114 1131 | 
             
                    end
         | 
| 1115 1132 |  | 
| 1116 | 
            -
                    request[ | 
| 1117 | 
            -
                       | 
| 1118 | 
            -
                      options[:realm] ? [[ | 
| 1133 | 
            +
                    request["Authorization"] = ::Signet::OAuth2.generate_bearer_authorization_header(
         | 
| 1134 | 
            +
                      access_token,
         | 
| 1135 | 
            +
                      options[:realm] ? [["realm", options[:realm]]] : nil
         | 
| 1119 1136 | 
             
                    )
         | 
| 1120 | 
            -
                    request[ | 
| 1121 | 
            -
                     | 
| 1137 | 
            +
                    request["Cache-Control"] = "no-store"
         | 
| 1138 | 
            +
                    request
         | 
| 1122 1139 | 
             
                  end
         | 
| 1140 | 
            +
                  # rubocop:enable Metrics/AbcSize
         | 
| 1141 | 
            +
                  # rubocop:enable Metrics/MethodLength
         | 
| 1142 | 
            +
                  # rubocop:enable Metrics/PerceivedComplexity
         | 
| 1123 1143 |  | 
| 1124 1144 | 
             
                  ##
         | 
| 1125 1145 | 
             
                  # Transmits a request for a protected resource.
         | 
| @@ -1151,27 +1171,22 @@ module Signet | |
| 1151 1171 | 
             
                  #   )
         | 
| 1152 1172 | 
             
                  #
         | 
| 1153 1173 | 
             
                  # @return [Array] The response object.
         | 
| 1154 | 
            -
                  def fetch_protected_resource | 
| 1155 | 
            -
                    options = deep_hash_normalize | 
| 1174 | 
            +
                  def fetch_protected_resource options = {}
         | 
| 1175 | 
            +
                    options = deep_hash_normalize options
         | 
| 1156 1176 |  | 
| 1157 1177 | 
             
                    options[:connection] ||= Faraday.default_connection
         | 
| 1158 | 
            -
                    request =  | 
| 1159 | 
            -
                    request_env = request.to_env | 
| 1178 | 
            +
                    request = generate_authenticated_request options
         | 
| 1179 | 
            +
                    request_env = request.to_env options[:connection]
         | 
| 1160 1180 | 
             
                    request_env[:request] ||= request
         | 
| 1161 | 
            -
                    response = options[:connection].app.call | 
| 1162 | 
            -
                     | 
| 1163 | 
            -
             | 
| 1164 | 
            -
             | 
| 1165 | 
            -
             | 
| 1166 | 
            -
             | 
| 1167 | 
            -
             | 
| 1168 | 
            -
                       | 
| 1169 | 
            -
             | 
| 1170 | 
            -
                        message, :request => request, :response => response
         | 
| 1171 | 
            -
                      )
         | 
| 1172 | 
            -
                    else
         | 
| 1173 | 
            -
                      return response
         | 
| 1174 | 
            -
                    end
         | 
| 1181 | 
            +
                    response = options[:connection].app.call request_env
         | 
| 1182 | 
            +
                    return response unless response.status.to_i == 401
         | 
| 1183 | 
            +
                    # When accessing a protected resource, we only want to raise an
         | 
| 1184 | 
            +
                    # error for 401 responses.
         | 
| 1185 | 
            +
                    message = "Authorization failed."
         | 
| 1186 | 
            +
                    message += "  Server message:\n#{response.body.to_s.strip}" unless response.body.to_s.strip.empty?
         | 
| 1187 | 
            +
                    raise ::Signet::AuthorizationError.new(
         | 
| 1188 | 
            +
                      message, request: request, response: response
         | 
| 1189 | 
            +
                    )
         | 
| 1175 1190 | 
             
                  end
         | 
| 1176 1191 |  | 
| 1177 1192 | 
             
                  private
         | 
| @@ -1179,33 +1194,33 @@ module Signet | |
| 1179 1194 | 
             
                  ##
         | 
| 1180 1195 | 
             
                  # Check if URI is Google's postmessage flow (not a valid redirect_uri by spec, but allowed)
         | 
| 1181 1196 | 
             
                  # @private
         | 
| 1182 | 
            -
                  def uri_is_postmessage? | 
| 1183 | 
            -
                     | 
| 1197 | 
            +
                  def uri_is_postmessage? uri
         | 
| 1198 | 
            +
                    uri.to_s.casecmp("postmessage").zero?
         | 
| 1184 1199 | 
             
                  end
         | 
| 1185 1200 |  | 
| 1186 1201 | 
             
                  ##
         | 
| 1187 1202 | 
             
                  # Check if the URI is a out-of-band
         | 
| 1188 1203 | 
             
                  # @private
         | 
| 1189 | 
            -
                  def uri_is_oob? | 
| 1190 | 
            -
                     | 
| 1204 | 
            +
                  def uri_is_oob? uri
         | 
| 1205 | 
            +
                    OOB_MODES.include? uri.to_s
         | 
| 1191 1206 | 
             
                  end
         | 
| 1192 1207 |  | 
| 1193 1208 | 
             
                  # Convert all keys in this hash (nested) to symbols for uniform retrieval
         | 
| 1194 | 
            -
                  def recursive_hash_normalize_keys | 
| 1209 | 
            +
                  def recursive_hash_normalize_keys val
         | 
| 1195 1210 | 
             
                    if val.is_a? Hash
         | 
| 1196 | 
            -
                      deep_hash_normalize | 
| 1211 | 
            +
                      deep_hash_normalize val
         | 
| 1197 1212 | 
             
                    else
         | 
| 1198 1213 | 
             
                      val
         | 
| 1199 1214 | 
             
                    end
         | 
| 1200 1215 | 
             
                  end
         | 
| 1201 1216 |  | 
| 1202 | 
            -
                  def deep_hash_normalize | 
| 1217 | 
            +
                  def deep_hash_normalize old_hash
         | 
| 1203 1218 | 
             
                    sym_hash = {}
         | 
| 1204 | 
            -
                    old_hash | 
| 1219 | 
            +
                    old_hash&.each { |k, v| sym_hash[k.to_sym] = recursive_hash_normalize_keys v }
         | 
| 1205 1220 | 
             
                    sym_hash
         | 
| 1206 1221 | 
             
                  end
         | 
| 1207 1222 |  | 
| 1208 | 
            -
                  def normalize_timestamp | 
| 1223 | 
            +
                  def normalize_timestamp time
         | 
| 1209 1224 | 
             
                    case time
         | 
| 1210 1225 | 
             
                    when NilClass
         | 
| 1211 1226 | 
             
                      nil
         | 
| @@ -1214,15 +1229,15 @@ module Signet | |
| 1214 1229 | 
             
                    when DateTime
         | 
| 1215 1230 | 
             
                      time.to_time
         | 
| 1216 1231 | 
             
                    when String
         | 
| 1217 | 
            -
                      Time.parse | 
| 1232 | 
            +
                      Time.parse time
         | 
| 1218 1233 | 
             
                    when Integer
         | 
| 1219 | 
            -
                      Time.at | 
| 1234 | 
            +
                      Time.at time
         | 
| 1220 1235 | 
             
                    else
         | 
| 1221 | 
            -
                       | 
| 1236 | 
            +
                      raise "Invalid time value #{time}"
         | 
| 1222 1237 | 
             
                    end
         | 
| 1223 1238 | 
             
                  end
         | 
| 1224 1239 |  | 
| 1225 | 
            -
                  def set_relative_expires_at | 
| 1240 | 
            +
                  def set_relative_expires_at issued_at, expires_in
         | 
| 1226 1241 | 
             
                    self.issued_at = issued_at
         | 
| 1227 1242 | 
             
                    # Using local expires_in because if self.expires_in is used, it returns
         | 
| 1228 1243 | 
             
                    # the time left before the token expires
         |