diff --git a/CHANGELOG.md b/CHANGELOG.md index 49a2464..844d1cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## [Unreleased](https://github.com/auth0/omniauth-auth0/tree/HEAD) + +**Added** +- Forward custom query parameters to `/authorize` via the configurable `passthrough_prefixes` option (defaults to `ext-`, set to `[]` to disable). Closes [\#214](https://github.com/auth0/omniauth-auth0/issues/214) + ## [v3.2.0](https://github.com/auth0/omniauth-auth0/tree/v3.2.0) (2026-05-27) [Full Changelog](https://github.com/auth0/omniauth-auth0/compare/v3.1.1...v3.2.0) diff --git a/README.md b/README.md index b286c22..eb9cde9 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,27 @@ end **Note**: The client_assertion_signing_key must be provided as a PKey object. +### Forwarding query parameters to `/authorize` + +The well-known Auth0 `/authorize` parameters (`connection`, `connection_scope`, `prompt`, `screen_hint`, `login_hint`, `organization`, `invitation`, `ui_locales`) are forwarded from the request to the authorization endpoint when present. + +Auth0 also supports [custom query parameters](https://auth0.com/docs/customize/login-pages/universal-login/customize-templates#custom-query-parameters) that are surfaced to the Universal Login page and to Actions, as long as they are prefixed with `ext-`. Any request parameter whose name starts with a configured prefix is forwarded as well. This defaults to `ext-`, so a request to `/auth/auth0?ext-promo=summer` forwards `ext-promo=summer` to `/authorize`. + +Use the `passthrough_prefixes` option to add prefixes, or set it to `[]` to disable the behavior entirely: + +```ruby +Rails.application.config.middleware.use OmniAuth::Builder do + provider( + :auth0, + AUTH0_CONFIG['auth0_client_id'], + AUTH0_CONFIG['auth0_client_secret'], + AUTH0_CONFIG['auth0_domain'], + # Defaults to %w[ext-]; pass [] to forward no prefixed parameters. + passthrough_prefixes: %w[ext-] + ) +end +``` + ### Create the callback controller Create a new controller `./app/controllers/auth0_controller.rb` to handle the callback from Auth0. diff --git a/lib/omniauth/strategies/auth0.rb b/lib/omniauth/strategies/auth0.rb index f694364..03d153b 100644 --- a/lib/omniauth/strategies/auth0.rb +++ b/lib/omniauth/strategies/auth0.rb @@ -17,8 +17,23 @@ class Auth0 < OmniAuth::Strategies::OAuth2 AUTHORIZATION_CODE_GRANT_TYPE = 'authorization_code' CLIENT_ASSERTION_TYPE = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer' + # Auth0 /authorize parameters forwarded from the request when present. + PASSTHROUGH_AUTHORIZE_PARAMS = %w[ + connection + connection_scope + prompt + screen_hint + login_hint + organization + invitation + ui_locales + ].freeze + option :name, 'auth0' + # Also forward request parameters whose name starts with one of these prefixes. + option :passthrough_prefixes, %w[ext-] + args %i[ client_id client_secret @@ -90,7 +105,7 @@ def client def authorize_params params = super - params.merge! request.params.select{|k,b| is_authorized_param?(k)} + params.merge!(request.params.select { |key, _value| passthrough_param?(key) }) # Generate nonce params[:nonce] = SecureRandom.hex @@ -128,10 +143,10 @@ def callback_phase private - def is_authorized_param?(param_key) - authorized_keys = %w[connection connection_scope prompt screen_hint login_hint organization invitation ui_locales] - - param_key.start_with?("ext-") || authorized_keys.include?(param_key) + # Check if a request parameter should be forwarded to /authorize. + def passthrough_param?(key) + PASSTHROUGH_AUTHORIZE_PARAMS.include?(key) || + Array(options.passthrough_prefixes).any? { |prefix| key.start_with?(prefix) } end def client_assertion_signing_key_auth? diff --git a/spec/omniauth/strategies/auth0_spec.rb b/spec/omniauth/strategies/auth0_spec.rb index 9ee0c8a..b3e5ce7 100644 --- a/spec/omniauth/strategies/auth0_spec.rb +++ b/spec/omniauth/strategies/auth0_spec.rb @@ -368,6 +368,48 @@ it_behaves_like 'oauth redirects with various parameters' end + context 'with a custom passthrough prefix configured' do + before do + @app = make_application(passthrough_prefixes: %w[custom-]) + end + + it 'forwards parameters matching the configured prefix' do + get 'auth/auth0?custom-foo=bar' + redirect_url = last_response.headers['Location'] + expect(redirect_url).to have_query('custom-foo', 'bar') + end + + it 'does not forward the default ext- prefix once overridden' do + get 'auth/auth0?ext-test=testval' + redirect_url = last_response.headers['Location'] + expect(redirect_url).not_to have_query('ext-test') + end + + it 'still forwards the standard Auth0 parameters' do + get 'auth/auth0?connection=abcd' + redirect_url = last_response.headers['Location'] + expect(redirect_url).to have_query('connection', 'abcd') + end + end + + context 'with passthrough prefixes disabled' do + before do + @app = make_application(passthrough_prefixes: []) + end + + it 'does not forward ext- prefixed parameters' do + get 'auth/auth0?ext-test=testval' + redirect_url = last_response.headers['Location'] + expect(redirect_url).not_to have_query('ext-test') + end + + it 'still forwards the standard Auth0 parameters' do + get 'auth/auth0?connection=abcd' + redirect_url = last_response.headers['Location'] + expect(redirect_url).to have_query('connection', 'abcd') + end + end + def session session_cookie = last_response.cookies['rack.session'].first session_data, _, _ = session_cookie.rpartition('--')