diff --git a/test/accounts/user_authentication_test.exs b/test/accounts/user_authentication_test.exs index da84e81..e1dde87 100644 --- a/test/accounts/user_authentication_test.exs +++ b/test/accounts/user_authentication_test.exs @@ -130,6 +130,10 @@ defmodule Mv.Accounts.UserAuthenticationTest do ) case result do + {:ok, found_user} when is_struct(found_user) -> + assert found_user.id == user.id + assert found_user.oidc_id == "oidc_identifier_12345" + {:ok, [found_user]} -> assert found_user.id == user.id assert found_user.oidc_id == "oidc_identifier_12345" @@ -137,6 +141,9 @@ defmodule Mv.Accounts.UserAuthenticationTest do {:ok, []} -> flunk("User should be found by oidc_id") + {:ok, nil} -> + flunk("User should be found by oidc_id") + {:error, error} -> flunk("Unexpected error: #{inspect(error)}") end @@ -231,11 +238,14 @@ defmodule Mv.Accounts.UserAuthenticationTest do actor: system_actor ) - # Either returns empty list OR authentication error - both mean "user not found" + # Either returns empty/nil OR authentication error - both mean "user not found" case result do {:ok, []} -> :ok + {:ok, nil} -> + :ok + {:error, %Ash.Error.Forbidden{errors: [%AshAuthentication.Errors.AuthenticationFailed{}]}} -> :ok @@ -272,11 +282,14 @@ defmodule Mv.Accounts.UserAuthenticationTest do actor: system_actor ) - # Either returns empty list OR authentication error - both mean "user not found" + # Either returns empty/nil OR authentication error - both mean "user not found" case result do {:ok, []} -> :ok + {:ok, nil} -> + :ok + {:error, %Ash.Error.Forbidden{errors: [%AshAuthentication.Errors.AuthenticationFailed{}]}} -> :ok diff --git a/test/mv/oidc_role_sync_test.exs b/test/mv/oidc_role_sync_test.exs index acde5b5..d05441b 100644 --- a/test/mv/oidc_role_sync_test.exs +++ b/test/mv/oidc_role_sync_test.exs @@ -83,6 +83,25 @@ defmodule Mv.OidcRoleSyncTest do {:ok, after_user} = get_user(user1.id) assert after_user.role_id == mitglied_role_id() end + + test "when user_info has no groups, groups are read from access_token JWT (e.g. Rauthy)" do + email = "sync-from-token-#{System.unique_integer([:positive])}@test.example.com" + {:ok, user} = create_user_with_mitglied(email) + user_info = %{"sub" => "oidc-123"} + + # Minimal JWT: header.payload.signature with "groups" in payload (Rauthy puts groups in access_token) + payload = Jason.encode!(%{"groups" => ["mila-admin"], "sub" => "oidc-123"}) + payload_b64 = Base.url_encode64(payload, padding: false) + header_b64 = Base.url_encode64("{\"alg\":\"HS256\",\"typ\":\"JWT\"}", padding: false) + sig_b64 = Base.url_encode64("sig", padding: false) + access_token = "#{header_b64}.#{payload_b64}.#{sig_b64}" + oauth_tokens = %{"access_token" => access_token} + + assert :ok = OidcRoleSync.apply_admin_role_from_user_info(user, user_info, oauth_tokens) + + {:ok, after_user} = get_user(user.id) + assert after_user.role_id == admin_role_id() + end end # B3: Role sync after registration is implemented via after_action in register_with_rauthy. diff --git a/test/mv_web/controllers/oidc_e2e_flow_test.exs b/test/mv_web/controllers/oidc_e2e_flow_test.exs index fbd59d2..76dd266 100644 --- a/test/mv_web/controllers/oidc_e2e_flow_test.exs +++ b/test/mv_web/controllers/oidc_e2e_flow_test.exs @@ -37,7 +37,7 @@ defmodule MvWeb.OidcE2EFlowTest do assert is_nil(new_user.hashed_password) # Verify user can be found by oidc_id - {:ok, [found_user]} = + result = Mv.Accounts.read_sign_in_with_rauthy( %{ user_info: user_info, @@ -46,6 +46,13 @@ defmodule MvWeb.OidcE2EFlowTest do actor: actor ) + found_user = + case result do + {:ok, u} when is_struct(u) -> u + {:ok, [u]} -> u + _ -> flunk("Expected user, got: #{inspect(result)}") + end + assert found_user.id == new_user.id end end @@ -177,7 +184,7 @@ defmodule MvWeb.OidcE2EFlowTest do assert linked_user.hashed_password == password_user.hashed_password # Step 5: User can now sign in via OIDC - {:ok, [signed_in_user]} = + result = Mv.Accounts.read_sign_in_with_rauthy( %{ user_info: user_info, @@ -186,6 +193,13 @@ defmodule MvWeb.OidcE2EFlowTest do actor: actor ) + signed_in_user = + case result do + {:ok, u} when is_struct(u) -> u + {:ok, [u]} -> u + _ -> flunk("Expected user, got: #{inspect(result)}") + end + assert signed_in_user.id == password_user.id assert signed_in_user.oidc_id == "oidc_link_888" end @@ -331,6 +345,9 @@ defmodule MvWeb.OidcE2EFlowTest do {:ok, []} -> :ok + {:ok, nil} -> + :ok + {:error, %Ash.Error.Forbidden{}} -> :ok diff --git a/test/mv_web/controllers/oidc_integration_test.exs b/test/mv_web/controllers/oidc_integration_test.exs index 650158a..cdd352e 100644 --- a/test/mv_web/controllers/oidc_integration_test.exs +++ b/test/mv_web/controllers/oidc_integration_test.exs @@ -27,7 +27,7 @@ defmodule MvWeb.OidcIntegrationTest do # Test sign_in_with_rauthy action directly system_actor = Mv.Helpers.SystemActor.get_system_actor() - {:ok, [found_user]} = + result = Mv.Accounts.read_sign_in_with_rauthy( %{ user_info: user_info, @@ -36,6 +36,13 @@ defmodule MvWeb.OidcIntegrationTest do actor: system_actor ) + found_user = + case result do + {:ok, u} when is_struct(u) -> u + {:ok, [u]} -> u + _ -> flunk("Expected user, got: #{inspect(result)}") + end + assert found_user.id == user.id assert to_string(found_user.email) == "existing@example.com" assert found_user.oidc_id == "existing_oidc_123" @@ -104,6 +111,9 @@ defmodule MvWeb.OidcIntegrationTest do {:ok, []} -> :ok + {:ok, nil} -> + :ok + {:error, %Ash.Error.Forbidden{errors: [%AshAuthentication.Errors.AuthenticationFailed{}]}} -> :ok @@ -129,7 +139,7 @@ defmodule MvWeb.OidcIntegrationTest do system_actor = Mv.Helpers.SystemActor.get_system_actor() - {:ok, [found_user]} = + result = Mv.Accounts.read_sign_in_with_rauthy( %{ user_info: correct_user_info, @@ -138,6 +148,13 @@ defmodule MvWeb.OidcIntegrationTest do actor: system_actor ) + found_user = + case result do + {:ok, u} when is_struct(u) -> u + {:ok, [u]} -> u + _ -> flunk("Expected user, got: #{inspect(result)}") + end + assert found_user.id == user.id # Try with wrong oidc_id but correct email @@ -155,11 +172,14 @@ defmodule MvWeb.OidcIntegrationTest do actor: system_actor ) - # Either returns empty list OR authentication error - both mean "user not found" + # Either returns empty/nil OR authentication error - both mean "user not found" case result do {:ok, []} -> :ok + {:ok, nil} -> + :ok + {:error, %Ash.Error.Forbidden{errors: [%AshAuthentication.Errors.AuthenticationFailed{}]}} -> :ok @@ -193,11 +213,14 @@ defmodule MvWeb.OidcIntegrationTest do actor: system_actor ) - # Either returns empty list OR authentication error - both mean "user not found" + # Either returns empty/nil OR authentication error - both mean "user not found" case result do {:ok, []} -> :ok + {:ok, nil} -> + :ok + {:error, %Ash.Error.Forbidden{errors: [%AshAuthentication.Errors.AuthenticationFailed{}]}} -> :ok