84 lines
2.3 KiB
Ruby
84 lines
2.3 KiB
Ruby
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
|