Merge origin/main into bugfix/rund-seeds-just-once
All checks were successful
continuous-integration/drone/push Build is passing

Resolved conflicts:
- CHANGELOG.md: keep FORCE_SEEDS and Seeds entries, keep OIDC/Unauthenticated from main
- auth_controller.ex: use sign_in_path_after_oidc_failure() for OIDC-only redirect
- oidc_only_sign_in_redirect.ex: take main's oidc_failed=1 handling
- gettext default.po: remove obsolete #~ block
- auth_controller_test.exs: keep test for oidc_failed=1 redirect loop

Made-with: Cursor
This commit is contained in:
Simon 2026-03-16 19:25:18 +01:00
commit 28c40ec2a9
Signed by: simon
GPG key ID: 40E7A58C4AA1EDB2
9 changed files with 108 additions and 39 deletions

View file

@ -21,7 +21,7 @@ defmodule MvWeb.AuthController do
if Config.oidc_only?() do
conn
|> put_flash(:error, gettext("Only sign-in via Single Sign-On (SSO) is allowed."))
|> redirect(to: ~p"/sign-in")
|> redirect(to: sign_in_path_after_oidc_failure())
else
success_continue(conn, {:password, :sign_in}, user, token)
end
@ -149,7 +149,7 @@ defmodule MvWeb.AuthController do
_ ->
conn
|> put_flash(:error, gettext("Unable to authenticate with OIDC. Please try again."))
|> redirect(to: ~p"/sign-in")
|> redirect(to: sign_in_path_after_oidc_failure())
end
end
@ -163,7 +163,7 @@ defmodule MvWeb.AuthController do
:error,
gettext("The authentication server is currently unavailable. Please try again later.")
)
|> redirect(to: ~p"/sign-in")
|> redirect(to: sign_in_path_after_oidc_failure())
end
# Handle Assent invalid response errors (configuration or malformed responses)
@ -176,7 +176,7 @@ defmodule MvWeb.AuthController do
:error,
gettext("Authentication configuration error. Please contact the administrator.")
)
|> redirect(to: ~p"/sign-in")
|> redirect(to: sign_in_path_after_oidc_failure())
end
# Catch-all clause for any other error types
@ -186,7 +186,7 @@ defmodule MvWeb.AuthController do
conn
|> put_flash(:error, gettext("Unable to authenticate with OIDC. Please try again."))
|> redirect(to: ~p"/sign-in")
|> redirect(to: sign_in_path_after_oidc_failure())
end
# Handle generic AuthenticationFailed errors
@ -226,10 +226,14 @@ defmodule MvWeb.AuthController do
conn
|> put_flash(:error, error_message)
|> redirect(to: ~p"/sign-in")
|> redirect(to: sign_in_path_after_oidc_failure())
end
end
# Path used when redirecting to sign-in after an OIDC failure. The query param tells
# OidcOnlySignInRedirect to show the sign-in page instead of redirecting back to OIDC (avoids loop).
defp sign_in_path_after_oidc_failure, do: "/sign-in?oidc_failed=1"
# Extract meaningful error message from Ash errors
defp extract_meaningful_error_message(errors) do
# Look for specific error messages in InvalidAttribute errors

View file

@ -900,17 +900,17 @@ defmodule MvWeb.GlobalSettingsLive do
saves_vereinfacht = vereinfacht_params?(setting_params_clean)
case MvWeb.LiveHelpers.submit_form(socket.assigns.form, setting_params_clean, actor) do
{:ok, _updated_settings} ->
{:ok, fresh_settings} = Membership.get_settings()
{:ok, updated_settings} ->
# Use the returned record for the form so saved values show immediately;
# get_settings() can return cached data without the new attribute until reload.
test_result =
if saves_vereinfacht, do: Mv.Vereinfacht.test_connection(), else: nil
socket =
socket
|> assign(:settings, fresh_settings)
|> assign(:registration_enabled, fresh_settings.registration_enabled != false)
|> assign(:vereinfacht_api_key_set, present?(fresh_settings.vereinfacht_api_key))
|> assign(:settings, updated_settings)
|> assign(:registration_enabled, updated_settings.registration_enabled != false)
|> assign(:vereinfacht_api_key_set, present?(updated_settings.vereinfacht_api_key))
|> assign(:oidc_client_secret_set, Mv.Config.oidc_client_secret_set?())
|> assign(:oidc_only, Mv.Config.oidc_only?())
|> assign(:oidc_configured, Mv.Config.oidc_configured?())

View file

@ -2,6 +2,8 @@ defmodule MvWeb.Plugs.OidcOnlySignInRedirect do
@moduledoc """
When OIDC-only mode is active:
- GET /sign-in redirects to the OIDC flow when OIDC is configured (sign-in page skipped).
- GET /sign-in?oidc_failed=1 is not redirected, so the sign-in page is shown after an OIDC
failure (avoids redirect loop when the provider is down or misconfigured).
- GET /auth/user/password/sign_in_with_token is rejected (redirect to /sign-in with error)
so password sign-in cannot complete.
"""
@ -19,19 +21,29 @@ defmodule MvWeb.Plugs.OidcOnlySignInRedirect do
end
defp maybe_redirect_sign_in_to_oidc(conn) do
if conn.request_path == "/sign-in" and conn.method == "GET" do
if Config.oidc_only?() and Config.oidc_configured?() do
conn
|> redirect(to: "/auth/user/oidc")
|> halt()
else
conn
end
else
if conn.request_path != "/sign-in" or conn.method != "GET" do
conn
else
conn = fetch_query_params(conn)
maybe_redirect_sign_in_to_oidc_checked(conn)
end
end
defp maybe_redirect_sign_in_to_oidc_checked(conn) do
cond do
# Show sign-in page when returning from OIDC failure to avoid redirect loop.
conn.query_params["oidc_failed"] -> conn
Config.oidc_only?() and Config.oidc_configured?() -> redirect_and_halt(conn)
true -> conn
end
end
defp redirect_and_halt(conn) do
conn
|> redirect(to: "/auth/user/oidc")
|> halt()
end
defp maybe_reject_password_token_sign_in(conn) do
if conn.halted, do: conn, else: reject_password_token_sign_in_if_applicable(conn)
end