fix(auth): trigger RP-initiated logout at OIDC provider

This commit is contained in:
Moritz 2026-06-01 19:59:52 +02:00
parent 22955bdd9e
commit ba66bc15db
4 changed files with 192 additions and 6 deletions

View file

@ -62,6 +62,87 @@ defmodule MvWeb.AuthControllerTest do
assert redirected_to(conn) == ~p"/"
end
describe "DELETE /sign-out with OIDC configured" do
@base_url "https://idp.example.com"
defp with_oidc_settings(fun) do
{:ok, settings} = Membership.get_settings()
prev = %{
oidc_client_id: settings.oidc_client_id,
oidc_base_url: settings.oidc_base_url,
oidc_redirect_uri: settings.oidc_redirect_uri,
oidc_client_secret: settings.oidc_client_secret
}
{:ok, _} =
Membership.update_settings(settings, %{
oidc_client_id: "test-client",
oidc_base_url: @base_url,
oidc_redirect_uri: "http://localhost:4000/auth/user/oidc/callback",
oidc_client_secret: "test-secret"
})
try do
fun.()
after
Mv.Oidc.Discovery.clear_cache()
{:ok, s} = Membership.get_settings()
Membership.update_settings(s, prev)
end
end
test "redirects to end_session_endpoint when discovery succeeds", %{
conn: authenticated_conn
} do
with_oidc_settings(fn ->
end_session_url = "https://idp.example.com/end-session"
Mv.Oidc.Discovery.put_cache(
@base_url,
{:ok, %{"end_session_endpoint" => end_session_url}}
)
conn =
authenticated_conn
|> conn_with_oidc_user()
|> delete(~p"/sign-out")
assert redirected_to(conn, 302) == end_session_url
end)
end
test "falls back to /sign-in?oidc_failed=1 when discovery fails", %{
conn: authenticated_conn
} do
with_oidc_settings(fn ->
Mv.Oidc.Discovery.put_cache(@base_url, {:error, :test_failure})
conn =
authenticated_conn
|> conn_with_oidc_user()
|> delete(~p"/sign-out")
assert redirected_to(conn) == "/sign-in?oidc_failed=1"
end)
end
test "falls back to /sign-in?oidc_failed=1 when end_session_endpoint is missing", %{
conn: authenticated_conn
} do
with_oidc_settings(fn ->
Mv.Oidc.Discovery.put_cache(@base_url, {:ok, %{"issuer" => @base_url}})
conn =
authenticated_conn
|> conn_with_oidc_user()
|> delete(~p"/sign-out")
assert redirected_to(conn) == "/sign-in?oidc_failed=1"
end)
end
end
defp csrf_token_from_sign_out_form(html) when is_binary(html) do
case Regex.run(~r/name="_csrf_token"[^>]*value="([^"]+)"/, html) do
[_, token] ->