diff --git a/lib/ash_authentication/plug/helpers.ex b/lib/ash_authentication/plug/helpers.ex index 594ca6e8f..91c810353 100644 --- a/lib/ash_authentication/plug/helpers.ex +++ b/lib/ash_authentication/plug/helpers.ex @@ -216,12 +216,13 @@ defmodule AshAuthentication.Plug.Helpers do authenticate_resource_from_session(resource, session, otp_app, opts), resource, options, - session + session, + opts ) end) end - defp handle_session_auth_result(conn, {:ok, user}, _resource, options, session) do + defp handle_session_auth_result(conn, {:ok, user}, _resource, options, session, _opts) do current_subject_name = current_subject_name(options.subject_name) # Restore authentication metadata from the session onto the user @@ -230,7 +231,7 @@ defmodule AshAuthentication.Plug.Helpers do Conn.assign(conn, current_subject_name, user) end - defp handle_session_auth_result(conn, :error, resource, options, _session) do + defp handle_session_auth_result(conn, :error, resource, options, _session, opts) do current_subject_name = current_subject_name(options.subject_name) require_token? = @@ -239,9 +240,22 @@ defmodule AshAuthentication.Plug.Helpers do session_key = if require_token?, do: session_key(options.subject_name), else: options.subject_name - conn - |> Conn.assign(current_subject_name, nil) - |> Conn.delete_session(session_key) + conn = Conn.assign(conn, current_subject_name, nil) + + if tenant_required?(resource, opts) do + # The lookup was rejected before it could reach the data layer because this + # request has no tenant, so we know nothing about whether the session is + # valid and must leave it alone rather than signing the user out. + conn + else + Conn.delete_session(conn, session_key) + end + end + + defp tenant_required?(resource, opts) do + is_nil(Keyword.get(opts, :tenant)) and + not is_nil(Resource.Info.multitenancy_strategy(resource)) and + not Resource.Info.multitenancy_global?(resource) end defp restore_metadata_from_session(user, subject_name, session) do diff --git a/test/ash_authentication/plug/helpers_test.exs b/test/ash_authentication/plug/helpers_test.exs index 0a29d40e5..eadcc835e 100644 --- a/test/ash_authentication/plug/helpers_test.exs +++ b/test/ash_authentication/plug/helpers_test.exs @@ -160,6 +160,33 @@ defmodule AshAuthentication.Plug.HelpersTest do refute conn.assigns.current_user_with_token_required end + test "when the subject can't be found it removes the session", %{conn: conn} do + conn = + conn + |> Conn.put_session("user", "jti:user?id=#{Ash.UUID.generate()}") + |> Helpers.retrieve_from_session(:ash_authentication) + + refute conn.assigns.current_user + refute conn.private.plug_session["user"] + end + + test "when a resource requires a tenant and the request has none it keeps the session", %{ + conn: conn + } do + # Without a tenant the query is rejected by Ash before it reaches the data + # layer, so we have learned nothing about whether the session is valid and + # must not discard it. + session_value = "jti:multi_tenant_user_with_web_authn?id=#{Ash.UUID.generate()}" + + conn = + conn + |> Conn.put_session("multi_tenant_user_with_web_authn", session_value) + |> Helpers.retrieve_from_session(:ash_authentication) + + refute conn.assigns.current_multi_tenant_user_with_web_authn + assert conn.private.plug_session["multi_tenant_user_with_web_authn"] == session_value + end + test "with opts", %{conn: conn} do # without token user = build_user()