diff --git a/README.md b/README.md index 169d908f..93e5d928 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ These are the configuration options for the client_options hash of the configura | scheme | The http scheme to use | https | | | host | The host of the authorization server | nil | | | port | The port for the authorization server | 443 | | +| audience | The intended consumer (`aud` field) of the id_token | nil | | | authorization_endpoint | The authorize endpoint on the authorization server | /authorize | yes | | token_endpoint | The token endpoint on the authorization server | /token | yes | | userinfo_endpoint | The user info endpoint on the authorization server | /userinfo | yes | diff --git a/lib/omniauth/strategies/openid_connect.rb b/lib/omniauth/strategies/openid_connect.rb index ebfaaa17..0627392b 100644 --- a/lib/omniauth/strategies/openid_connect.rb +++ b/lib/omniauth/strategies/openid_connect.rb @@ -28,6 +28,7 @@ class OpenIDConnect # rubocop:disable Metrics/ClassLength scheme: 'https', host: nil, port: 443, + audience: nil, authorization_endpoint: '/authorize', token_endpoint: '/token', userinfo_endpoint: '/userinfo', @@ -464,9 +465,14 @@ def configured_response_type def verify_id_token!(id_token) return unless id_token - decode_id_token(id_token).verify!(issuer: options.issuer, - client_id: client_options.identifier, - nonce: params['nonce'].presence || stored_nonce) + verify_kwargs = { + issuer: options.issuer, + client_id: client_options.identifier, + nonce: params['nonce'].presence || stored_nonce, + } + verify_kwargs.merge!(audience: client_options.audience) if client_options.audience + + decode_id_token(id_token).verify!(**verify_kwargs) end class CallbackError < StandardError diff --git a/test/lib/omniauth/strategies/openid_connect_test.rb b/test/lib/omniauth/strategies/openid_connect_test.rb index 031e1e3c..105edceb 100644 --- a/test/lib/omniauth/strategies/openid_connect_test.rb +++ b/test/lib/omniauth/strategies/openid_connect_test.rb @@ -248,6 +248,27 @@ def test_callback_phase_with_id_token strategy.callback_phase end + def test_callback_phase_with_audience + state = SecureRandom.hex(16) + strategy.options.response_type = 'id_token' + strategy.options.issuer = 'example.com' + strategy.options.client_options.audience = 'my_audience' + + id_token = stub('OpenIDConnect::ResponseObject::IdToken') + id_token.expects(:verify!).with(issuer: strategy.options.issuer, client_id: @identifier, audience: 'my_audience', + nonce: nonce).returns(true) + id_token.stubs(:raw_attributes, :to_h).returns(payload) + + request.stubs(:params).returns('state' => state, 'nounce' => nonce, 'id_token' => id_token) + request.stubs(:path).returns('') + + strategy.stubs(:decode_id_token).returns(id_token) + strategy.stubs(:stored_state).returns(state) + + strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) + strategy.callback_phase + end + def test_callback_phase_with_id_token_and_param_provided_nonce # rubocop:disable Metrics/AbcSize code = SecureRandom.hex(16) state = SecureRandom.hex(16)