Fix OIDC account linking by using SystemActor in LinkOidcAccountLive
All checks were successful
continuous-integration/drone/push Build is passing

- Add SystemActor to all Ash operations in LinkOidcAccountLive
- Enables user lookup, reload, and oidc_id linking during OIDC flow
- User is not yet logged in during linking, so SystemActor provides authorization
This commit is contained in:
Moritz 2026-01-23 02:14:59 +01:00
parent bad4e5ca7c
commit 41e342a1d6

View file

@ -20,10 +20,13 @@ defmodule MvWeb.LinkOidcAccountLive do
@impl true
def mount(_params, session, socket) do
# Use SystemActor for authorization during OIDC linking (user is not yet logged in)
system_actor = Mv.Helpers.SystemActor.get_system_actor()
with user_id when not is_nil(user_id) <- Map.get(session, "oidc_linking_user_id"),
oidc_user_info when not is_nil(oidc_user_info) <-
Map.get(session, "oidc_linking_user_info"),
{:ok, user} <- Ash.get(Mv.Accounts.User, user_id) do
{:ok, user} <- Ash.get(Mv.Accounts.User, user_id, actor: system_actor) do
# Check if user is passwordless
if passwordless?(user) do
# Auto-link passwordless user immediately
@ -46,9 +49,12 @@ defmodule MvWeb.LinkOidcAccountLive do
end
defp reload_user!(user_id) do
# Use SystemActor for authorization during OIDC linking (user is not yet logged in)
system_actor = Mv.Helpers.SystemActor.get_system_actor()
Mv.Accounts.User
|> Ash.Query.filter(id == ^user_id)
|> Ash.read_one!()
|> Ash.read_one!(actor: system_actor)
end
defp reset_password_form(socket) do
@ -58,13 +64,16 @@ defmodule MvWeb.LinkOidcAccountLive do
defp auto_link_passwordless_user(socket, user, oidc_user_info) do
oidc_id = Map.get(oidc_user_info, "sub") || Map.get(oidc_user_info, "id")
# Use SystemActor for authorization (passwordless user auto-linking)
system_actor = Mv.Helpers.SystemActor.get_system_actor()
case user.id
|> reload_user!()
|> Ash.Changeset.for_update(:link_oidc_id, %{
oidc_id: oidc_id,
oidc_user_info: oidc_user_info
})
|> Ash.update() do
|> Ash.update(actor: system_actor) do
{:ok, updated_user} ->
Logger.info(
"Passwordless account auto-linked to OIDC: user_id=#{updated_user.id}, oidc_id=#{oidc_id}"
@ -187,6 +196,9 @@ defmodule MvWeb.LinkOidcAccountLive do
defp link_oidc_account(socket, user, oidc_user_info) do
oidc_id = Map.get(oidc_user_info, "sub") || Map.get(oidc_user_info, "id")
# Use SystemActor for authorization (user just verified password but is not yet logged in)
system_actor = Mv.Helpers.SystemActor.get_system_actor()
# Update the user with the OIDC ID
case user.id
|> reload_user!()
@ -194,7 +206,7 @@ defmodule MvWeb.LinkOidcAccountLive do
oidc_id: oidc_id,
oidc_user_info: oidc_user_info
})
|> Ash.update() do
|> Ash.update(actor: system_actor) do
{:ok, updated_user} ->
# After successful linking, redirect to OIDC login
# Since the user now has an oidc_id, the next OIDC login will succeed