omniauth-auth0 2.4.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +27 -5
- data/.github/CODEOWNERS +1 -1
- data/.github/ISSUE_TEMPLATE/config.yml +8 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +39 -0
- data/.github/ISSUE_TEMPLATE/report_a_bug.md +55 -0
- data/.gitignore +2 -0
- data/CHANGELOG.md +70 -0
- data/Gemfile +1 -1
- data/README.md +99 -2
- data/lib/omniauth-auth0/version.rb +1 -1
- data/lib/omniauth/auth0/jwt_validator.rb +63 -13
- data/lib/omniauth/strategies/auth0.rb +17 -7
- data/omniauth-auth0.gemspec +3 -2
- data/spec/omniauth/auth0/jwt_validator_spec.rb +261 -33
- data/spec/omniauth/strategies/auth0_spec.rb +74 -17
- metadata +26 -11
- data/.github/ISSUE_TEMPLATE.md +0 -39
- data/Gemfile.lock +0 -167
| @@ -57,8 +57,7 @@ module OmniAuth | |
| 57 57 | 
             
                    auth_scope = session_authorize_params[:scope]
         | 
| 58 58 | 
             
                    if auth_scope.respond_to?(:include?) && auth_scope.include?('openid')
         | 
| 59 59 | 
             
                      # Make sure the ID token can be verified and decoded.
         | 
| 60 | 
            -
                       | 
| 61 | 
            -
                      auth0_jwt.verify(credentials['id_token'], session_authorize_params)
         | 
| 60 | 
            +
                      jwt_validator.verify(credentials['id_token'], session_authorize_params)
         | 
| 62 61 | 
             
                    end
         | 
| 63 62 |  | 
| 64 63 | 
             
                    credentials
         | 
| @@ -85,9 +84,8 @@ module OmniAuth | |
| 85 84 | 
             
                  # Define the parameters used for the /authorize endpoint
         | 
| 86 85 | 
             
                  def authorize_params
         | 
| 87 86 | 
             
                    params = super
         | 
| 88 | 
            -
                     | 
| 89 | 
            -
             | 
| 90 | 
            -
                      params[key] = parsed_query[key] if parsed_query.key?(key)
         | 
| 87 | 
            +
                    %w[connection connection_scope prompt screen_hint login_hint organization invitation].each do |key|
         | 
| 88 | 
            +
                      params[key] = request.params[key] if request.params.key?(key)
         | 
| 91 89 | 
             
                    end
         | 
| 92 90 |  | 
| 93 91 | 
             
                    # Generate nonce
         | 
| @@ -130,11 +128,23 @@ module OmniAuth | |
| 130 128 | 
             
                  end
         | 
| 131 129 |  | 
| 132 130 | 
             
                  private
         | 
| 131 | 
            +
                  def jwt_validator
         | 
| 132 | 
            +
                    @jwt_validator ||= OmniAuth::Auth0::JWTValidator.new(options)
         | 
| 133 | 
            +
                  end
         | 
| 133 134 |  | 
| 134 135 | 
             
                  # Parse the raw user info.
         | 
| 135 136 | 
             
                  def raw_info
         | 
| 136 | 
            -
                     | 
| 137 | 
            -
             | 
| 137 | 
            +
                    return @raw_info if @raw_info
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                    if access_token["id_token"]
         | 
| 140 | 
            +
                      claims, header = jwt_validator.decode(access_token["id_token"])
         | 
| 141 | 
            +
                      @raw_info = claims
         | 
| 142 | 
            +
                    else
         | 
| 143 | 
            +
                      userinfo_url = options.client_options.userinfo_url
         | 
| 144 | 
            +
                      @raw_info = access_token.get(userinfo_url).parsed
         | 
| 145 | 
            +
                    end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    return @raw_info
         | 
| 138 148 | 
             
                  end
         | 
| 139 149 |  | 
| 140 150 | 
             
                  # Check if the options include a client_id
         | 
    
        data/omniauth-auth0.gemspec
    CHANGED
    
    | @@ -21,9 +21,10 @@ omniauth-auth0 is the OmniAuth strategy for Auth0. | |
| 21 21 | 
             
              s.executables   = `git ls-files -- bin/*`.split('\n').map{ |f| File.basename(f) }
         | 
| 22 22 | 
             
              s.require_paths = ['lib']
         | 
| 23 23 |  | 
| 24 | 
            -
              s.add_runtime_dependency 'omniauth | 
| 24 | 
            +
              s.add_runtime_dependency 'omniauth', '~> 2.0'
         | 
| 25 | 
            +
              s.add_runtime_dependency 'omniauth-oauth2', '~> 1.7'
         | 
| 25 26 |  | 
| 26 | 
            -
              s.add_development_dependency 'bundler' | 
| 27 | 
            +
              s.add_development_dependency 'bundler'
         | 
| 27 28 |  | 
| 28 29 | 
             
              s.license = 'MIT'
         | 
| 29 30 | 
             
            end
         | 
| @@ -12,17 +12,17 @@ describe OmniAuth::Auth0::JWTValidator do | |
| 12 12 | 
             
              let(:domain) { 'samples.auth0.com' }
         | 
| 13 13 | 
             
              let(:future_timecode) { 32_503_680_000 }
         | 
| 14 14 | 
             
              let(:past_timecode) { 303_912_000 }
         | 
| 15 | 
            -
              let(: | 
| 15 | 
            +
              let(:valid_jwks_kid) { 'NkJCQzIyQzRBMEU4NjhGNUU4MzU4RkY0M0ZDQzkwOUQ0Q0VGNUMwQg' }
         | 
| 16 16 |  | 
| 17 17 | 
             
              let(:rsa_private_key) do
         | 
| 18 18 | 
             
                OpenSSL::PKey::RSA.generate 2048
         | 
| 19 19 | 
             
              end
         | 
| 20 20 |  | 
| 21 | 
            -
              let(: | 
| 21 | 
            +
              let(:valid_jwks) do
         | 
| 22 22 | 
             
                {
         | 
| 23 23 | 
             
                  keys: [
         | 
| 24 24 | 
             
                    {
         | 
| 25 | 
            -
                      kid:  | 
| 25 | 
            +
                      kid: valid_jwks_kid,
         | 
| 26 26 | 
             
                      x5c: [Base64.encode64(make_cert(rsa_private_key).to_der)]
         | 
| 27 27 | 
             
                    }
         | 
| 28 28 | 
             
                  ]
         | 
| @@ -91,29 +91,29 @@ describe OmniAuth::Auth0::JWTValidator do | |
| 91 91 | 
             
                end
         | 
| 92 92 | 
             
              end
         | 
| 93 93 |  | 
| 94 | 
            -
              describe 'JWT verifier  | 
| 94 | 
            +
              describe 'JWT verifier jwks key parsing' do
         | 
| 95 95 | 
             
                let(:jwt_validator) do
         | 
| 96 96 | 
             
                  make_jwt_validator
         | 
| 97 97 | 
             
                end
         | 
| 98 98 |  | 
| 99 99 | 
             
                before do
         | 
| 100 | 
            -
                   | 
| 100 | 
            +
                  stub_complete_jwks
         | 
| 101 101 | 
             
                end
         | 
| 102 102 |  | 
| 103 103 | 
             
                it 'should return a key' do
         | 
| 104 | 
            -
                  expect(jwt_validator.jwks_key(:alg,  | 
| 104 | 
            +
                  expect(jwt_validator.jwks_key(:alg, valid_jwks_kid)).to eq('RS256')
         | 
| 105 105 | 
             
                end
         | 
| 106 106 |  | 
| 107 107 | 
             
                it 'should return an x5c key' do
         | 
| 108 | 
            -
                  expect(jwt_validator.jwks_key(:x5c,  | 
| 108 | 
            +
                  expect(jwt_validator.jwks_key(:x5c, valid_jwks_kid).length).to eq(1)
         | 
| 109 109 | 
             
                end
         | 
| 110 110 |  | 
| 111 111 | 
             
                it 'should return nil if there is not key' do
         | 
| 112 | 
            -
                  expect(jwt_validator.jwks_key(:auth0,  | 
| 112 | 
            +
                  expect(jwt_validator.jwks_key(:auth0, valid_jwks_kid)).to eq(nil)
         | 
| 113 113 | 
             
                end
         | 
| 114 114 |  | 
| 115 115 | 
             
                it 'should return nil if the key ID is invalid' do
         | 
| 116 | 
            -
                  expect(jwt_validator.jwks_key(:alg, "#{ | 
| 116 | 
            +
                  expect(jwt_validator.jwks_key(:alg, "#{valid_jwks_kid}_invalid")).to eq(nil)
         | 
| 117 117 | 
             
                end
         | 
| 118 118 | 
             
              end
         | 
| 119 119 |  | 
| @@ -133,16 +133,26 @@ describe OmniAuth::Auth0::JWTValidator do | |
| 133 133 | 
             
                end
         | 
| 134 134 |  | 
| 135 135 | 
             
                context 'different from domain' do
         | 
| 136 | 
            -
                   | 
| 137 | 
            -
                    make_jwt_validator(opt_issuer:  | 
| 136 | 
            +
                  shared_examples_for 'has correct issuer and domain' do
         | 
| 137 | 
            +
                    let(:jwt_validator) { make_jwt_validator(opt_issuer: opt_issuer) }
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                    it 'should have the correct issuer' do
         | 
| 140 | 
            +
                      expect(jwt_validator.issuer).to eq('https://different.auth0.com/')
         | 
| 141 | 
            +
                    end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    it 'should have the correct domain' do
         | 
| 144 | 
            +
                      expect(jwt_validator.domain).to eq('https://samples.auth0.com/')
         | 
| 145 | 
            +
                    end
         | 
| 138 146 | 
             
                  end
         | 
| 139 147 |  | 
| 140 | 
            -
                   | 
| 141 | 
            -
                     | 
| 148 | 
            +
                  context 'without protocol and trailing slash' do
         | 
| 149 | 
            +
                    let(:opt_issuer) { 'different.auth0.com' }
         | 
| 150 | 
            +
                    it_behaves_like 'has correct issuer and domain'
         | 
| 142 151 | 
             
                  end
         | 
| 143 152 |  | 
| 144 | 
            -
                   | 
| 145 | 
            -
                     | 
| 153 | 
            +
                  context 'with protocol and trailing slash' do
         | 
| 154 | 
            +
                    let(:opt_issuer) { 'https://different.auth0.com/' }
         | 
| 155 | 
            +
                    it_behaves_like 'has correct issuer and domain'
         | 
| 146 156 | 
             
                  end
         | 
| 147 157 | 
             
                end
         | 
| 148 158 | 
             
              end
         | 
| @@ -153,8 +163,24 @@ describe OmniAuth::Auth0::JWTValidator do | |
| 153 163 | 
             
                end
         | 
| 154 164 |  | 
| 155 165 | 
             
                before do
         | 
| 156 | 
            -
                   | 
| 157 | 
            -
                   | 
| 166 | 
            +
                  stub_complete_jwks
         | 
| 167 | 
            +
                  stub_expected_jwks
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                it 'should fail when JWT is nil' do
         | 
| 171 | 
            +
                  expect do
         | 
| 172 | 
            +
                    jwt_validator.verify(nil)
         | 
| 173 | 
            +
                  end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
         | 
| 174 | 
            +
                    message: "ID token is required but missing"
         | 
| 175 | 
            +
                  }))
         | 
| 176 | 
            +
                end
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                it 'should fail when JWT is not well-formed' do
         | 
| 179 | 
            +
                  expect do
         | 
| 180 | 
            +
                    jwt_validator.verify('abc.123')
         | 
| 181 | 
            +
                  end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
         | 
| 182 | 
            +
                    message: "ID token could not be decoded"
         | 
| 183 | 
            +
                  }))
         | 
| 158 184 | 
             
                end
         | 
| 159 185 |  | 
| 160 186 | 
             
                it 'should fail with missing issuer' do
         | 
| @@ -248,6 +274,39 @@ describe OmniAuth::Auth0::JWTValidator do | |
| 248 274 | 
             
                  }))
         | 
| 249 275 | 
             
                end
         | 
| 250 276 |  | 
| 277 | 
            +
                it 'should pass when past expiration but within default leeway' do
         | 
| 278 | 
            +
                  exp = Time.now.to_i - 59
         | 
| 279 | 
            +
                  payload = {
         | 
| 280 | 
            +
                    iss: "https://#{domain}/",
         | 
| 281 | 
            +
                    sub: 'sub',
         | 
| 282 | 
            +
                    aud: client_id,
         | 
| 283 | 
            +
                    exp: exp,
         | 
| 284 | 
            +
                    iat: past_timecode
         | 
| 285 | 
            +
                  }
         | 
| 286 | 
            +
             | 
| 287 | 
            +
                  token = make_hs256_token(payload)
         | 
| 288 | 
            +
                  id_token = jwt_validator.verify(token)
         | 
| 289 | 
            +
                  expect(id_token['exp']).to eq(exp)
         | 
| 290 | 
            +
                end
         | 
| 291 | 
            +
             | 
| 292 | 
            +
                it 'should fail when past expiration and outside default leeway' do
         | 
| 293 | 
            +
                  exp = Time.now.to_i - 61
         | 
| 294 | 
            +
                  payload = {
         | 
| 295 | 
            +
                    iss: "https://#{domain}/",
         | 
| 296 | 
            +
                    sub: 'sub',
         | 
| 297 | 
            +
                    aud: client_id,
         | 
| 298 | 
            +
                    exp: exp,
         | 
| 299 | 
            +
                    iat: past_timecode
         | 
| 300 | 
            +
                  }
         | 
| 301 | 
            +
             | 
| 302 | 
            +
                  token = make_hs256_token(payload)
         | 
| 303 | 
            +
                  expect do
         | 
| 304 | 
            +
                    jwt_validator.verify(token)
         | 
| 305 | 
            +
                  end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
         | 
| 306 | 
            +
                    message: "Expiration time (exp) claim error in the ID token; current time (#{Time.now}) is after expiration time (#{Time.at(exp + 60)})"
         | 
| 307 | 
            +
                  }))
         | 
| 308 | 
            +
                end
         | 
| 309 | 
            +
             | 
| 251 310 | 
             
                it 'should fail when missing iat' do
         | 
| 252 311 | 
             
                  payload = {
         | 
| 253 312 | 
             
                    iss: "https://#{domain}/",
         | 
| @@ -377,6 +436,149 @@ describe OmniAuth::Auth0::JWTValidator do | |
| 377 436 | 
             
                  }))
         | 
| 378 437 | 
             
                end
         | 
| 379 438 |  | 
| 439 | 
            +
                it 'should fail when “max_age” sent on the authentication request and this claim added the “max_age” value doesn’t represent a date in the future, outside the default leeway' do
         | 
| 440 | 
            +
                  now = Time.now.to_i
         | 
| 441 | 
            +
                  auth_time = now - 121
         | 
| 442 | 
            +
                  max_age = 60
         | 
| 443 | 
            +
                  payload = {
         | 
| 444 | 
            +
                    iss: "https://#{domain}/",
         | 
| 445 | 
            +
                    sub: 'sub',
         | 
| 446 | 
            +
                    aud: client_id,
         | 
| 447 | 
            +
                    exp: future_timecode,
         | 
| 448 | 
            +
                    iat: past_timecode,
         | 
| 449 | 
            +
                    auth_time: auth_time
         | 
| 450 | 
            +
                  }
         | 
| 451 | 
            +
             | 
| 452 | 
            +
                  token = make_hs256_token(payload)
         | 
| 453 | 
            +
                  expect do
         | 
| 454 | 
            +
                    jwt_validator.verify(token, { max_age: max_age })
         | 
| 455 | 
            +
                    # Time.at(auth_time + max_age + leeway
         | 
| 456 | 
            +
                  end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
         | 
| 457 | 
            +
                    message: "Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication. Current time (#{Time.now}) is after last auth time (#{Time.at(auth_time + max_age + 60)})"
         | 
| 458 | 
            +
                  }))
         | 
| 459 | 
            +
                end
         | 
| 460 | 
            +
             | 
| 461 | 
            +
                it 'should verify when “max_age” sent on the authentication request and this claim added the “max_age” value doesn’t represent a date in the future, outside the default leeway' do
         | 
| 462 | 
            +
                  now = Time.now.to_i
         | 
| 463 | 
            +
                  auth_time = now - 119
         | 
| 464 | 
            +
                  max_age = 60
         | 
| 465 | 
            +
                  payload = {
         | 
| 466 | 
            +
                    iss: "https://#{domain}/",
         | 
| 467 | 
            +
                    sub: 'sub',
         | 
| 468 | 
            +
                    aud: client_id,
         | 
| 469 | 
            +
                    exp: future_timecode,
         | 
| 470 | 
            +
                    iat: past_timecode,
         | 
| 471 | 
            +
                    auth_time: auth_time
         | 
| 472 | 
            +
                  }
         | 
| 473 | 
            +
             | 
| 474 | 
            +
                  token = make_hs256_token(payload)
         | 
| 475 | 
            +
                  id_token = jwt_validator.verify(token, { max_age: max_age })
         | 
| 476 | 
            +
                  expect(id_token['auth_time']).to eq(auth_time)
         | 
| 477 | 
            +
                end
         | 
| 478 | 
            +
             | 
| 479 | 
            +
                it 'should fail when authorize params has organization but org_id is missing in the token', focus: true do
         | 
| 480 | 
            +
                  payload = {
         | 
| 481 | 
            +
                    iss: "https://#{domain}/",
         | 
| 482 | 
            +
                    sub: 'sub',
         | 
| 483 | 
            +
                    aud: client_id,
         | 
| 484 | 
            +
                    exp: future_timecode,
         | 
| 485 | 
            +
                    iat: past_timecode
         | 
| 486 | 
            +
                  }
         | 
| 487 | 
            +
             | 
| 488 | 
            +
                  token = make_hs256_token(payload)
         | 
| 489 | 
            +
                  expect do
         | 
| 490 | 
            +
                    jwt_validator.verify(token, { organization: 'Test Org' })
         | 
| 491 | 
            +
                  end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
         | 
| 492 | 
            +
                    message: "Organization Id (org_id) claim must be a string present in the ID token"
         | 
| 493 | 
            +
                  }))
         | 
| 494 | 
            +
                end
         | 
| 495 | 
            +
             | 
| 496 | 
            +
                it 'should fail when authorize params has organization but token org_id does not match', focus: true do
         | 
| 497 | 
            +
                  payload = {
         | 
| 498 | 
            +
                    iss: "https://#{domain}/",
         | 
| 499 | 
            +
                    sub: 'sub',
         | 
| 500 | 
            +
                    aud: client_id,
         | 
| 501 | 
            +
                    exp: future_timecode,
         | 
| 502 | 
            +
                    iat: past_timecode,
         | 
| 503 | 
            +
                    org_id: 'Wrong Org'
         | 
| 504 | 
            +
                  }
         | 
| 505 | 
            +
             | 
| 506 | 
            +
                  token = make_hs256_token(payload)
         | 
| 507 | 
            +
                  expect do
         | 
| 508 | 
            +
                    jwt_validator.verify(token, { organization: 'Test Org' })
         | 
| 509 | 
            +
                  end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
         | 
| 510 | 
            +
                    message: "Organization Id (org_id) claim value mismatch in the ID token; expected 'Test Org', found 'Wrong Org'"
         | 
| 511 | 
            +
                  }))
         | 
| 512 | 
            +
                end
         | 
| 513 | 
            +
             | 
| 514 | 
            +
                it 'should fail for RS256 token when kid is incorrect' do
         | 
| 515 | 
            +
                  domain = 'example.org'
         | 
| 516 | 
            +
                  sub = 'abc123'
         | 
| 517 | 
            +
                  payload = {
         | 
| 518 | 
            +
                    sub: sub,
         | 
| 519 | 
            +
                    exp: future_timecode,
         | 
| 520 | 
            +
                    iss: "https://#{domain}/",
         | 
| 521 | 
            +
                    iat: past_timecode,
         | 
| 522 | 
            +
                    aud: client_id
         | 
| 523 | 
            +
                  }
         | 
| 524 | 
            +
                  invalid_kid = 'invalid-kid'
         | 
| 525 | 
            +
                  token = make_rs256_token(payload, invalid_kid)
         | 
| 526 | 
            +
                  expect do
         | 
| 527 | 
            +
                    verified_token = make_jwt_validator(opt_domain: domain).verify(token)
         | 
| 528 | 
            +
                  end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
         | 
| 529 | 
            +
                    message: "Could not find a public key for Key ID (kid) 'invalid-kid'"
         | 
| 530 | 
            +
                  }))
         | 
| 531 | 
            +
                end
         | 
| 532 | 
            +
             | 
| 533 | 
            +
                it 'should fail when RS256 token has invalid signature' do
         | 
| 534 | 
            +
                  domain = 'example.org'
         | 
| 535 | 
            +
                  sub = 'abc123'
         | 
| 536 | 
            +
                  payload = {
         | 
| 537 | 
            +
                    sub: sub,
         | 
| 538 | 
            +
                    exp: future_timecode,
         | 
| 539 | 
            +
                    iss: "https://#{domain}/",
         | 
| 540 | 
            +
                    iat: past_timecode,
         | 
| 541 | 
            +
                    aud: client_id
         | 
| 542 | 
            +
                  }
         | 
| 543 | 
            +
                  token = make_rs256_token(payload) + 'bad'
         | 
| 544 | 
            +
                  expect do
         | 
| 545 | 
            +
                    verified_token = make_jwt_validator(opt_domain: domain).verify(token)
         | 
| 546 | 
            +
                  end.to raise_error(an_instance_of(JWT::VerificationError).and having_attributes({
         | 
| 547 | 
            +
                    message: "Signature verification raised"
         | 
| 548 | 
            +
                  }))
         | 
| 549 | 
            +
                end
         | 
| 550 | 
            +
             | 
| 551 | 
            +
                it 'should fail when algorithm is not RS256 or HS256' do
         | 
| 552 | 
            +
                  payload = {
         | 
| 553 | 
            +
                    iss: "https://#{domain}/",
         | 
| 554 | 
            +
                    sub: 'abc123',
         | 
| 555 | 
            +
                    aud: client_id,
         | 
| 556 | 
            +
                    exp: future_timecode,
         | 
| 557 | 
            +
                    iat: past_timecode
         | 
| 558 | 
            +
                  }
         | 
| 559 | 
            +
                  token = JWT.encode payload, 'secret', 'HS384'
         | 
| 560 | 
            +
                  expect do
         | 
| 561 | 
            +
                    jwt_validator.verify(token)
         | 
| 562 | 
            +
                  end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
         | 
| 563 | 
            +
                    message: "Signature algorithm of HS384 is not supported. Expected the ID token to be signed with RS256 or HS256"
         | 
| 564 | 
            +
                  }))
         | 
| 565 | 
            +
                end
         | 
| 566 | 
            +
             | 
| 567 | 
            +
                it 'should fail when HS256 token has invalid signature' do
         | 
| 568 | 
            +
                  payload = {
         | 
| 569 | 
            +
                    iss: "https://#{domain}/",
         | 
| 570 | 
            +
                    sub: 'abc123',
         | 
| 571 | 
            +
                    aud: client_id,
         | 
| 572 | 
            +
                    exp: future_timecode,
         | 
| 573 | 
            +
                    iat: past_timecode
         | 
| 574 | 
            +
                  }
         | 
| 575 | 
            +
                  token = make_hs256_token(payload, 'bad_secret')
         | 
| 576 | 
            +
                  expect do
         | 
| 577 | 
            +
                    # validator is configured to use "CLIENT_SECRET" by default
         | 
| 578 | 
            +
                    jwt_validator.verify(token)
         | 
| 579 | 
            +
                  end.to raise_error(an_instance_of(JWT::VerificationError))
         | 
| 580 | 
            +
                end
         | 
| 581 | 
            +
             | 
| 380 582 | 
             
                it 'should verify a valid HS256 token with multiple audiences' do
         | 
| 381 583 | 
             
                  audience = [
         | 
| 382 584 | 
             
                    client_id,
         | 
| @@ -417,13 +619,44 @@ describe OmniAuth::Auth0::JWTValidator do | |
| 417 619 | 
             
                    exp: future_timecode,
         | 
| 418 620 | 
             
                    iss: "https://#{domain}/",
         | 
| 419 621 | 
             
                    iat: past_timecode,
         | 
| 420 | 
            -
                    aud: client_id | 
| 421 | 
            -
                    kid: jwks_kid
         | 
| 622 | 
            +
                    aud: client_id
         | 
| 422 623 | 
             
                  }
         | 
| 423 624 | 
             
                  token = make_rs256_token(payload)
         | 
| 424 625 | 
             
                  verified_token = make_jwt_validator(opt_domain: domain).verify(token)
         | 
| 425 626 | 
             
                  expect(verified_token['sub']).to eq(sub)
         | 
| 426 627 | 
             
                end
         | 
| 628 | 
            +
             | 
| 629 | 
            +
                it 'should verify a HS256 JWT signature when calling verify signature directly' do
         | 
| 630 | 
            +
                  sub = 'abc123'
         | 
| 631 | 
            +
                    payload = {
         | 
| 632 | 
            +
                      iss: "https://#{domain}/",
         | 
| 633 | 
            +
                      sub: sub,
         | 
| 634 | 
            +
                      aud: client_id,
         | 
| 635 | 
            +
                      exp: future_timecode,
         | 
| 636 | 
            +
                      iat: past_timecode
         | 
| 637 | 
            +
                    }
         | 
| 638 | 
            +
                    token = make_hs256_token(payload)
         | 
| 639 | 
            +
                    verified_token_signature = jwt_validator.verify_signature(token)
         | 
| 640 | 
            +
                    expect(verified_token_signature[0]).to eq('CLIENT_SECRET')
         | 
| 641 | 
            +
                    expect(verified_token_signature[1]).to eq('HS256')
         | 
| 642 | 
            +
                end
         | 
| 643 | 
            +
             | 
| 644 | 
            +
                it 'should verify a RS256 JWT signature verify signature directly' do
         | 
| 645 | 
            +
                  domain = 'example.org'
         | 
| 646 | 
            +
                  sub = 'abc123'
         | 
| 647 | 
            +
                  payload = {
         | 
| 648 | 
            +
                    sub: sub,
         | 
| 649 | 
            +
                    exp: future_timecode,
         | 
| 650 | 
            +
                    iss: "https://#{domain}/",
         | 
| 651 | 
            +
                    iat: past_timecode,
         | 
| 652 | 
            +
                    aud: client_id
         | 
| 653 | 
            +
                  }
         | 
| 654 | 
            +
                  token = make_rs256_token(payload)
         | 
| 655 | 
            +
                  verified_token_signature = make_jwt_validator(opt_domain: domain).verify_signature(token)
         | 
| 656 | 
            +
                  expect(verified_token_signature.length).to be(2)
         | 
| 657 | 
            +
                  expect(verified_token_signature[0]).to be_a(OpenSSL::PKey::RSA)
         | 
| 658 | 
            +
                  expect(verified_token_signature[1]).to eq('RS256')
         | 
| 659 | 
            +
                end
         | 
| 427 660 | 
             
              end
         | 
| 428 661 |  | 
| 429 662 | 
             
              private
         | 
| @@ -439,14 +672,16 @@ describe OmniAuth::Auth0::JWTValidator do | |
| 439 672 | 
             
                OmniAuth::Auth0::JWTValidator.new(opts)
         | 
| 440 673 | 
             
              end
         | 
| 441 674 |  | 
| 442 | 
            -
              def make_hs256_token(payload = nil)
         | 
| 675 | 
            +
              def make_hs256_token(payload = nil, secret = nil)
         | 
| 443 676 | 
             
                payload = { sub: 'abc123' } if payload.nil?
         | 
| 444 | 
            -
                 | 
| 677 | 
            +
                secret = client_secret if secret.nil?
         | 
| 678 | 
            +
                JWT.encode payload, secret, 'HS256'
         | 
| 445 679 | 
             
              end
         | 
| 446 680 |  | 
| 447 | 
            -
              def make_rs256_token(payload = nil)
         | 
| 681 | 
            +
              def make_rs256_token(payload = nil, kid = nil)
         | 
| 448 682 | 
             
                payload = { sub: 'abc123' } if payload.nil?
         | 
| 449 | 
            -
                 | 
| 683 | 
            +
                kid = valid_jwks_kid if kid.nil?
         | 
| 684 | 
            +
                JWT.encode payload, rsa_private_key, 'RS256', kid: kid
         | 
| 450 685 | 
             
              end
         | 
| 451 686 |  | 
| 452 687 | 
             
              def make_cert(private_key)
         | 
| @@ -474,7 +709,7 @@ describe OmniAuth::Auth0::JWTValidator do | |
| 474 709 | 
             
                cert.sign private_key, OpenSSL::Digest::SHA1.new
         | 
| 475 710 | 
             
              end
         | 
| 476 711 |  | 
| 477 | 
            -
              def  | 
| 712 | 
            +
              def stub_complete_jwks
         | 
| 478 713 | 
             
                stub_request(:get, 'https://samples.auth0.com/.well-known/jwks.json')
         | 
| 479 714 | 
             
                  .to_return(
         | 
| 480 715 | 
             
                    headers: { 'Content-Type' => 'application/json' },
         | 
| @@ -483,18 +718,11 @@ describe OmniAuth::Auth0::JWTValidator do | |
| 483 718 | 
             
                  )
         | 
| 484 719 | 
             
              end
         | 
| 485 720 |  | 
| 486 | 
            -
              def  | 
| 487 | 
            -
                stub_request(:get, 'https://samples.auth0.com/.well-known/jwks-bad.json')
         | 
| 488 | 
            -
                  .to_return(
         | 
| 489 | 
            -
                    status: 404
         | 
| 490 | 
            -
                  )
         | 
| 491 | 
            -
              end
         | 
| 492 | 
            -
             | 
| 493 | 
            -
              def stub_dummy_jwks
         | 
| 721 | 
            +
              def stub_expected_jwks
         | 
| 494 722 | 
             
                stub_request(:get, 'https://example.org/.well-known/jwks.json')
         | 
| 495 723 | 
             
                  .to_return(
         | 
| 496 724 | 
             
                    headers: { 'Content-Type' => 'application/json' },
         | 
| 497 | 
            -
                    body:  | 
| 725 | 
            +
                    body: valid_jwks,
         | 
| 498 726 | 
             
                    status: 200
         | 
| 499 727 | 
             
                  )
         | 
| 500 728 | 
             
              end
         |