135 lines
4.4 KiB
Elixir
135 lines
4.4 KiB
Elixir
require Logger
|
|
|
|
defmodule MvWeb.AuthController do
|
|
use MvWeb, :controller
|
|
use AshAuthentication.Phoenix.Controller
|
|
|
|
def success(conn, activity, user, _token) do
|
|
return_to = get_session(conn, :return_to) || ~p"/"
|
|
|
|
message =
|
|
case activity do
|
|
{:confirm_new_user, :confirm} -> gettext("Your email address has now been confirmed")
|
|
{:password, :reset} -> gettext("Your password has successfully been reset")
|
|
_ -> gettext("You are now signed in")
|
|
end
|
|
|
|
conn
|
|
|> delete_session(:return_to)
|
|
|> store_in_session(user)
|
|
# If your resource has a different name, update the assign name here (i.e :current_admin)
|
|
|> assign(:current_user, user)
|
|
|> put_flash(:info, message)
|
|
|> redirect(to: return_to)
|
|
end
|
|
|
|
def failure(conn, activity, reason) do
|
|
# Log the error for debugging
|
|
Logger.warning(
|
|
"Authentication failure - Activity: #{inspect(activity)}, Reason: #{inspect(reason)}"
|
|
)
|
|
|
|
case {activity, reason} do
|
|
# OIDC registration with existing email requires password verification (direct error)
|
|
{{:rauthy, :register}, %Ash.Error.Invalid{errors: errors}} ->
|
|
handle_oidc_email_collision(conn, errors)
|
|
|
|
# OIDC registration with existing email (wrapped in AuthenticationFailed)
|
|
{{:rauthy, :register},
|
|
%AshAuthentication.Errors.AuthenticationFailed{
|
|
caused_by: %Ash.Error.Invalid{errors: errors}
|
|
}} ->
|
|
handle_oidc_email_collision(conn, errors)
|
|
|
|
# OIDC sign-in failure (wrapped)
|
|
{{:rauthy, :sign_in}, %AshAuthentication.Errors.AuthenticationFailed{caused_by: caused_by}} ->
|
|
# Check if it's actually a registration issue
|
|
case caused_by do
|
|
%Ash.Error.Invalid{errors: errors} ->
|
|
handle_oidc_email_collision(conn, errors)
|
|
|
|
_ ->
|
|
# Real sign-in failure
|
|
conn
|
|
|> put_flash(:error, gettext("Unable to sign in with OIDC. Please try again."))
|
|
|> redirect(to: ~p"/sign-in")
|
|
end
|
|
|
|
# OIDC callback failure (can be either sign-in or registration)
|
|
{{:rauthy, :callback}, %AshAuthentication.Errors.AuthenticationFailed{caused_by: caused_by}} ->
|
|
case caused_by do
|
|
%Ash.Error.Invalid{errors: errors} ->
|
|
handle_oidc_email_collision(conn, errors)
|
|
|
|
_ ->
|
|
conn
|
|
|> put_flash(:error, gettext("Unable to authenticate with OIDC. Please try again."))
|
|
|> redirect(to: ~p"/sign-in")
|
|
end
|
|
|
|
{_,
|
|
%AshAuthentication.Errors.AuthenticationFailed{
|
|
caused_by: %Ash.Error.Forbidden{
|
|
errors: [%AshAuthentication.Errors.CannotConfirmUnconfirmedUser{}]
|
|
}
|
|
}} ->
|
|
message =
|
|
gettext("""
|
|
You have already signed in another way, but have not confirmed your account.
|
|
You can confirm your account using the link we sent to you, or by resetting your password.
|
|
""")
|
|
|
|
conn
|
|
|> put_flash(:error, message)
|
|
|> redirect(to: ~p"/sign-in")
|
|
|
|
_ ->
|
|
message = gettext("Incorrect email or password")
|
|
|
|
conn
|
|
|> put_flash(:error, message)
|
|
|> redirect(to: ~p"/sign-in")
|
|
end
|
|
end
|
|
|
|
# Handle OIDC email collision - user needs to verify password
|
|
defp handle_oidc_email_collision(conn, errors) do
|
|
password_verification_error =
|
|
Enum.find(errors, fn err ->
|
|
match?(%Mv.Accounts.User.Errors.PasswordVerificationRequired{}, err)
|
|
end)
|
|
|
|
case password_verification_error do
|
|
%Mv.Accounts.User.Errors.PasswordVerificationRequired{
|
|
user_id: user_id,
|
|
oidc_user_info: oidc_user_info
|
|
} ->
|
|
# Store the OIDC info in session for the linking flow
|
|
conn
|
|
|> put_session(:oidc_linking_user_id, user_id)
|
|
|> put_session(:oidc_linking_user_info, oidc_user_info)
|
|
|> put_flash(
|
|
:info,
|
|
gettext(
|
|
"An account with this email already exists. Please verify your password to link your OIDC account."
|
|
)
|
|
)
|
|
|> redirect(to: ~p"/auth/link-oidc-account")
|
|
|
|
_ ->
|
|
# Other validation errors - show generic error
|
|
conn
|
|
|> put_flash(:error, gettext("Unable to sign in. Please try again."))
|
|
|> redirect(to: ~p"/sign-in")
|
|
end
|
|
end
|
|
|
|
def sign_out(conn, _params) do
|
|
return_to = get_session(conn, :return_to) || ~p"/"
|
|
|
|
conn
|
|
|> clear_session(:mv)
|
|
|> put_flash(:info, gettext("You are now signed out"))
|
|
|> redirect(to: return_to)
|
|
end
|
|
end
|