-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
We want to move our candidates to use one login when sigin up/in to our service. This commit adds the basic implementation for users to login or create a candidate account in apply using one login.
- Loading branch information
1 parent
24aefda
commit 91a1081
Showing
17 changed files
with
714 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
class OneLoginController < ApplicationController | ||
before_action :one_login_enabled | ||
before_action :set_sentry_context, only: :callback | ||
|
||
def callback | ||
auth = request.env['omniauth.auth'] | ||
session[:onelogin_id_token] = auth&.credentials&.id_token | ||
candidate = OneLoginUser.authentificate(auth) | ||
|
||
sign_in(candidate, scope: :candidate) | ||
candidate.update!(last_signed_in_at: Time.zone.now) | ||
|
||
redirect_to candidate_interface_interstitial_path | ||
rescue OneLoginUser::Error => e | ||
session[:one_login_error] = e.message | ||
redirect_to auth_onelogin_sign_out_path | ||
end | ||
|
||
def bypass_callback | ||
one_login_user_bypass = OneLoginUserBypass.new( | ||
token: request.env['omniauth.auth']&.uid, | ||
) | ||
candidate = one_login_user_bypass.authentificate | ||
|
||
if one_login_user_bypass.valid? && candidate.present? | ||
sign_in(candidate, scope: :candidate) | ||
candidate.update!(last_signed_in_at: Time.zone.now) | ||
|
||
redirect_to candidate_interface_interstitial_path | ||
else | ||
flash[:warning] = one_login_user_bypass.errors.full_messages.join('\n') | ||
redirect_to candidate_interface_create_account_or_sign_in_path | ||
end | ||
end | ||
|
||
def sign_out | ||
id_token = session[:onelogin_id_token] | ||
one_login_error = session[:one_login_error] | ||
reset_session | ||
|
||
session[:one_login_error] = one_login_error | ||
if OneLogin.bypass? | ||
redirect_to candidate_interface_create_account_or_sign_in_path | ||
else | ||
# Go back to one login to sign out the user on their end as well | ||
redirect_to logout_onelogin(id_token), allow_other_host: true | ||
end | ||
end | ||
|
||
def sign_out_complete | ||
if session[:one_login_error].present? | ||
Sentry.capture_message(session[:one_login_error], level: :error) | ||
redirect_to internal_server_error_path | ||
else | ||
redirect_to candidate_interface_create_account_or_sign_in_path | ||
end | ||
end | ||
|
||
def failure | ||
session[:one_login_error] = "One login failure with #{params[:message]} " \ | ||
"for onelogin_id_token: #{session[:onelogin_id_token]}" | ||
|
||
redirect_to auth_onelogin_sign_out_path | ||
end | ||
|
||
private | ||
|
||
def one_login_enabled | ||
return if FeatureFlag.active?(:one_login_candidate_sign_in) | ||
|
||
redirect_to root_path | ||
end | ||
|
||
def set_sentry_context | ||
Sentry.set_extras( | ||
omniauth_hash: request.env['omniauth.auth'].to_h, | ||
) | ||
end | ||
|
||
def logout_onelogin(id_token_hint) | ||
params = { | ||
post_logout_redirect_uri: URI(auth_onelogin_sign_out_complete_url), | ||
id_token_hint:, | ||
} | ||
URI.parse("#{ENV['GOVUK_ONE_LOGIN_ISSUER_URL']}logout").tap do |uri| | ||
uri.query = URI.encode_www_form(params) | ||
end.to_s | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
class OneLoginUser | ||
class Error < StandardError; end | ||
attr_reader :email_address, :token | ||
|
||
def initialize(omniauth_object) | ||
@email_address = omniauth_object.info.email | ||
@token = omniauth_object.uid | ||
end | ||
|
||
def self.authentificate(omniauth_auth) | ||
new(omniauth_auth).authentificate | ||
end | ||
|
||
def authentificate | ||
one_login_auth = OneLoginAuth.find_by(token:) | ||
existing_candidate = Candidate.find_by(email_address:) | ||
|
||
return candidate_with_one_login(one_login_auth) if one_login_auth | ||
return existing_candidate_without_one_login(existing_candidate) if existing_candidate | ||
|
||
created_candidate | ||
end | ||
|
||
private | ||
|
||
def candidate_with_one_login(one_login_auth) | ||
one_login_auth.update!(email_address:) | ||
one_login_auth.candidate | ||
end | ||
|
||
def existing_candidate_without_one_login(existing_candidate) | ||
if existing_candidate.one_login_auth.present? && existing_candidate.one_login_auth.token != token | ||
raise( | ||
Error, | ||
"Candidate #{existing_candidate.id} has a different one login " \ | ||
"token than the user trying to login. Token used to auth #{token}", | ||
) | ||
end | ||
|
||
existing_candidate.create_one_login_auth!(token:, email_address:) | ||
existing_candidate | ||
end | ||
|
||
def created_candidate | ||
candidate = Candidate.create!(email_address:) | ||
candidate.create_one_login_auth!(token:, email_address:) | ||
|
||
candidate | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
class OneLoginUserBypass | ||
include ActiveModel::Model | ||
|
||
validates :token, presence: true | ||
|
||
attr_accessor :token | ||
|
||
def authentificate | ||
return unless valid? | ||
|
||
one_login_auth = OneLoginAuth.find_by(token:) | ||
|
||
return one_login_auth.candidate if one_login_auth | ||
|
||
created_candidate | ||
end | ||
|
||
private | ||
|
||
def created_candidate | ||
candidate = Candidate.create!(email_address: bypass_email_address) | ||
candidate.create_one_login_auth!(token:, email_address: bypass_email_address) | ||
|
||
candidate | ||
end | ||
|
||
def bypass_email_address | ||
"#{token}@example.com" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
module OneloginSetup | ||
class OmniAuth::Strategies::GovukOneLogin < OmniAuth::Strategies::OpenIDConnect; end | ||
|
||
def self.configure(builder) | ||
client_id = ENV.fetch('GOVUK_ONE_LOGIN_CLIENT_ID', '') | ||
onelogin_issuer_uri = URI(ENV.fetch('GOVUK_ONE_LOGIN_ISSUER_URL', '')) | ||
private_key_pem = ENV.fetch('GOVUK_ONE_LOGIN_PRIVATE_KEY', '') | ||
application_url = HostingEnvironment.application_url | ||
|
||
begin | ||
private_key_pem = private_key_pem.gsub('\n', "\n") | ||
private_key = OpenSSL::PKey::RSA.new(private_key_pem) | ||
rescue OpenSSL::PKey::RSAError => e | ||
Rails.logger.warn "GOVUK ONE LOGIN PRIVATE error, is the key present? #{e.message}" | ||
end | ||
|
||
builder.provider :govuk_one_login, | ||
name: :onelogin, | ||
allow_authorize_params: %i[session_id], | ||
callback_path: '/auth/onelogin/callback', | ||
discovery: true, | ||
issuer: onelogin_issuer_uri.to_s, | ||
path_prefix: '/auth', | ||
post_logout_redirect_uri: "#{application_url}/auth/onelogin/sign-out-complete", | ||
response_type: :code, | ||
scope: %w[email openid], | ||
client_auth_method: :jwt_bearer, | ||
client_options: { | ||
authorization_endpoint: '/oauth2/authorize', | ||
end_session_endpoint: '/oauth2/logout', | ||
token_endpoint: '/oauth2/token', | ||
userinfo_endpoint: '/oauth2/userinfo', | ||
host: onelogin_issuer_uri.host, | ||
identifier: client_id, | ||
port: 443, | ||
redirect_uri: "#{application_url}/auth/onelogin/callback", | ||
scheme: 'https', | ||
private_key: private_key, | ||
} | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
require 'omniauth' | ||
|
||
module OmniAuth | ||
module Strategies | ||
class OneLoginDeveloper < Developer | ||
include OmniAuth::Strategy | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
FactoryBot.define do | ||
factory :one_login_auth do | ||
candidate | ||
token { SecureRandom.hex(10) } | ||
email_address { "#{SecureRandom.hex(5)}@example.com" } | ||
end | ||
end |
Oops, something went wrong.