Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ config.omniauth :openid_connect, {
| 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 |
| client_auth_method | Which authentication method to use to authenticate your app with the authorization server | no | Sym: basic | "basic", "jwks" |
| scope | Which OpenID scopes to include (:openid is always required) | no | Array<sym> [:openid] | [:openid, :profile, :email] |
| response_type | Which OAuth2 response type to use with the authorization request | no | String: code | one of: 'code', 'id_token' |
| response_type | Which OAuth2 response type to use with the authorization request | no | String or Array: code | 'code', 'id_token', or ['code', 'id_token'] |
| 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) } |
| require_state | Should state param be verified - this is recommended, not required by the OIDC specification | no | true | false |
| 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 |
Expand Down
14 changes: 8 additions & 6 deletions lib/omniauth/strategies/openid_connect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,11 @@ def callback_phase

options.issuer = issuer if options.issuer.nil? || options.issuer.empty?

verify_id_token!(params['id_token']) if configured_response_type == 'id_token'
verify_id_token!(params['id_token']) if configured_response_types.include?('id_token')
discover!
client.redirect_uri = redirect_uri

return id_token_callback_phase if configured_response_type == 'id_token'
return id_token_callback_phase if configured_response_types.include?('id_token')

client.authorization_code = authorization_code
access_token
Expand Down Expand Up @@ -260,7 +260,7 @@ def access_token
token_request_params[:code_verifier] = params['code_verifier'] || session.delete('omniauth.pkce.verifier') if options.pkce

@access_token = client.access_token!(token_request_params)
verify_id_token!(@access_token.id_token) if configured_response_type == 'code'
verify_id_token!(@access_token.id_token) if configured_response_types.include?('code')

@access_token
end
Expand Down Expand Up @@ -372,16 +372,18 @@ def id_token_callback_phase
end

def valid_response_type?
return true if params.key?(configured_response_type)
return true if configured_response_types.all? { |key| params.key?(key) }

configured_response_type, * = configured_response_types

error_attrs = RESPONSE_TYPE_EXCEPTIONS[configured_response_type]
fail!(error_attrs[:key], error_attrs[:exception_class].new(params['error']))

false
end

def configured_response_type
@configured_response_type ||= options.response_type.to_s
def configured_response_types
@configured_response_types ||= Array(options.response_type).map(&:to_s)
end

def verify_id_token!(id_token)
Expand Down
46 changes: 46 additions & 0 deletions test/lib/omniauth/strategies/openid_connect_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,28 @@ def test_request_phase_with_response_mode_symbol
strategy.request_phase
end

def test_request_phase_with_response_mode_array
expected_redirect = %r{^https://example\.com/authorize\?client_id=1234&nonce=\w{32}&response_mode=form_post&response_type=code%20id_token&scope=openid&state=\w{32}$}
strategy.options.issuer = 'example.com'
strategy.options.response_mode = 'form_post'
strategy.options.response_type = ['code', 'id_token']
strategy.options.client_options.host = 'example.com'

strategy.expects(:redirect).with(regexp_matches(expected_redirect))
strategy.request_phase
end

def test_request_phase_with_response_mode_symbol_array
expected_redirect = %r{^https://example\.com/authorize\?client_id=1234&nonce=\w{32}&response_mode=form_post&response_type=code%20id_token&scope=openid&state=\w{32}$}
strategy.options.issuer = 'example.com'
strategy.options.response_mode = 'form_post'
strategy.options.response_type = [:code, :id_token]
strategy.options.client_options.host = 'example.com'

strategy.expects(:redirect).with(regexp_matches(expected_redirect))
strategy.request_phase
end

def test_option_acr_values
strategy.options.client_options[:host] = 'foobar.com'

Expand Down Expand Up @@ -456,6 +478,30 @@ def test_callback_phase_without_id_token_symbol
strategy.callback_phase
end

def test_callback_phase_without_code_and_id_token
state = SecureRandom.hex(16)
request.stubs(:params).returns('state' => state)
request.stubs(:path).returns('')
strategy.options.response_type = ['code', 'id_token']

strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })

strategy.expects(:fail!).with(:missing_code, is_a(OmniAuth::OpenIDConnect::MissingCodeError))
strategy.callback_phase
end

def test_callback_phase_without_code_and_id_token_symbol
state = SecureRandom.hex(16)
request.stubs(:params).returns('state' => state)
request.stubs(:path).returns('')
strategy.options.response_type = [:code, :id_token]

strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })

strategy.expects(:fail!).with(:missing_code, is_a(OmniAuth::OpenIDConnect::MissingCodeError))
strategy.callback_phase
end

def test_callback_phase_with_timeout
code = SecureRandom.hex(16)
state = SecureRandom.hex(16)
Expand Down