Hello everyone, I’m building a collaborative editor where users can belong to multiple teams. I’ve decided to set up multi-tenancy on my user resource using the context strategy. So far, so good. However, Ash’s default behavior seems to require a team to be created before a user can be created and associated with it. This makes sense in general, but I need a different approach.
If you look at Notion, Figma, Vercel, Supabase, and many other applications with a “team” (organization, group, etc.) architecture, you’ll notice that they often register the user first. Then, the application creates a default organization, often using the user’s username, name, or other information.
Is there a way to adapt Ash’s expected flow to achieve this pattern? Something like:
User registration → Onboarding → Create user organization → Redirect to app home
I realize this is likely more of a design consideration than an Ash issue. If anyone has already tackled and solved this, I’d greatly appreciate any help. Thanks again, everyone!
My suggestion would be to create the users organization immediately on register, and then allow them to update/customize it after the fact. You can attach before/after action hooks to the register action for example.
Zach, i’ve also noticed that i’m not being able to register user with the default setup for magic link, do i have to make some tweak ? I am using ash with Inertia, therefore i am dispatching action by myself, here’s a snippet from my auth_controller
def magic_link_request(conn, user_params) do
strategy = Info.strategy!(User, :magic_link)
case Strategy.action(strategy, :request, user_params) do
:ok ->
redirect(conn, to: ~p"/login")
{:error, _} ->
conn
|> put_flash(:error, "Failed to send magic link")
|> redirect(to: ~p"/login")
end
end
After doing a multi-tenancy setup the emails aren’t being send anymore, can you help with this ?
You’ll need to make sure that you’re passing the tenant option. Additionally, we are releasing some fixes for multi tenancy + Ash Authentication shortly.
Yeah i got it but in that case i am just requesting the magic link, the user was not created yet so i cannot create an organization for this user yet, so there’s no tenant option to use (following your suggestion in above answer). So can it be adapted ?
Ah, sorry was a bit distracted and didn’t realize this was the same thread.
You can create an organization right there in that handler if one doesn’t exist yet for that user, and then you can set the tenant on the action. If you want to do it transactionally in the case that something goes wrong.
action :send_magic_link_request do
transaction? true
argument :user_params, :map, allow_nil?: false
run fn input, _ ->
# get org for users email
# if it doesn't exist, make it
org = get_or_create_org(...)
strategy = Info.strategy!(User, :magic_link)
Strategy.action(strategy, :request, input.arguments.user_params, tenant: org)
end
end
Then you can call that generic action from your controller.
Apologies for the delayed response. I didn’t see your reply earlier. My question is: Doesn’t it make more sense to set global? true on the users and token resources and then handle authentication separately from team creation?
It could. I’m not sure if I’d say it makes “more” sense necessarily though. I like having fewer invariants in my system. i.e if “all users always have an org” that is simpler than “users have an org except for a few minutes after they sign up and before they create their org”. You’d end up dealing with the fact that not all users have an org in a bunch of places.