From 4d106e8bfd3b6260ab65f0a7087104bbc5477166 Mon Sep 17 00:00:00 2001 From: James Mead Date: Tue, 3 Jun 2025 21:58:51 +0100 Subject: [PATCH 1/3] Restore OmniAuth test_mode in Auth request spec --- spec/support/omniauth.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spec/support/omniauth.rb b/spec/support/omniauth.rb index 8f5c115..e1ef0d4 100644 --- a/spec/support/omniauth.rb +++ b/spec/support/omniauth.rb @@ -1,3 +1,14 @@ # frozen_string_literal: true require 'omniauth' + +RSpec.configure do |config| + config.around do |example| + original_value = OmniAuth.config.test_mode + begin + example.run + ensure + OmniAuth.config.test_mode = original_value + end + end +end From 8eb742a97c87838fd876a77048e46cb8fc7836a8 Mon Sep 17 00:00:00 2001 From: Chris Roos Date: Wed, 19 Feb 2025 15:22:12 +0000 Subject: [PATCH 2/3] Allow OmniAuth Setup Phase to be configured The OmniAuth Setup Phase[1] allows for "request-time modification of an OmniAuth strategy". We're intending to use this in Experience CS so that we can optionally include the `student` scope[2] to allow students to login. The Profile app uses the presence of the `student` scope in the login request to decide whether to display the student login form (i.e. the form containing the school code textbox). We want to allow students (as well as non-students) to login to Experience CS and so need a way of changing the `scope` accordingly. By default the `scope` is fixed at Rails initialization time in the `RpiAuth::Engine`[3] but we need to be able to toggle it at runtime depending on whether user is a student or not. By utilising OmniAuth's setup phase we can toggle the `scope` based on something we set in the app (e.g. we might set something in the session to indicate that we want the student login flow). I initially added logic in this Gem to add the `student` scope if a specific value was set in the session but later decided that it should be up to the consuming apps to use this `setup` phase as they see fit. [1]: https://github.com/omniauth/omniauth/wiki/Setup-Phase [2]: https://github.com/RaspberryPiFoundation/documentation/blob/a92f03446a347d2d1acea48c50ea37f15293a9b6/docs/technology/codebases-and-products/accounts/profile-app/custom-oidc-scopes.md#available-custom-scopes [3]: https://github.com/RaspberryPiFoundation/rpi-auth/blob/b7771ee120254e4c6f2d90c5025be5714daeb542/lib/rpi_auth/engine.rb#L31 --- lib/rpi_auth/configuration.rb | 3 +- lib/rpi_auth/engine.rb | 3 ++ spec/dummy/config/initializers/rpi_auth.rb | 7 +++++ spec/dummy/spec/requests/auth_request_spec.rb | 30 +++++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/rpi_auth/configuration.rb b/lib/rpi_auth/configuration.rb index 79f4310..95aa78f 100644 --- a/lib/rpi_auth/configuration.rb +++ b/lib/rpi_auth/configuration.rb @@ -18,7 +18,8 @@ class Configuration :scope, :session_keys_to_persist, :success_redirect, - :user_model + :user_model, + :setup def initialize @bypass_auth = false diff --git a/lib/rpi_auth/engine.rb b/lib/rpi_auth/engine.rb index 3201c70..8c1c2ba 100644 --- a/lib/rpi_auth/engine.rb +++ b/lib/rpi_auth/engine.rb @@ -23,10 +23,12 @@ class Engine < ::Rails::Engine initializer 'RpiAuth.add_middleware' do |app| # rubocop:disable Metrics/BlockLength next unless RpiAuth.configuration + # rubocop:disable Metrics/BlockLength app.middleware.use OmniAuth::Builder do provider( :openid_connect, name: :rpi, + setup: RpiAuth.configuration.setup, issuer: RpiAuth.configuration.issuer, scope: RpiAuth.configuration.scope, callback_path: CALLBACK_PATH, @@ -52,6 +54,7 @@ class Engine < ::Rails::Engine RpiAuth.configuration.enable_auth_bypass if RpiAuth.configuration.bypass_auth end + # rubocop:enable Metrics/BlockLength end end end diff --git a/spec/dummy/config/initializers/rpi_auth.rb b/spec/dummy/config/initializers/rpi_auth.rb index d3355c4..5dce79d 100644 --- a/spec/dummy/config/initializers/rpi_auth.rb +++ b/spec/dummy/config/initializers/rpi_auth.rb @@ -1,4 +1,11 @@ RpiAuth.configure do |config| + config.setup = lambda do |env| + request = Rack::Request.new(env) + + if custom_scope = request.params['add-custom-scope'] + env['omniauth.strategy'].options[:scope] += [custom_scope] + end + end config.auth_url = 'http://localhost:9001/' config.auth_client_id = 'gem-dev' config.auth_client_secret = 'secret' diff --git a/spec/dummy/spec/requests/auth_request_spec.rb b/spec/dummy/spec/requests/auth_request_spec.rb index 9476af1..20e6941 100644 --- a/spec/dummy/spec/requests/auth_request_spec.rb +++ b/spec/dummy/spec/requests/auth_request_spec.rb @@ -312,5 +312,35 @@ end end end + + describe 'and toggling the scope at runtime' do + let(:custom_scope) { 'custom-scope' } + + before do + OmniAuth.config.test_mode = false + end + + it 'does not append a custom scope' do + post '/auth/rpi' + + scopes = extract_scopes_from_redirect_location(response) + + expect(scopes).not_to include(custom_scope) + end + + it 'appends a custom scope' do + post "/auth/rpi?add-custom-scope=#{custom_scope}" + + scopes = extract_scopes_from_redirect_location(response) + + expect(scopes).to include(custom_scope) + end + + def extract_scopes_from_redirect_location(response) + location = response.headers['location'] + params = CGI.parse(URI.parse(location).query) + params['scope'].first.split + end + end end end From dd0e884f850dad99e57645bb3b9cce1106f3c195 Mon Sep 17 00:00:00 2001 From: Chris Roos Date: Wed, 21 May 2025 17:05:37 +0100 Subject: [PATCH 3/3] Extract openid_connect_options So that I can avoid disabling the Metrics/BlockLength RuboCop rule. --- lib/rpi_auth/engine.rb | 51 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/lib/rpi_auth/engine.rb b/lib/rpi_auth/engine.rb index 8c1c2ba..55b4e30 100644 --- a/lib/rpi_auth/engine.rb +++ b/lib/rpi_auth/engine.rb @@ -23,38 +23,37 @@ class Engine < ::Rails::Engine initializer 'RpiAuth.add_middleware' do |app| # rubocop:disable Metrics/BlockLength next unless RpiAuth.configuration - # rubocop:disable Metrics/BlockLength + openid_connect_options = { + name: :rpi, + setup: RpiAuth.configuration.setup, + issuer: RpiAuth.configuration.issuer, + scope: RpiAuth.configuration.scope, + callback_path: CALLBACK_PATH, + response_type: RpiAuth.configuration.response_type, + client_auth_method: RpiAuth.configuration.client_auth_method, + client_options: { + identifier: RpiAuth.configuration.auth_client_id, + secret: RpiAuth.configuration.auth_client_secret, + scheme: RpiAuth.configuration.token_endpoint.scheme, + host: RpiAuth.configuration.token_endpoint.host, + port: RpiAuth.configuration.token_endpoint.port, + authorization_endpoint: RpiAuth.configuration.authorization_endpoint, + token_endpoint: RpiAuth.configuration.token_endpoint, + jwks_uri: RpiAuth.configuration.jwks_uri, + redirect_uri: URI.join(RpiAuth.configuration.host_url, CALLBACK_PATH) + }, + extra_authorize_params: { brand: RpiAuth.configuration.brand }, + allow_authorize_params: [:login_options], + origin_param: 'returnTo' + } + app.middleware.use OmniAuth::Builder do - provider( - :openid_connect, - name: :rpi, - setup: RpiAuth.configuration.setup, - issuer: RpiAuth.configuration.issuer, - scope: RpiAuth.configuration.scope, - callback_path: CALLBACK_PATH, - response_type: RpiAuth.configuration.response_type, - client_auth_method: RpiAuth.configuration.client_auth_method, - client_options: { - identifier: RpiAuth.configuration.auth_client_id, - secret: RpiAuth.configuration.auth_client_secret, - scheme: RpiAuth.configuration.token_endpoint.scheme, - host: RpiAuth.configuration.token_endpoint.host, - port: RpiAuth.configuration.token_endpoint.port, - authorization_endpoint: RpiAuth.configuration.authorization_endpoint, - token_endpoint: RpiAuth.configuration.token_endpoint, - jwks_uri: RpiAuth.configuration.jwks_uri, - redirect_uri: URI.join(RpiAuth.configuration.host_url, CALLBACK_PATH) - }, - extra_authorize_params: { brand: RpiAuth.configuration.brand }, - allow_authorize_params: [:login_options], - origin_param: 'returnTo' - ) + provider(:openid_connect, openid_connect_options) OmniAuth.config.on_failure = RpiAuth::AuthController.action(:failure) RpiAuth.configuration.enable_auth_bypass if RpiAuth.configuration.bypass_auth end - # rubocop:enable Metrics/BlockLength end end end