UI for oidc account linking

This commit is contained in:
Moritz 2025-11-05 19:04:34 +01:00 committed by moritz
parent 87e54cb13f
commit 527657d37b
3 changed files with 256 additions and 15 deletions

View file

@ -24,28 +24,104 @@ defmodule MvWeb.AuthController do
end
def failure(conn, activity, reason) do
Logger.error(%{conn: conn, reason: reason})
# Log the error for debugging
Logger.warning(
"Authentication failure - Activity: #{inspect(activity)}, Reason: #{inspect(reason)}"
)
message =
case {activity, reason} do
{_,
%AshAuthentication.Errors.AuthenticationFailed{
caused_by: %Ash.Error.Forbidden{
errors: [%AshAuthentication.Errors.CannotConfirmUnconfirmedUser{}]
}
}} ->
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.
""")
_ ->
gettext("Incorrect email or password")
end
conn
|> put_flash(:error, message)
|> redirect(to: ~p"/sign-in")
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