Best way to handle actors/roles
Hi everyone! π
Weβre working on a multi-app Phoenix project and could use some advice on unifying authorization actors.
Setup
We have three apps β
api_web
, console_web
, and desk_web
β all sharing the same business logic and Ash resources.
Auth Flow
1. A user authenticates with an Auth.Account
(email-based).
2. After login, they choose a role, which becomes their Auth.User
.
3. That selected role is the actor.
Current Situation
api_web and console_web β using a custom Auth.Actor
struct that mirrors fields from Auth.User
.
* console_web specifically β no DB users; here, Auth.Actor
just has a type
(admin
, support
, user
) and we allow actions based on it (before checking for any user specific field).
* desk_web β using ash_authentication
, with Auth.User
as the actor.
Because of this, our shared Ash actions now need to handle both Auth.User
and Auth.Actor
, which feels messy and wrong.
Question
Is there a recommended way to unify this? Ideally, weβd like a single actor struct that represents both the authenticated Account
and the selected User
role, and use it consistently across all apps β including the one using ash_authentication
.
This is how we have the Phoenix assigns atm:
api_web - just standard Ash.PlugHelpers.set_actor(conn, actor)
desk_web - @current_account (after sign in) and @current_user (after role selector)
console_web - also Ash.PlugHelpers.set_actor(conn, actor)
but with another plug that assigns @current_user map with an actor struct inside it4 Replies
ideally we will migrate every app to ash_authentication eventually
having multiple actor types is normal for many cases
the app I'm working on has three already
the issues i face with different actor types is that in many actions we try to access fields that only exist in one like
actor.user_id
in user it would just be id
so we would have duplicate policies for these two fields. If the actors were truly different then maybe it would make sense but here they represent the same thing basically so it is better to be consistent with the struct i guessSolution
Gotcha, so normalizing them into a single struct makes perfect sense