From c9e2992fe09d7828e5e38f6f31c9e44aa83f1702 Mon Sep 17 00:00:00 2001 From: Dan Milne Date: Fri, 7 Nov 2025 16:38:18 +1100 Subject: [PATCH] Switch to diy oauth --- VERSION | 1 + app/controllers/oidc_auth_controller.rb | 83 ++++++++++++++++++++++ test/policies/network_range_policy_test.rb | 18 +++++ test/policies/rule_policy_test.rb | 18 +++++ 4 files changed, 120 insertions(+) create mode 100644 VERSION create mode 100644 app/controllers/oidc_auth_controller.rb create mode 100644 test/policies/network_range_policy_test.rb create mode 100644 test/policies/rule_policy_test.rb diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..17e51c3 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.1 diff --git a/app/controllers/oidc_auth_controller.rb b/app/controllers/oidc_auth_controller.rb new file mode 100644 index 0000000..d3a2f6a --- /dev/null +++ b/app/controllers/oidc_auth_controller.rb @@ -0,0 +1,83 @@ +class OidcAuthController < ApplicationController + allow_unauthenticated_access only: [:authorize, :callback] + + # POST /auth/oidc - Initiate OIDC flow + def authorize + redirect_to oidc_client.authorization_uri( + scope: [:openid, :email, :profile], + state: generate_state_token, + nonce: generate_nonce + ), allow_other_host: true + end + + # GET /auth/oidc/callback - Handle provider callback + def callback + # Verify state token + unless valid_state_token?(params[:state]) + redirect_to new_session_path, alert: "Invalid authentication state" + return + end + + # Exchange authorization code for tokens + oidc_client.authorization_code = params[:code] + access_token = oidc_client.access_token! + + # Get user info + user_info = access_token.userinfo! + + # Find user by email + user = User.find_by(email_address: user_info.email) + + unless user + redirect_to new_session_path, alert: "No user found with email: #{user_info.email}" + return + end + + # Update role based on OIDC groups if present + if user_info.respond_to?(:groups) && user_info.groups.present? + user.update_role_from_oidc_groups(user_info.groups) + end + + start_new_session_for(user) + redirect_to root_path, notice: "Successfully signed in via OIDC" + + rescue OpenIDConnect::Exception, Rack::OAuth2::Client::Error => e + Rails.logger.error "OIDC authentication failed: #{e.message}" + redirect_to new_session_path, alert: "Authentication failed: #{e.message}" + end + + private + + def oidc_client + @oidc_client ||= begin + discovery = OpenIDConnect::Discovery::Provider::Config.discover!(ENV['OIDC_DISCOVERY_URL']) + + OpenIDConnect::Client.new( + identifier: ENV['OIDC_CLIENT_ID'], + secret: ENV['OIDC_CLIENT_SECRET'], + redirect_uri: ENV['OIDC_REDIRECT_URI'] || oidc_callback_url, + authorization_endpoint: discovery.authorization_endpoint, + token_endpoint: discovery.token_endpoint, + userinfo_endpoint: discovery.userinfo_endpoint + ) + end + end + + def generate_state_token + token = SecureRandom.hex(32) + session[:oidc_state] = token + token + end + + def generate_nonce + SecureRandom.hex(32) + end + + def valid_state_token?(state) + state.present? && session[:oidc_state] == state + end + + def oidc_callback_url + "#{request.base_url}/auth/oidc/callback" + end +end diff --git a/test/policies/network_range_policy_test.rb b/test/policies/network_range_policy_test.rb new file mode 100644 index 0000000..77c19fe --- /dev/null +++ b/test/policies/network_range_policy_test.rb @@ -0,0 +1,18 @@ +require 'test_helper' + +class NetworkRangePolicyTest < ActiveSupport::TestCase + def test_scope + end + + def test_show + end + + def test_create + end + + def test_update + end + + def test_destroy + end +end diff --git a/test/policies/rule_policy_test.rb b/test/policies/rule_policy_test.rb new file mode 100644 index 0000000..d102acc --- /dev/null +++ b/test/policies/rule_policy_test.rb @@ -0,0 +1,18 @@ +require 'test_helper' + +class RulePolicyTest < ActiveSupport::TestCase + def test_scope + end + + def test_show + end + + def test_create + end + + def test_update + end + + def test_destroy + end +end