Skip to content

Commit 19b1c3d

Browse files
committed
Make the state parameter verification optional
1 parent ec03912 commit 19b1c3d

File tree

4 files changed

+68
-2
lines changed

4 files changed

+68
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# v0.4.1 (06.02.2022)
2+
3+
- Make the state parameter verification optional [#122](https://github.com/omniauth/omniauth_openid_connect/pull/122)
4+
15
# v0.4.0 (06.02.2022)
26

37
- Support dynamic parameters to the authorize URI [#90](https://github.com/omniauth/omniauth_openid_connect/pull/90)

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@ config.omniauth :openid_connect, {
4949

5050
| Field | Description | Required | Default | Example/Options |
5151
|------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|----------------------------|-----------------------------------------------------|
52-
| name | Arbitrary string to identify connection and identify it from other openid_connect providers | no | String: openid_connect | :my_idp |
52+
| name | Arbitrary string to identify connection and identify it from other openid_connect providers | no | String: openid_connect | :my_idp |
5353
| issuer | Root url for the authorization server | yes | | https://myprovider.com |
5454
| discovery | Should OpenID discovery be used. This is recommended if the IDP provides a discovery endpoint. See client config for how to manually enter discovered values. | no | false | one of: true, false |
5555
| client_auth_method | Which authentication method to use to authenticate your app with the authorization server | no | Sym: basic | "basic", "jwks" |
5656
| scope | Which OpenID scopes to include (:openid is always required) | no | Array<sym> [:openid] | [:openid, :profile, :email] |
5757
| response_type | Which OAuth2 response type to use with the authorization request | no | String: code | one of: 'code', 'id_token' |
58+
| require_state | Should state param be verified - this is recommended, not required by the OIDC specification | no | true | false |
5859
| state | A value to be used for the OAuth2 state parameter on the authorization request. Can be a proc that generates a string. | no | Random 16 character string | Proc.new { SecureRandom.hex(32) } |
5960
| response_mode | The response mode per [spec](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html) | no | nil | one of: :query, :fragment, :form_post, :web_message |
6061
| display | An optional parameter to the authorization request to determine how the authorization and consent page | no | nil | one of: :page, :popup, :touch, :wap |

lib/omniauth/strategies/openid_connect.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class OpenIDConnect
4141
option :client_x509_signing_key
4242
option :scope, [:openid]
4343
option :response_type, 'code' # ['code', 'id_token']
44+
option :require_state, true
4445
option :state
4546
option :response_mode # [:query, :fragment, :form_post, :web_message]
4647
option :display, nil # [:page, :popup, :touch, :wap]
@@ -107,7 +108,7 @@ def request_phase
107108
def callback_phase
108109
error = params['error_reason'] || params['error']
109110
error_description = params['error_description'] || params['error_reason']
110-
invalid_state = params['state'].to_s.empty? || params['state'] != stored_state
111+
invalid_state = (options.require_state && params['state'].to_s.empty?) || params['state'] != stored_state
111112

112113
raise CallbackError, error: params['error'], reason: error_description, uri: params['error_uri'] if error
113114
raise CallbackError, error: :csrf_detected, reason: "Invalid 'state' parameter" if invalid_state

test/lib/omniauth/strategies/openid_connect_test.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,66 @@ def test_callback_phase_with_discovery # rubocop:disable Metrics/AbcSize
284284
strategy.callback_phase
285285
end
286286

287+
def test_callback_phase_with_no_state_without_state_verification
288+
code = SecureRandom.hex(16)
289+
nonce = SecureRandom.hex(16)
290+
jwks = JSON::JWK::Set.new(JSON.parse(File.read('test/fixtures/jwks.json'))['keys'])
291+
292+
strategy.options.require_state = false
293+
294+
request.stubs(:params).returns('code' => code)
295+
request.stubs(:path).returns('')
296+
297+
strategy.options.client_options.host = 'example.com'
298+
strategy.options.discovery = true
299+
300+
issuer = stub('OpenIDConnect::Discovery::Issuer')
301+
issuer.stubs(:issuer).returns('https://example.com/')
302+
::OpenIDConnect::Discovery::Provider.stubs(:discover!).returns(issuer)
303+
304+
config = stub('OpenIDConnect::Discovery::Provder::Config')
305+
config.stubs(:authorization_endpoint).returns('https://example.com/authorization')
306+
config.stubs(:token_endpoint).returns('https://example.com/token')
307+
config.stubs(:userinfo_endpoint).returns('https://example.com/userinfo')
308+
config.stubs(:jwks_uri).returns('https://example.com/jwks')
309+
config.stubs(:jwks).returns(jwks)
310+
311+
::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config)
312+
313+
id_token = stub('OpenIDConnect::ResponseObject::IdToken')
314+
id_token.stubs(:raw_attributes).returns('sub' => 'sub', 'name' => 'name', 'email' => 'email')
315+
id_token.stubs(:verify!).with(issuer: 'https://example.com/', client_id: @identifier, nonce: nonce).returns(true)
316+
::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token)
317+
318+
strategy.unstub(:user_info)
319+
access_token = stub('OpenIDConnect::AccessToken')
320+
access_token.stubs(:access_token)
321+
access_token.stubs(:refresh_token)
322+
access_token.stubs(:expires_in)
323+
access_token.stubs(:scope)
324+
access_token.stubs(:id_token).returns(File.read('test/fixtures/id_token.txt'))
325+
client.expects(:access_token!).at_least_once.returns(access_token)
326+
access_token.expects(:userinfo!).returns(user_info)
327+
328+
strategy.call!('rack.session' => { 'omniauth.nonce' => nonce })
329+
strategy.callback_phase
330+
end
331+
332+
def test_callback_phase_with_invalid_state_without_state_verification
333+
code = SecureRandom.hex(16)
334+
state = SecureRandom.hex(16)
335+
nonce = SecureRandom.hex(16)
336+
337+
strategy.options.require_state = false
338+
339+
request.stubs(:params).returns('code' => code, 'state' => 'foobar')
340+
request.stubs(:path).returns('')
341+
342+
strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
343+
strategy.expects(:fail!)
344+
strategy.callback_phase
345+
end
346+
287347
def test_callback_phase_with_error
288348
state = SecureRandom.hex(16)
289349
nonce = SecureRandom.hex(16)

0 commit comments

Comments
 (0)