diff --git a/app/controllers/one_login_controller.rb b/app/controllers/one_login_controller.rb new file mode 100644 index 00000000000..4bfd8109c5c --- /dev/null +++ b/app/controllers/one_login_controller.rb @@ -0,0 +1,58 @@ +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 sign_out + id_token = session[:onelogin_id_token] + one_login_error = session[:one_login_error] + reset_session + + session[:one_login_error] = one_login_error + # Go back to one login to sign out the user on their end as well + redirect_to logout_onelogin_path(id_token_hint: id_token) + 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 +end diff --git a/app/models/one_login_user.rb b/app/models/one_login_user.rb new file mode 100644 index 00000000000..f41cbccc2ed --- /dev/null +++ b/app/models/one_login_user.rb @@ -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 diff --git a/app/views/candidate_interface/start_page/create_account_or_sign_in.html.erb b/app/views/candidate_interface/start_page/create_account_or_sign_in.html.erb index 350a6fddd86..021b76df73d 100644 --- a/app/views/candidate_interface/start_page/create_account_or_sign_in.html.erb +++ b/app/views/candidate_interface/start_page/create_account_or_sign_in.html.erb @@ -4,31 +4,43 @@
+ <%= t('govuk.one_login_account_guidance') %> +
+ + <%= govuk_button_to(t('continue'), '/auth/onelogin') %> + <% else %> + <%= form_with( + model: @create_account_or_sign_in_form, + url: candidate_interface_create_account_or_sign_in_path(providerCode: params[:providerCode], courseCode: params[:courseCode]), + method: :post, + ) do |f| %> + <%= f.govuk_error_summary %> + +- You can usually start applying for teacher training in October, the - year before your course starts. Courses can fill up quickly, so apply - as soon as you can. - <%= govuk_link_to 'Read how the application process works', candidate_interface_guidance_path %>. -
++ You can usually start applying for teacher training in October, the + year before your course starts. Courses can fill up quickly, so apply + as soon as you can. + <%= govuk_link_to 'Read how the application process works', candidate_interface_guidance_path %>. +
+ <% end %>