omniauth_openid_connect 0.3.5 → 0.5.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/.github/workflows/main.yml +63 -0
- data/.rubocop.yml +8 -5
- data/CHANGELOG.md +16 -0
- data/Gemfile +6 -0
- data/Guardfile +1 -1
- data/README.md +32 -20
- data/Rakefile +2 -0
- data/lib/omniauth/openid_connect/errors.rb +2 -0
- data/lib/omniauth/openid_connect/version.rb +1 -1
- data/lib/omniauth/strategies/openid_connect.rb +66 -18
- data/omniauth_openid_connect.gemspec +13 -6
- data/test/lib/omniauth/strategies/openid_connect_test.rb +226 -79
- data/test/strategy_test_case.rb +37 -3
- data/test/test_helper.rb +17 -7
- metadata +41 -50
- data/.github/config/rubocop_linter_action.yml +0 -59
- data/.github/workflows/rubocop.yml +0 -22
- data/.travis.yml +0 -8
- data/test/fixtures/id_token.txt +0 -1
- data/test/fixtures/jwks.json +0 -8
| @@ -1,8 +1,10 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require_relative '../../../test_helper'
         | 
| 2 4 |  | 
| 3 5 | 
             
            module OmniAuth
         | 
| 4 6 | 
             
              module Strategies
         | 
| 5 | 
            -
                class OpenIDConnectTest < StrategyTestCase
         | 
| 7 | 
            +
                class OpenIDConnectTest < StrategyTestCase # rubocop:disable Metrics/ClassLength
         | 
| 6 8 | 
             
                  def test_client_options_defaults
         | 
| 7 9 | 
             
                    assert_equal 'https', strategy.options.client_options.scheme
         | 
| 8 10 | 
             
                    assert_equal 443, strategy.options.client_options.port
         | 
| @@ -11,7 +13,7 @@ module OmniAuth | |
| 11 13 | 
             
                  end
         | 
| 12 14 |  | 
| 13 15 | 
             
                  def test_request_phase
         | 
| 14 | 
            -
                    expected_redirect =  | 
| 16 | 
            +
                    expected_redirect = %r{^https://example\.com/authorize\?client_id=1234&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}$}
         | 
| 15 17 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 16 18 | 
             
                    strategy.options.client_options.host = 'example.com'
         | 
| 17 19 | 
             
                    strategy.expects(:redirect).with(regexp_matches(expected_redirect))
         | 
| @@ -19,7 +21,7 @@ module OmniAuth | |
| 19 21 | 
             
                  end
         | 
| 20 22 |  | 
| 21 23 | 
             
                  def test_logout_phase_with_discovery
         | 
| 22 | 
            -
                    expected_redirect = %r{^https | 
| 24 | 
            +
                    expected_redirect = %r{^https://example\.com/logout$}
         | 
| 23 25 | 
             
                    strategy.options.client_options.host = 'example.com'
         | 
| 24 26 | 
             
                    strategy.options.discovery = true
         | 
| 25 27 |  | 
| @@ -36,6 +38,7 @@ module OmniAuth | |
| 36 38 | 
             
                    ::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config)
         | 
| 37 39 |  | 
| 38 40 | 
             
                    request.stubs(:path_info).returns('/auth/openid_connect/logout')
         | 
| 41 | 
            +
                    request.stubs(:path).returns('/auth/openid_connect/logout')
         | 
| 39 42 |  | 
| 40 43 | 
             
                    strategy.expects(:redirect).with(regexp_matches(expected_redirect))
         | 
| 41 44 | 
             
                    strategy.other_phase
         | 
| @@ -60,6 +63,7 @@ module OmniAuth | |
| 60 63 | 
             
                    ::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config)
         | 
| 61 64 |  | 
| 62 65 | 
             
                    request.stubs(:path_info).returns('/auth/openid_connect/logout')
         | 
| 66 | 
            +
                    request.stubs(:path).returns('/auth/openid_connect/logout')
         | 
| 63 67 |  | 
| 64 68 | 
             
                    strategy.expects(:redirect).with(expected_redirect)
         | 
| 65 69 | 
             
                    strategy.other_phase
         | 
| @@ -69,14 +73,14 @@ module OmniAuth | |
| 69 73 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 70 74 | 
             
                    strategy.options.client_options.host = 'example.com'
         | 
| 71 75 |  | 
| 72 | 
            -
                    request.stubs(: | 
| 76 | 
            +
                    request.stubs(:path).returns('/auth/openid_connect/logout')
         | 
| 73 77 |  | 
| 74 78 | 
             
                    strategy.expects(:call_app!)
         | 
| 75 79 | 
             
                    strategy.other_phase
         | 
| 76 80 | 
             
                  end
         | 
| 77 81 |  | 
| 78 82 | 
             
                  def test_request_phase_with_params
         | 
| 79 | 
            -
                    expected_redirect =  | 
| 83 | 
            +
                    expected_redirect = %r{^https://example\.com/authorize\?claims_locales=es&client_id=1234&login_hint=john.doe%40example.com&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}&ui_locales=en$}
         | 
| 80 84 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 81 85 | 
             
                    strategy.options.client_options.host = 'example.com'
         | 
| 82 86 | 
             
                    request.stubs(:params).returns('login_hint' => 'john.doe@example.com', 'ui_locales' => 'en', 'claims_locales' => 'es')
         | 
| @@ -86,7 +90,7 @@ module OmniAuth | |
| 86 90 | 
             
                  end
         | 
| 87 91 |  | 
| 88 92 | 
             
                  def test_request_phase_with_discovery
         | 
| 89 | 
            -
                    expected_redirect =  | 
| 93 | 
            +
                    expected_redirect = %r{^https://example\.com/authorization\?client_id=1234&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}$}
         | 
| 90 94 | 
             
                    strategy.options.client_options.host = 'example.com'
         | 
| 91 95 | 
             
                    strategy.options.discovery = true
         | 
| 92 96 |  | 
| @@ -113,7 +117,7 @@ module OmniAuth | |
| 113 117 | 
             
                  end
         | 
| 114 118 |  | 
| 115 119 | 
             
                  def test_request_phase_with_response_mode
         | 
| 116 | 
            -
                    expected_redirect =  | 
| 120 | 
            +
                    expected_redirect = %r{^https://example\.com/authorize\?client_id=1234&nonce=\w{32}&response_mode=form_post&response_type=id_token&scope=openid&state=\w{32}$}
         | 
| 117 121 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 118 122 | 
             
                    strategy.options.response_mode = 'form_post'
         | 
| 119 123 | 
             
                    strategy.options.response_type = 'id_token'
         | 
| @@ -124,7 +128,7 @@ module OmniAuth | |
| 124 128 | 
             
                  end
         | 
| 125 129 |  | 
| 126 130 | 
             
                  def test_request_phase_with_response_mode_symbol
         | 
| 127 | 
            -
                    expected_redirect =  | 
| 131 | 
            +
                    expected_redirect = %r{^https://example\.com/authorize\?client_id=1234&nonce=\w{32}&response_mode=form_post&response_type=id_token&scope=openid&state=\w{32}$}
         | 
| 128 132 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 129 133 | 
             
                    strategy.options.response_mode = 'form_post'
         | 
| 130 134 | 
             
                    strategy.options.response_type = :id_token
         | 
| @@ -137,19 +141,33 @@ module OmniAuth | |
| 137 141 | 
             
                  def test_option_acr_values
         | 
| 138 142 | 
             
                    strategy.options.client_options[:host] = 'foobar.com'
         | 
| 139 143 |  | 
| 140 | 
            -
                     | 
| 144 | 
            +
                    refute_match(/acr_values=/, strategy.authorize_uri, 'URI must not contain acr_values')
         | 
| 141 145 |  | 
| 142 146 | 
             
                    strategy.options.acr_values = 'urn:some:acr:values:value'
         | 
| 143 | 
            -
                     | 
| 147 | 
            +
                    assert_match(/acr_values=/, strategy.authorize_uri, 'URI must contain acr_values')
         | 
| 144 148 | 
             
                  end
         | 
| 145 149 |  | 
| 146 150 | 
             
                  def test_option_custom_attributes
         | 
| 147 151 | 
             
                    strategy.options.client_options[:host] = 'foobar.com'
         | 
| 148 | 
            -
                    strategy.options.extra_authorize_params = {resource: 'xyz'}
         | 
| 152 | 
            +
                    strategy.options.extra_authorize_params = { resource: 'xyz' }
         | 
| 149 153 |  | 
| 150 154 | 
             
                    assert(strategy.authorize_uri =~ /resource=xyz/, 'URI must contain custom params')
         | 
| 151 155 | 
             
                  end
         | 
| 152 156 |  | 
| 157 | 
            +
                  def test_request_phase_with_allowed_params
         | 
| 158 | 
            +
                    strategy.options.issuer = 'example.com'
         | 
| 159 | 
            +
                    strategy.options.allow_authorize_params = %i[name logo resource]
         | 
| 160 | 
            +
                    strategy.options.extra_authorize_params = { resource: 'xyz' }
         | 
| 161 | 
            +
                    strategy.options.client_options.host = 'example.com'
         | 
| 162 | 
            +
                    request.stubs(:params).returns('name' => 'example', 'logo' => 'example_logo', 'resource' => 'abc',
         | 
| 163 | 
            +
                                                   'not_allowed' => 'filter_me')
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                    assert(strategy.authorize_uri =~ /resource=xyz/, 'URI must contain fixed param resource')
         | 
| 166 | 
            +
                    assert(strategy.authorize_uri =~ /name=example/, 'URI must contain dynamic param name')
         | 
| 167 | 
            +
                    assert(strategy.authorize_uri =~ /logo=example_logo/, 'URI must contain dynamic param logo')
         | 
| 168 | 
            +
                    refute(strategy.authorize_uri =~ /not_allowed=filter_me/, 'URI must filter not allowed param')
         | 
| 169 | 
            +
                  end
         | 
| 170 | 
            +
             | 
| 153 171 | 
             
                  def test_uid
         | 
| 154 172 | 
             
                    assert_equal user_info.sub, strategy.uid
         | 
| 155 173 |  | 
| @@ -160,16 +178,15 @@ module OmniAuth | |
| 160 178 | 
             
                    assert_equal user_info.sub, strategy.uid
         | 
| 161 179 | 
             
                  end
         | 
| 162 180 |  | 
| 163 | 
            -
                  def test_callback_phase( | 
| 181 | 
            +
                  def test_callback_phase(_session = {}, _params = {}) # rubocop:disable Metrics/AbcSize
         | 
| 164 182 | 
             
                    code = SecureRandom.hex(16)
         | 
| 165 183 | 
             
                    state = SecureRandom.hex(16)
         | 
| 166 | 
            -
                    nonce = SecureRandom.hex(16)
         | 
| 167 184 | 
             
                    request.stubs(:params).returns('code' => code, 'state' => state)
         | 
| 168 | 
            -
                    request.stubs(: | 
| 185 | 
            +
                    request.stubs(:path).returns('')
         | 
| 169 186 |  | 
| 170 187 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 171 188 | 
             
                    strategy.options.client_signing_alg = :RS256
         | 
| 172 | 
            -
                    strategy.options.client_jwk_signing_key =  | 
| 189 | 
            +
                    strategy.options.client_jwk_signing_key = jwks.to_s
         | 
| 173 190 | 
             
                    strategy.options.response_type = 'code'
         | 
| 174 191 |  | 
| 175 192 | 
             
                    strategy.unstub(:user_info)
         | 
| @@ -178,7 +195,7 @@ module OmniAuth | |
| 178 195 | 
             
                    access_token.stubs(:refresh_token)
         | 
| 179 196 | 
             
                    access_token.stubs(:expires_in)
         | 
| 180 197 | 
             
                    access_token.stubs(:scope)
         | 
| 181 | 
            -
                    access_token.stubs(:id_token).returns( | 
| 198 | 
            +
                    access_token.stubs(:id_token).returns(jwt.to_s)
         | 
| 182 199 | 
             
                    client.expects(:access_token!).at_least_once.returns(access_token)
         | 
| 183 200 | 
             
                    access_token.expects(:userinfo!).returns(user_info)
         | 
| 184 201 |  | 
| @@ -193,15 +210,13 @@ module OmniAuth | |
| 193 210 | 
             
                  end
         | 
| 194 211 |  | 
| 195 212 | 
             
                  def test_callback_phase_with_id_token
         | 
| 196 | 
            -
                    code = SecureRandom.hex(16)
         | 
| 197 213 | 
             
                    state = SecureRandom.hex(16)
         | 
| 198 | 
            -
                     | 
| 199 | 
            -
                    request.stubs(: | 
| 200 | 
            -
                    request.stubs(:path_info).returns('')
         | 
| 214 | 
            +
                    request.stubs(:params).returns('id_token' => jwt.to_s, 'state' => state)
         | 
| 215 | 
            +
                    request.stubs(:path).returns('')
         | 
| 201 216 |  | 
| 202 217 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 203 218 | 
             
                    strategy.options.client_signing_alg = :RS256
         | 
| 204 | 
            -
                    strategy.options.client_jwk_signing_key =  | 
| 219 | 
            +
                    strategy.options.client_jwk_signing_key = jwks.to_json
         | 
| 205 220 | 
             
                    strategy.options.response_type = 'id_token'
         | 
| 206 221 |  | 
| 207 222 | 
             
                    strategy.unstub(:user_info)
         | 
| @@ -210,7 +225,7 @@ module OmniAuth | |
| 210 225 | 
             
                    access_token.stubs(:refresh_token)
         | 
| 211 226 | 
             
                    access_token.stubs(:expires_in)
         | 
| 212 227 | 
             
                    access_token.stubs(:scope)
         | 
| 213 | 
            -
                    access_token.stubs(:id_token).returns( | 
| 228 | 
            +
                    access_token.stubs(:id_token).returns(jwt.to_s)
         | 
| 214 229 |  | 
| 215 230 | 
             
                    id_token = stub('OpenIDConnect::ResponseObject::IdToken')
         | 
| 216 231 | 
             
                    id_token.stubs(:raw_attributes).returns('sub' => 'sub', 'name' => 'name', 'email' => 'email')
         | 
| @@ -222,14 +237,86 @@ module OmniAuth | |
| 222 237 | 
             
                    strategy.callback_phase
         | 
| 223 238 | 
             
                  end
         | 
| 224 239 |  | 
| 225 | 
            -
                  def  | 
| 240 | 
            +
                  def test_callback_phase_with_id_token_and_param_provided_nonce # rubocop:disable Metrics/AbcSize
         | 
| 226 241 | 
             
                    code = SecureRandom.hex(16)
         | 
| 227 242 | 
             
                    state = SecureRandom.hex(16)
         | 
| 228 243 | 
             
                    nonce = SecureRandom.hex(16)
         | 
| 229 | 
            -
                     | 
| 244 | 
            +
                    request.stubs(:params).returns('code' => code, 'state' => state, 'nonce' => nonce)
         | 
| 245 | 
            +
                    request.stubs(:path).returns('')
         | 
| 230 246 |  | 
| 231 | 
            -
                     | 
| 232 | 
            -
                     | 
| 247 | 
            +
                    strategy.options.issuer = 'example.com'
         | 
| 248 | 
            +
                    strategy.options.client_signing_alg = :RS256
         | 
| 249 | 
            +
                    strategy.options.client_jwk_signing_key = jwks.to_s
         | 
| 250 | 
            +
                    strategy.options.response_type = 'code'
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                    strategy.unstub(:user_info)
         | 
| 253 | 
            +
                    access_token = stub('OpenIDConnect::AccessToken')
         | 
| 254 | 
            +
                    access_token.stubs(:access_token)
         | 
| 255 | 
            +
                    access_token.stubs(:refresh_token)
         | 
| 256 | 
            +
                    access_token.stubs(:expires_in)
         | 
| 257 | 
            +
                    access_token.stubs(:scope)
         | 
| 258 | 
            +
                    access_token.stubs(:id_token).returns(jwt.to_s)
         | 
| 259 | 
            +
                    client.expects(:access_token!).at_least_once.returns(access_token)
         | 
| 260 | 
            +
                    access_token.expects(:userinfo!).returns(user_info)
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                    id_token = stub('OpenIDConnect::ResponseObject::IdToken')
         | 
| 263 | 
            +
                    id_token.stubs(:raw_attributes).returns('sub' => 'sub', 'name' => 'name', 'email' => 'email')
         | 
| 264 | 
            +
                    id_token.stubs(:verify!).with(issuer: strategy.options.issuer, client_id: @identifier, nonce: nonce).returns(true)
         | 
| 265 | 
            +
                    id_token.expects(:verify!)
         | 
| 266 | 
            +
             | 
| 267 | 
            +
                    strategy.expects(:decode_id_token).twice.with(access_token.id_token).returns(id_token)
         | 
| 268 | 
            +
                    strategy.call!('rack.session' => { 'omniauth.state' => state })
         | 
| 269 | 
            +
                    strategy.callback_phase
         | 
| 270 | 
            +
                  end
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                  def test_callback_phase_with_discovery # rubocop:disable Metrics/AbcSize
         | 
| 273 | 
            +
                    state = SecureRandom.hex(16)
         | 
| 274 | 
            +
             | 
| 275 | 
            +
                    request.stubs(:params).returns('code' => jwt.to_s, 'state' => state)
         | 
| 276 | 
            +
                    request.stubs(:path).returns('')
         | 
| 277 | 
            +
             | 
| 278 | 
            +
                    strategy.options.client_options.host = 'example.com'
         | 
| 279 | 
            +
                    strategy.options.discovery = true
         | 
| 280 | 
            +
             | 
| 281 | 
            +
                    issuer = stub('OpenIDConnect::Discovery::Issuer')
         | 
| 282 | 
            +
                    issuer.stubs(:issuer).returns('https://example.com/')
         | 
| 283 | 
            +
                    ::OpenIDConnect::Discovery::Provider.stubs(:discover!).returns(issuer)
         | 
| 284 | 
            +
             | 
| 285 | 
            +
                    config = stub('OpenIDConnect::Discovery::Provder::Config')
         | 
| 286 | 
            +
                    config.stubs(:authorization_endpoint).returns('https://example.com/authorization')
         | 
| 287 | 
            +
                    config.stubs(:token_endpoint).returns('https://example.com/token')
         | 
| 288 | 
            +
                    config.stubs(:userinfo_endpoint).returns('https://example.com/userinfo')
         | 
| 289 | 
            +
                    config.stubs(:jwks_uri).returns('https://example.com/jwks')
         | 
| 290 | 
            +
                    config.stubs(:jwks).returns(JSON::JWK::Set.new(jwks['keys']))
         | 
| 291 | 
            +
             | 
| 292 | 
            +
                    ::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config)
         | 
| 293 | 
            +
             | 
| 294 | 
            +
                    id_token = stub('OpenIDConnect::ResponseObject::IdToken')
         | 
| 295 | 
            +
                    id_token.stubs(:raw_attributes).returns('sub' => 'sub', 'name' => 'name', 'email' => 'email')
         | 
| 296 | 
            +
                    id_token.stubs(:verify!).with(issuer: 'https://example.com/', client_id: @identifier, nonce: nonce).returns(true)
         | 
| 297 | 
            +
                    ::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token)
         | 
| 298 | 
            +
             | 
| 299 | 
            +
                    strategy.unstub(:user_info)
         | 
| 300 | 
            +
                    access_token = stub('OpenIDConnect::AccessToken')
         | 
| 301 | 
            +
                    access_token.stubs(:access_token)
         | 
| 302 | 
            +
                    access_token.stubs(:refresh_token)
         | 
| 303 | 
            +
                    access_token.stubs(:expires_in)
         | 
| 304 | 
            +
                    access_token.stubs(:scope)
         | 
| 305 | 
            +
                    access_token.stubs(:id_token).returns(jwt.to_s)
         | 
| 306 | 
            +
                    client.expects(:access_token!).at_least_once.returns(access_token)
         | 
| 307 | 
            +
                    access_token.expects(:userinfo!).returns(user_info)
         | 
| 308 | 
            +
             | 
| 309 | 
            +
                    strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
         | 
| 310 | 
            +
                    strategy.callback_phase
         | 
| 311 | 
            +
                  end
         | 
| 312 | 
            +
             | 
| 313 | 
            +
                  def test_callback_phase_with_no_state_without_state_verification # rubocop:disable Metrics/AbcSize
         | 
| 314 | 
            +
                    code = SecureRandom.hex(16)
         | 
| 315 | 
            +
             | 
| 316 | 
            +
                    strategy.options.require_state = false
         | 
| 317 | 
            +
             | 
| 318 | 
            +
                    request.stubs(:params).returns('code' => code)
         | 
| 319 | 
            +
                    request.stubs(:path).returns('')
         | 
| 233 320 |  | 
| 234 321 | 
             
                    strategy.options.client_options.host = 'example.com'
         | 
| 235 322 | 
             
                    strategy.options.discovery = true
         | 
| @@ -243,7 +330,7 @@ module OmniAuth | |
| 243 330 | 
             
                    config.stubs(:token_endpoint).returns('https://example.com/token')
         | 
| 244 331 | 
             
                    config.stubs(:userinfo_endpoint).returns('https://example.com/userinfo')
         | 
| 245 332 | 
             
                    config.stubs(:jwks_uri).returns('https://example.com/jwks')
         | 
| 246 | 
            -
                    config.stubs(:jwks).returns(jwks)
         | 
| 333 | 
            +
                    config.stubs(:jwks).returns(JSON::JWK::Set.new(jwks['keys']))
         | 
| 247 334 |  | 
| 248 335 | 
             
                    ::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config)
         | 
| 249 336 |  | 
| @@ -258,21 +345,67 @@ module OmniAuth | |
| 258 345 | 
             
                    access_token.stubs(:refresh_token)
         | 
| 259 346 | 
             
                    access_token.stubs(:expires_in)
         | 
| 260 347 | 
             
                    access_token.stubs(:scope)
         | 
| 261 | 
            -
                    access_token.stubs(:id_token).returns( | 
| 348 | 
            +
                    access_token.stubs(:id_token).returns(jwt.to_s)
         | 
| 262 349 | 
             
                    client.expects(:access_token!).at_least_once.returns(access_token)
         | 
| 263 350 | 
             
                    access_token.expects(:userinfo!).returns(user_info)
         | 
| 264 351 |  | 
| 352 | 
            +
                    strategy.call!('rack.session' => { 'omniauth.nonce' => nonce })
         | 
| 353 | 
            +
                    strategy.callback_phase
         | 
| 354 | 
            +
                  end
         | 
| 355 | 
            +
             | 
| 356 | 
            +
                  def test_callback_phase_with_invalid_state_without_state_verification
         | 
| 357 | 
            +
                    code = SecureRandom.hex(16)
         | 
| 358 | 
            +
                    state = SecureRandom.hex(16)
         | 
| 359 | 
            +
             | 
| 360 | 
            +
                    strategy.options.require_state = false
         | 
| 361 | 
            +
             | 
| 362 | 
            +
                    request.stubs(:params).returns('code' => code, 'state' => 'foobar')
         | 
| 363 | 
            +
                    request.stubs(:path).returns('')
         | 
| 364 | 
            +
             | 
| 365 | 
            +
                    strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
         | 
| 366 | 
            +
                    strategy.expects(:fail!)
         | 
| 367 | 
            +
                    strategy.callback_phase
         | 
| 368 | 
            +
                  end
         | 
| 369 | 
            +
             | 
| 370 | 
            +
                  def test_callback_phase_with_jwks_uri
         | 
| 371 | 
            +
                    id_token = jwt.to_s
         | 
| 372 | 
            +
                    state = SecureRandom.hex(16)
         | 
| 373 | 
            +
                    request.stubs(:params).returns('id_token' => id_token, 'state' => state)
         | 
| 374 | 
            +
                    request.stubs(:path_info).returns('')
         | 
| 375 | 
            +
             | 
| 376 | 
            +
                    strategy.options.issuer = 'example.com'
         | 
| 377 | 
            +
                    strategy.options.client_options.jwks_uri = 'https://jwks.example.com'
         | 
| 378 | 
            +
                    strategy.options.response_type = 'id_token'
         | 
| 379 | 
            +
             | 
| 380 | 
            +
                    HTTPClient
         | 
| 381 | 
            +
                      .any_instance.stubs(:get_content)
         | 
| 382 | 
            +
                      .with(strategy.options.client_options.jwks_uri)
         | 
| 383 | 
            +
                      .returns(jwks.to_json)
         | 
| 384 | 
            +
             | 
| 385 | 
            +
                    strategy.unstub(:user_info)
         | 
| 386 | 
            +
                    access_token = stub('OpenIDConnect::AccessToken')
         | 
| 387 | 
            +
                    access_token.stubs(:access_token)
         | 
| 388 | 
            +
                    access_token.stubs(:refresh_token)
         | 
| 389 | 
            +
                    access_token.stubs(:expires_in)
         | 
| 390 | 
            +
                    access_token.stubs(:scope)
         | 
| 391 | 
            +
                    access_token.stubs(:id_token).returns(id_token)
         | 
| 392 | 
            +
             | 
| 393 | 
            +
                    id_token = stub('OpenIDConnect::ResponseObject::IdToken')
         | 
| 394 | 
            +
                    id_token.stubs(:raw_attributes).returns('sub' => 'sub', 'name' => 'name', 'email' => 'email')
         | 
| 395 | 
            +
                    id_token.stubs(:verify!).with(issuer: strategy.options.issuer, client_id: @identifier, nonce: nonce).returns(true)
         | 
| 396 | 
            +
                    ::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token)
         | 
| 397 | 
            +
                    id_token.expects(:verify!)
         | 
| 398 | 
            +
             | 
| 265 399 | 
             
                    strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
         | 
| 266 400 | 
             
                    strategy.callback_phase
         | 
| 267 401 | 
             
                  end
         | 
| 268 402 |  | 
| 269 403 | 
             
                  def test_callback_phase_with_error
         | 
| 270 404 | 
             
                    state = SecureRandom.hex(16)
         | 
| 271 | 
            -
                    nonce = SecureRandom.hex(16)
         | 
| 272 405 | 
             
                    request.stubs(:params).returns('error' => 'invalid_request')
         | 
| 273 | 
            -
                    request.stubs(: | 
| 406 | 
            +
                    request.stubs(:path).returns('')
         | 
| 274 407 |  | 
| 275 | 
            -
                    strategy.call!({'rack.session' => {'omniauth.state' => state, 'omniauth.nonce' => nonce}})
         | 
| 408 | 
            +
                    strategy.call!({ 'rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce } })
         | 
| 276 409 | 
             
                    strategy.expects(:fail!)
         | 
| 277 410 | 
             
                    strategy.callback_phase
         | 
| 278 411 | 
             
                  end
         | 
| @@ -280,9 +413,8 @@ module OmniAuth | |
| 280 413 | 
             
                  def test_callback_phase_with_invalid_state
         | 
| 281 414 | 
             
                    code = SecureRandom.hex(16)
         | 
| 282 415 | 
             
                    state = SecureRandom.hex(16)
         | 
| 283 | 
            -
                    nonce = SecureRandom.hex(16)
         | 
| 284 416 | 
             
                    request.stubs(:params).returns('code' => code, 'state' => 'foobar')
         | 
| 285 | 
            -
                    request.stubs(: | 
| 417 | 
            +
                    request.stubs(:path).returns('')
         | 
| 286 418 |  | 
| 287 419 | 
             
                    strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
         | 
| 288 420 | 
             
                    strategy.expects(:fail!)
         | 
| @@ -291,9 +423,8 @@ module OmniAuth | |
| 291 423 |  | 
| 292 424 | 
             
                  def test_callback_phase_without_code
         | 
| 293 425 | 
             
                    state = SecureRandom.hex(16)
         | 
| 294 | 
            -
                    nonce = SecureRandom.hex(16)
         | 
| 295 426 | 
             
                    request.stubs(:params).returns('state' => state)
         | 
| 296 | 
            -
                    request.stubs(: | 
| 427 | 
            +
                    request.stubs(:path).returns('')
         | 
| 297 428 |  | 
| 298 429 | 
             
                    strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
         | 
| 299 430 |  | 
| @@ -303,9 +434,8 @@ module OmniAuth | |
| 303 434 |  | 
| 304 435 | 
             
                  def test_callback_phase_without_id_token
         | 
| 305 436 | 
             
                    state = SecureRandom.hex(16)
         | 
| 306 | 
            -
                    nonce = SecureRandom.hex(16)
         | 
| 307 437 | 
             
                    request.stubs(:params).returns('state' => state)
         | 
| 308 | 
            -
                    request.stubs(: | 
| 438 | 
            +
                    request.stubs(:path).returns('')
         | 
| 309 439 | 
             
                    strategy.options.response_type = 'id_token'
         | 
| 310 440 |  | 
| 311 441 | 
             
                    strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
         | 
| @@ -316,9 +446,8 @@ module OmniAuth | |
| 316 446 |  | 
| 317 447 | 
             
                  def test_callback_phase_without_id_token_symbol
         | 
| 318 448 | 
             
                    state = SecureRandom.hex(16)
         | 
| 319 | 
            -
                    nonce = SecureRandom.hex(16)
         | 
| 320 449 | 
             
                    request.stubs(:params).returns('state' => state)
         | 
| 321 | 
            -
                    request.stubs(: | 
| 450 | 
            +
                    request.stubs(:path).returns('')
         | 
| 322 451 | 
             
                    strategy.options.response_type = :id_token
         | 
| 323 452 |  | 
| 324 453 | 
             
                    strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
         | 
| @@ -330,9 +459,8 @@ module OmniAuth | |
| 330 459 | 
             
                  def test_callback_phase_with_timeout
         | 
| 331 460 | 
             
                    code = SecureRandom.hex(16)
         | 
| 332 461 | 
             
                    state = SecureRandom.hex(16)
         | 
| 333 | 
            -
                    nonce = SecureRandom.hex(16)
         | 
| 334 462 | 
             
                    request.stubs(:params).returns('code' => code, 'state' => state)
         | 
| 335 | 
            -
                    request.stubs(: | 
| 463 | 
            +
                    request.stubs(:path).returns('')
         | 
| 336 464 |  | 
| 337 465 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 338 466 |  | 
| @@ -350,9 +478,8 @@ module OmniAuth | |
| 350 478 | 
             
                  def test_callback_phase_with_etimeout
         | 
| 351 479 | 
             
                    code = SecureRandom.hex(16)
         | 
| 352 480 | 
             
                    state = SecureRandom.hex(16)
         | 
| 353 | 
            -
                    nonce = SecureRandom.hex(16)
         | 
| 354 481 | 
             
                    request.stubs(:params).returns('code' => code, 'state' => state)
         | 
| 355 | 
            -
                    request.stubs(: | 
| 482 | 
            +
                    request.stubs(:path).returns('')
         | 
| 356 483 |  | 
| 357 484 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 358 485 |  | 
| @@ -370,9 +497,8 @@ module OmniAuth | |
| 370 497 | 
             
                  def test_callback_phase_with_socket_error
         | 
| 371 498 | 
             
                    code = SecureRandom.hex(16)
         | 
| 372 499 | 
             
                    state = SecureRandom.hex(16)
         | 
| 373 | 
            -
                    nonce = SecureRandom.hex(16)
         | 
| 374 500 | 
             
                    request.stubs(:params).returns('code' => code, 'state' => state)
         | 
| 375 | 
            -
                    request.stubs(: | 
| 501 | 
            +
                    request.stubs(:path).returns('')
         | 
| 376 502 |  | 
| 377 503 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 378 504 |  | 
| @@ -390,9 +516,8 @@ module OmniAuth | |
| 390 516 | 
             
                  def test_callback_phase_with_rack_oauth2_client_error
         | 
| 391 517 | 
             
                    code = SecureRandom.hex(16)
         | 
| 392 518 | 
             
                    state = SecureRandom.hex(16)
         | 
| 393 | 
            -
                    nonce = SecureRandom.hex(16)
         | 
| 394 519 | 
             
                    request.stubs(:params).returns('code' => code, 'state' => state)
         | 
| 395 | 
            -
                    request.stubs(: | 
| 520 | 
            +
                    request.stubs(:path).returns('')
         | 
| 396 521 |  | 
| 397 522 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 398 523 |  | 
| @@ -411,6 +536,7 @@ module OmniAuth | |
| 411 536 | 
             
                    info = strategy.info
         | 
| 412 537 | 
             
                    assert_equal user_info.name, info[:name]
         | 
| 413 538 | 
             
                    assert_equal user_info.email, info[:email]
         | 
| 539 | 
            +
                    assert_equal user_info.email_verified, info[:email_verified]
         | 
| 414 540 | 
             
                    assert_equal user_info.preferred_username, info[:nickname]
         | 
| 415 541 | 
             
                    assert_equal user_info.given_name, info[:first_name]
         | 
| 416 542 | 
             
                    assert_equal user_info.family_name, info[:last_name]
         | 
| @@ -427,7 +553,7 @@ module OmniAuth | |
| 427 553 | 
             
                  def test_credentials
         | 
| 428 554 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 429 555 | 
             
                    strategy.options.client_signing_alg = :RS256
         | 
| 430 | 
            -
                    strategy.options.client_jwk_signing_key =  | 
| 556 | 
            +
                    strategy.options.client_jwk_signing_key = jwks.to_json
         | 
| 431 557 |  | 
| 432 558 | 
             
                    id_token = stub('OpenIDConnect::ResponseObject::IdToken')
         | 
| 433 559 | 
             
                    id_token.stubs(:verify!).returns(true)
         | 
| @@ -438,7 +564,7 @@ module OmniAuth | |
| 438 564 | 
             
                    access_token.stubs(:refresh_token).returns(SecureRandom.hex(16))
         | 
| 439 565 | 
             
                    access_token.stubs(:expires_in).returns(Time.now)
         | 
| 440 566 | 
             
                    access_token.stubs(:scope).returns('openidconnect')
         | 
| 441 | 
            -
                    access_token.stubs(:id_token).returns( | 
| 567 | 
            +
                    access_token.stubs(:id_token).returns(jwt.to_s)
         | 
| 442 568 |  | 
| 443 569 | 
             
                    client.expects(:access_token!).returns(access_token)
         | 
| 444 570 | 
             
                    access_token.expects(:refresh_token).returns(access_token.refresh_token)
         | 
| @@ -450,7 +576,7 @@ module OmniAuth | |
| 450 576 | 
             
                        token: access_token.access_token,
         | 
| 451 577 | 
             
                        refresh_token: access_token.refresh_token,
         | 
| 452 578 | 
             
                        expires_in: access_token.expires_in,
         | 
| 453 | 
            -
                        scope: access_token.scope
         | 
| 579 | 
            +
                        scope: access_token.scope,
         | 
| 454 580 | 
             
                      },
         | 
| 455 581 | 
             
                      strategy.credentials
         | 
| 456 582 | 
             
                    )
         | 
| @@ -458,11 +584,10 @@ module OmniAuth | |
| 458 584 |  | 
| 459 585 | 
             
                  def test_option_send_nonce
         | 
| 460 586 | 
             
                    strategy.options.client_options[:host] = 'foobar.com'
         | 
| 461 | 
            -
             | 
| 462 | 
            -
                    assert(strategy.authorize_uri =~ /nonce=/, 'URI must contain nonce')
         | 
| 587 | 
            +
                    assert_match(/nonce/, strategy.authorize_uri, 'URI must contain nonce')
         | 
| 463 588 |  | 
| 464 589 | 
             
                    strategy.options.send_nonce = false
         | 
| 465 | 
            -
                     | 
| 590 | 
            +
                    refute_match(/nonce/, strategy.authorize_uri, 'URI must not contain nonce')
         | 
| 466 591 | 
             
                  end
         | 
| 467 592 |  | 
| 468 593 | 
             
                  def test_failure_endpoint_redirect
         | 
| @@ -472,9 +597,9 @@ module OmniAuth | |
| 472 597 |  | 
| 473 598 | 
             
                    result = strategy.callback_phase
         | 
| 474 599 |  | 
| 475 | 
            -
                    assert(result.is_a? | 
| 600 | 
            +
                    assert(result.is_a?(Array))
         | 
| 476 601 | 
             
                    assert(result[0] == 302, 'Redirect')
         | 
| 477 | 
            -
                    assert(result[1][ | 
| 602 | 
            +
                    assert(result[1]['Location'] =~ %r{/auth/failure})
         | 
| 478 603 | 
             
                  end
         | 
| 479 604 |  | 
| 480 605 | 
             
                  def test_state
         | 
| @@ -493,7 +618,7 @@ module OmniAuth | |
| 493 618 | 
             
                    # the following should fail because the wrong state is passed to the callback
         | 
| 494 619 | 
             
                    code = SecureRandom.hex(16)
         | 
| 495 620 | 
             
                    request.stubs(:params).returns('code' => code, 'state' => 43)
         | 
| 496 | 
            -
                    request.stubs(: | 
| 621 | 
            +
                    request.stubs(:path).returns('')
         | 
| 497 622 |  | 
| 498 623 | 
             
                    strategy.call!('rack.session' => session)
         | 
| 499 624 | 
             
                    strategy.expects(:fail!)
         | 
| @@ -502,8 +627,8 @@ module OmniAuth | |
| 502 627 |  | 
| 503 628 | 
             
                  def test_dynamic_state
         | 
| 504 629 | 
             
                    # Stub request parameters
         | 
| 505 | 
            -
                    request.stubs(: | 
| 506 | 
            -
                    strategy.call!('rack.session' => { | 
| 630 | 
            +
                    request.stubs(:path).returns('')
         | 
| 631 | 
            +
                    strategy.call!('rack.session' => {}, QUERY_STRING: { state: 'abc', client_id: '123' })
         | 
| 507 632 |  | 
| 508 633 | 
             
                    strategy.options.state = lambda { |env|
         | 
| 509 634 | 
             
                      # Get params from request, e.g. CGI.parse(env['QUERY_STRING'])
         | 
| @@ -519,23 +644,22 @@ module OmniAuth | |
| 519 644 |  | 
| 520 645 | 
             
                  def test_option_client_auth_method
         | 
| 521 646 | 
             
                    state = SecureRandom.hex(16)
         | 
| 522 | 
            -
                    nonce = SecureRandom.hex(16)
         | 
| 523 647 |  | 
| 524 648 | 
             
                    opts = strategy.options.client_options
         | 
| 525 649 | 
             
                    opts[:host] = 'foobar.com'
         | 
| 526 650 | 
             
                    strategy.options.issuer = 'foobar.com'
         | 
| 527 651 | 
             
                    strategy.options.client_auth_method = :not_basic
         | 
| 528 652 | 
             
                    strategy.options.client_signing_alg = :RS256
         | 
| 529 | 
            -
                    strategy.options.client_jwk_signing_key =  | 
| 653 | 
            +
                    strategy.options.client_jwk_signing_key = jwks.to_json
         | 
| 530 654 |  | 
| 531 655 | 
             
                    json_response = {
         | 
| 532 656 | 
             
                      access_token: 'test_access_token',
         | 
| 533 | 
            -
                      id_token:  | 
| 657 | 
            +
                      id_token: jwt.to_s,
         | 
| 534 658 | 
             
                      token_type: 'Bearer',
         | 
| 535 659 | 
             
                    }.to_json
         | 
| 536 660 | 
             
                    success = Struct.new(:status, :body).new(200, json_response)
         | 
| 537 661 |  | 
| 538 | 
            -
                    request.stubs(: | 
| 662 | 
            +
                    request.stubs(:path).returns('')
         | 
| 539 663 | 
             
                    strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
         | 
| 540 664 |  | 
| 541 665 | 
             
                    id_token = stub('OpenIDConnect::ResponseObject::IdToken')
         | 
| @@ -548,21 +672,19 @@ module OmniAuth | |
| 548 672 | 
             
                      {}
         | 
| 549 673 | 
             
                    ).returns(success)
         | 
| 550 674 |  | 
| 551 | 
            -
                    assert(strategy.send | 
| 675 | 
            +
                    assert(strategy.send(:access_token))
         | 
| 552 676 | 
             
                  end
         | 
| 553 677 |  | 
| 554 678 | 
             
                  def test_public_key_with_jwks
         | 
| 555 679 | 
             
                    strategy.options.client_signing_alg = :RS256
         | 
| 556 | 
            -
                    strategy.options.client_jwk_signing_key =  | 
| 680 | 
            +
                    strategy.options.client_jwk_signing_key = jwks.to_json
         | 
| 557 681 |  | 
| 558 682 | 
             
                    assert_equal JSON::JWK::Set, strategy.public_key.class
         | 
| 559 683 | 
             
                  end
         | 
| 560 684 |  | 
| 561 685 | 
             
                  def test_public_key_with_jwk
         | 
| 562 686 | 
             
                    strategy.options.client_signing_alg = :RS256
         | 
| 563 | 
            -
                     | 
| 564 | 
            -
                    jwks = JSON.parse(jwks_str)
         | 
| 565 | 
            -
                    jwk = jwks['keys'].first
         | 
| 687 | 
            +
                    jwk = jwks[:keys].first
         | 
| 566 688 | 
             
                    strategy.options.client_jwk_signing_key = jwk.to_json
         | 
| 567 689 |  | 
| 568 690 | 
             
                    assert_equal JSON::JWK, strategy.public_key.class
         | 
| @@ -582,25 +704,15 @@ module OmniAuth | |
| 582 704 |  | 
| 583 705 | 
             
                  def test_id_token_auth_hash
         | 
| 584 706 | 
             
                    state = SecureRandom.hex(16)
         | 
| 585 | 
            -
                    nonce = SecureRandom.hex(16)
         | 
| 586 707 | 
             
                    strategy.options.response_type = 'id_token'
         | 
| 587 708 | 
             
                    strategy.options.issuer = 'example.com'
         | 
| 588 709 |  | 
| 589 710 | 
             
                    id_token = stub('OpenIDConnect::ResponseObject::IdToken')
         | 
| 590 711 | 
             
                    id_token.stubs(:verify!).returns(true)
         | 
| 591 | 
            -
                    id_token.stubs(:raw_attributes, :to_h).returns(
         | 
| 592 | 
            -
                      {
         | 
| 593 | 
            -
                        "iss": "http://server.example.com",
         | 
| 594 | 
            -
                        "sub": "248289761001",
         | 
| 595 | 
            -
                        "aud": "s6BhdRkqt3",
         | 
| 596 | 
            -
                        "nonce": "n-0S6_WzA2Mj",
         | 
| 597 | 
            -
                        "exp": 1311281970,
         | 
| 598 | 
            -
                        "iat": 1311280970,
         | 
| 599 | 
            -
                      }
         | 
| 600 | 
            -
                    )
         | 
| 712 | 
            +
                    id_token.stubs(:raw_attributes, :to_h).returns(payload)
         | 
| 601 713 |  | 
| 602 714 | 
             
                    request.stubs(:params).returns('state' => state, 'nounce' => nonce, 'id_token' => id_token)
         | 
| 603 | 
            -
                    request.stubs(: | 
| 715 | 
            +
                    request.stubs(:path).returns('')
         | 
| 604 716 |  | 
| 605 717 | 
             
                    strategy.stubs(:decode_id_token).returns(id_token)
         | 
| 606 718 | 
             
                    strategy.stubs(:stored_state).returns(state)
         | 
| @@ -615,6 +727,41 @@ module OmniAuth | |
| 615 727 | 
             
                    assert auth_hash.key?('extra')
         | 
| 616 728 | 
             
                    assert auth_hash['extra'].key?('raw_info')
         | 
| 617 729 | 
             
                  end
         | 
| 730 | 
            +
             | 
| 731 | 
            +
                  def test_option_pkce
         | 
| 732 | 
            +
                    strategy.options.client_options[:host] = 'example.com'
         | 
| 733 | 
            +
             | 
| 734 | 
            +
                    # test pkce disabled
         | 
| 735 | 
            +
                    strategy.options.pkce = false
         | 
| 736 | 
            +
             | 
| 737 | 
            +
                    assert((strategy.authorize_uri !~ /code_challenge=/), 'URI must not contain code challenge param')
         | 
| 738 | 
            +
                    assert((strategy.authorize_uri !~ /code_challenge_method=/), 'URI must not contain code challenge method param')
         | 
| 739 | 
            +
             | 
| 740 | 
            +
                    # test pkce enabled with default opts
         | 
| 741 | 
            +
                    strategy.options.pkce = true
         | 
| 742 | 
            +
             | 
| 743 | 
            +
                    assert(strategy.authorize_uri =~ /code_challenge=/, 'URI must contain code challenge param')
         | 
| 744 | 
            +
                    assert(strategy.authorize_uri =~ /code_challenge_method=/, 'URI must contain code challenge method param')
         | 
| 745 | 
            +
             | 
| 746 | 
            +
                    # test pkce with custom verifier code
         | 
| 747 | 
            +
                    strategy.options.pkce_verifier = proc { 'dummy_verifier' }
         | 
| 748 | 
            +
                    code_challenge_value = Base64.urlsafe_encode64(
         | 
| 749 | 
            +
                      Digest::SHA2.digest(strategy.options.pkce_verifier.call),
         | 
| 750 | 
            +
                      padding: false
         | 
| 751 | 
            +
                    )
         | 
| 752 | 
            +
             | 
| 753 | 
            +
                    assert(strategy.authorize_uri =~ /#{Regexp.quote(code_challenge_value)}/, 'URI must contain code challenge value')
         | 
| 754 | 
            +
             | 
| 755 | 
            +
                    # test pkce with custom options and plain text code
         | 
| 756 | 
            +
                    strategy.options.pkce_options =
         | 
| 757 | 
            +
                      {
         | 
| 758 | 
            +
                        code_challenge: proc { |verifier| verifier },
         | 
| 759 | 
            +
                        code_challenge_method: 'plain',
         | 
| 760 | 
            +
                      }
         | 
| 761 | 
            +
             | 
| 762 | 
            +
                    assert(strategy.authorize_uri =~ /#{Regexp.quote(strategy.options.pkce_verifier.call)}/,
         | 
| 763 | 
            +
                           'URI must contain code challenge value')
         | 
| 764 | 
            +
                  end
         | 
| 618 765 | 
             
                end
         | 
| 619 766 | 
             
              end
         | 
| 620 767 | 
             
            end
         |