Multitenancy and different Liveviews

So this might not be directly related to AshAuthentication, but somebody could help me figure out what's going. I'm still super new to Elixir and Phoenix so bear with me haha Continuing from the discussion above with multitenancy I have a live_view being rendered normally from the router, but this one also has a child live_view rendered with live_render like so:
<%= live_render(@socket, MyAppWeb.Messaging,
id: "messaging",
sticky: true
) %>
<div class="flex flex-grow flex-col">
<.live_component
module={MyAppWeb.Components.AppHeader}
id="header"
page_title={@page_title}
current_user={@current_user}
/>
<div class="p-2">
<p class="text-2xl">Component here</p>
</div>
</div>
<%= live_render(@socket, MyAppWeb.Messaging,
id: "messaging",
sticky: true
) %>
<div class="flex flex-grow flex-col">
<.live_component
module={MyAppWeb.Components.AppHeader}
id="header"
page_title={@page_title}
current_user={@current_user}
/>
<div class="p-2">
<p class="text-2xl">Component here</p>
</div>
</div>
When I log in, I automatically gets logged out. When I inspect the socket and session of the messaging live_view, on the second render, there aren't any users or tenant. In messaging.ex, I have the following: on_mount {MyAppWeb.UserAuth, :current_user} which points to the following:
def on_mount(:current_user, _params, _session, socket) do
{:cont, Phoenix.Component.assign_new(socket, :current_user, &get_tenant/1)}
end
def on_mount(:current_user, _params, _session, socket) do
{:cont, Phoenix.Component.assign_new(socket, :current_user, &get_tenant/1)}
end
Is there anything I'm missing? If I comment out the multitenancy part out, I don't encounter the isssue, same thing if I comment out the messaging live view
57 Replies
richaard0
richaard0OP•3y ago
The second time that mount is called for the messaging live view, I don't have any error log, it simply goes through the else block here: (it goes through the happy path correctly the first time around)
def on_mount(:ensure_authenticated, _params, _session, socket) do
if socket.assigns[:current_user] do
{:cont, socket}
else
socket =
socket
|> Phoenix.LiveView.put_flash(:error, "You must log in to access this page.")
|> Phoenix.LiveView.redirect(to: ~p"/login")

{:halt, socket}
end
end
def on_mount(:ensure_authenticated, _params, _session, socket) do
if socket.assigns[:current_user] do
{:cont, socket}
else
socket =
socket
|> Phoenix.LiveView.put_flash(:error, "You must log in to access this page.")
|> Phoenix.LiveView.redirect(to: ~p"/login")

{:halt, socket}
end
end
get_tenant is the one from Ash.PlugHelpers
ZachDaniel
ZachDaniel•3y ago
why would you assign the tenant to the current user?
richaard0
richaard0OP•3y ago
Sorry, I've just tried so many things to get it working lol. I changed it from :current_user to :tenant: but I get the same result
ZachDaniel
ZachDaniel•3y ago
🤔 get_tenant is meant to be called with a conn Try just not altering the current user assign? I'll have to look into it tomorrow if that doesn't work
richaard0
richaard0OP•3y ago
What do you mean? Simply not calling that on_mount(:current_user)? Doesn't change anything 😦
ZachDaniel
ZachDaniel•3y ago
Yeah, so I think the problem is that you may need to somehow thread the current_user assign through to the child liveview? Since it sounds like you're nesting liveviews which is not something I've done before. Looking a bit into live_render it appears that all of the session information from the parent live view should be available. However, it looks like the basic problem may be what ash_authentication_live_session does for you is not extended to child rendered liveviews So the basic issue is that ash_authentication_live_session takes the information from the session and ensures that a current_user assign exists. I think this is likely a question for the phoenix team, specifically "when you have a live_session with an on_mount responsible for setting current_user, how do you get that user into the child liveview", to which I think the answer is probably "figure it out". So in your child liveview you'll have the session, and you may need to add your custom on_mount hook to fetch the user again from the db. My read on this is to avoid nesting liveviews like that if you can 🙂
frankdugan3
frankdugan3•3y ago
LiveBeats is a demo app that uses a sticky live view to maintain an audio player while you navigate from page to page. You can just add the current user on_mount hook on the LV to pull it in automatically. 🙂 https://github.com/fly-apps/live_beats/blob/712204ca172ed6d36456c8ed4fd6aaba50099447/lib/live_beats_web/live/player_live.ex
GitHub
live_beats/player_live.ex at 712204ca172ed6d36456c8ed4fd6aaba500994...
Contribute to fly-apps/live_beats development by creating an account on GitHub.
frankdugan3
frankdugan3•3y ago
GitHub
live_beats/live.html.heex at 712204ca172ed6d36456c8ed4fd6aaba500994...
Contribute to fly-apps/live_beats development by creating an account on GitHub.
ZachDaniel
ZachDaniel•3y ago
I think you may need to add the ash authentication hook that way too. Can you add multiple on mount hooks? Try @on_mount AshAuthentication.Phoenix.LiveSession
richaard0
richaard0OP•3y ago
I've managed to make it work by simply passing the current user to the session of the child Liveview
<%= live_render(@socket, MyAppWeb.Messaging,
id: "messaging",
sticky: true,
session: %{"current_user" => @current_user},
) %>
<%= live_render(@socket, MyAppWeb.Messaging,
id: "messaging",
sticky: true,
session: %{"current_user" => @current_user},
) %>
ZachDaniel
ZachDaniel•3y ago
I think they advise against that They say to only put the user_id in the session Did you try this? @on_mount AshAuthentication.Phoenix.LiveSession in MyAppWeb.Messaging? Also given the example @frankdugan3 showed I get why you'd nest liveviews, its not an organizational thing, its the only way to actually do certain things Just not something I've done before
richaard0
richaard0OP•3y ago
Yes I just tried it but it didn't work. I know the amount of data being serialized is pretty big when I inspect the element in browser haha. I mean, I guess could pass the ID only and fetch it again on_mount or something
ZachDaniel
ZachDaniel•3y ago
what is in the session? like before you add current_user there? Ideally I want to have a story for other users doing this with ash_authentication Based on the fact that they should be extending the parent session into the child rendered liveview, there should be a "current_user" session with the user's id if there is, then @on_mount AshAuthentication.Phoenix.LiveSession ought to pick that up I wonder...could it just be an order of operations thing between your on_mount and the on_mount I just mentioned?
richaard0
richaard0OP•3y ago
This is what I get, so the user id is actually there it looks like under user
[debug] MOUNT MyAppWeb.Messaging
Parameters: %{}
Session: %{"_csrf_token" => "H0Cun4K00npheuTF43gukRXp", "user" => "user?id=f6c8440a-d147-4e94-8e0c-b641b0148629"}
[debug] MOUNT MyAppWeb.Messaging
Parameters: %{}
Session: %{"_csrf_token" => "H0Cun4K00npheuTF43gukRXp", "user" => "user?id=f6c8440a-d147-4e94-8e0c-b641b0148629"}
This is just before being redirected to the login page and being signed out
ZachDaniel
ZachDaniel•3y ago
oh how strange ah, okay. I see Is there a way to add session hooks in a liveview like @on_mount Module? i.e @session Session? Anyway: there is a workaround here
session: %{"current_user" => current_user.id}
session: %{"current_user" => current_user.id}
that coupled with the on_mount hook I gave you should do it okay sorry I might be being dumb
richaard0
richaard0OP•3y ago
Didn't even need the on_mount hook, it works just by passing the id actually
ZachDaniel
ZachDaniel•3y ago
This is very strange 😄 and what is the current_user assign in your LV? The actual user?
richaard0
richaard0OP•3y ago
I'm digging to see if I used one somewhere before going to bed last night that I don't remember 😂
ZachDaniel
ZachDaniel•3y ago
that wouldn't really make sense
def on_mount(:default, _params, session, socket) do
otp_app = otp_app_from_socket(socket)

resources =
otp_app
|> AshAuthentication.authenticated_resources()
|> Stream.map(&{to_string(Info.authentication_subject_name!(&1)), &1})
|> Map.new()

socket =
session
|> Enum.reduce(socket, fn {key, value}, socket ->
with {:ok, resource} <- Map.fetch(resources, key),
{:ok, user} <-
AshAuthentication.subject_to_user(value, resource, tenant: session["tenant"]),
{:ok, subject_name} <-
Info.authentication_subject_name(resource) do
assign(socket, String.to_existing_atom("current_#{subject_name}"), user)
else
_ -> socket
end
end)

{:cont, socket}
end

def on_mount(_, _params, _session, socket), do: {:cont, socket}
def on_mount(:default, _params, session, socket) do
otp_app = otp_app_from_socket(socket)

resources =
otp_app
|> AshAuthentication.authenticated_resources()
|> Stream.map(&{to_string(Info.authentication_subject_name!(&1)), &1})
|> Map.new()

socket =
session
|> Enum.reduce(socket, fn {key, value}, socket ->
with {:ok, resource} <- Map.fetch(resources, key),
{:ok, user} <-
AshAuthentication.subject_to_user(value, resource, tenant: session["tenant"]),
{:ok, subject_name} <-
Info.authentication_subject_name(resource) do
assign(socket, String.to_existing_atom("current_#{subject_name}"), user)
else
_ -> socket
end
end)

{:cont, socket}
end

def on_mount(_, _params, _session, socket), do: {:cont, socket}
So this is our on_mount hook, the one I'm having you add It goes through the resources and ought to turn just the "user" session into :current_user My suspicion here is that you don't need the session: %{...} option at all, just that you need to reorder your on_mount hooks i.e
on_mount AshAuthentication.Phoenix.Livesession
on_mount YourCurrentUserOnMount
on_mount AshAuthentication.Phoenix.Livesession
on_mount YourCurrentUserOnMount
richaard0
richaard0OP•3y ago
I'm not actually calling any of those on_mount hooks, they are commented out right now, which is what I find weird
ZachDaniel
ZachDaniel•3y ago
:thinkies: are you actually using the current_user anywhere in your child liveview?
richaard0
richaard0OP•3y ago
Ok so basically, as long as I have something in my current_user, it doesn't kick me out. I guess this is gonna be on me to fetch and validate what I need with on_mount AshAuthentication.Phoenix.Livesession or any other hook I might need to I just tried passing it a random string and it didn't kick me out. So I'll try to see what I need to do to get the info loaded into that child live_view and report back later today I guess, I don't wanna take up any more of your time
ZachDaniel
ZachDaniel•3y ago
no worries. With any luck, that live session hook is what you need 😄
richaard0
richaard0OP•3y ago
I'm sure it is being called, but on_mount AshAuthentication.Phoenix.LiveSession never changes the session nor the socket/assigns when I inspect them, I really need to pass "something" to session: %{"current_user" => ....} so I don't get kicked out. Whatever I pass to the session: %{"current_user" => ...} ends up in the assigns of the socket under that same key, but it doesn't get resolved to anything, it's just "there. Reading the docs for that on_mount hook, this part doesn't seem to be working in my case:

For example a session containing `{"user",
"user?id=aa6c179c-ee75-4d49-8796-528c2981b396"}` becomes an assign called
`current_user` with the loaded user as the value.

For example a session containing `{"user",
"user?id=aa6c179c-ee75-4d49-8796-528c2981b396"}` becomes an assign called
`current_user` with the loaded user as the value.
I'll try to give as many details as I can about my use case. Here's what my session and socket for the messaging looks like: This is basically a live_view that will always be present, living on the side of the screen besides the main app and its live_views
messaging: %{
"_csrf_token" => "H0Cun4K00npheuTF43gukRXp",
"current_user" => "f6c8440a-d147-4e94-8e0c-b641b0148629", # <-- This is added by me when passing the session with the current_user key.
"user" => "user?id=f6c8440a-d147-4e94-8e0c-b641b0148629" # <-- The user is already there though.
}
messaging socket: #Phoenix.LiveView.Socket<
id: "messaging",
endpoint: MyAppWeb.Endpoint,
view: MyAppWeb.Messaging,
parent_pid: nil,
root_pid: #PID<0.6599.0>,
router: MyAppWeb.Router,
assigns: %{
__changed__: %{current_user: true},
current_user: "f6c8440a-d147-4e94-8e0c-b641b0148629", # <-- The current_user ID is here in lieu of the current_user map
flash: %{},
live_action: nil
},
transport_pid: #PID<0.6589.0>,
...
>
messaging: %{
"_csrf_token" => "H0Cun4K00npheuTF43gukRXp",
"current_user" => "f6c8440a-d147-4e94-8e0c-b641b0148629", # <-- This is added by me when passing the session with the current_user key.
"user" => "user?id=f6c8440a-d147-4e94-8e0c-b641b0148629" # <-- The user is already there though.
}
messaging socket: #Phoenix.LiveView.Socket<
id: "messaging",
endpoint: MyAppWeb.Endpoint,
view: MyAppWeb.Messaging,
parent_pid: nil,
root_pid: #PID<0.6599.0>,
router: MyAppWeb.Router,
assigns: %{
__changed__: %{current_user: true},
current_user: "f6c8440a-d147-4e94-8e0c-b641b0148629", # <-- The current_user ID is here in lieu of the current_user map
flash: %{},
live_action: nil
},
transport_pid: #PID<0.6589.0>,
...
>
and here are the session and socket of my "main" live view
socket flight watch: #Phoenix.LiveView.Socket<
id: "phx-F0wC0JirhnkREjWF",
endpoint: MyAppWeb.Endpoint,
view: MyAppWeb.FlightWatch,
parent_pid: nil,
root_pid: #PID<0.6595.0>,
router: VigilWeb.Router,
assigns: %{
__changed__: %{current_user: true},
current_user: #MyApp.Accounts.User<
airline: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
id: "f6c8440a-d147-4e94-8e0c-b641b0148629",
inserted_at: ~U[2023-03-10 19:48:24.538262Z],
updated_at: ~U[2023-03-10 19:48:24.538262Z],
airline_id: "4c3fad6a-ddb8-4158-9e21-ee7c1de41a90",
aggregates: %{},
calculations: %{},
__order__: nil,
...
>,
flash: %{"info" => "Successfully logged in"},
live_action: nil
},
transport_pid: #PID<0.6589.0>,
...
>
session flight watch: %{
"_csrf_token" => "H0Cun4K00npheuTF43gukRXp",
"tenant" => [], # <-- I just noticed the empty array for the tenant
"user" => "user?id=f6c8440a-d147-4e94-8e0c-b641b0148629"
}
socket flight watch: #Phoenix.LiveView.Socket<
id: "phx-F0wC0JirhnkREjWF",
endpoint: MyAppWeb.Endpoint,
view: MyAppWeb.FlightWatch,
parent_pid: nil,
root_pid: #PID<0.6595.0>,
router: VigilWeb.Router,
assigns: %{
__changed__: %{current_user: true},
current_user: #MyApp.Accounts.User<
airline: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
id: "f6c8440a-d147-4e94-8e0c-b641b0148629",
inserted_at: ~U[2023-03-10 19:48:24.538262Z],
updated_at: ~U[2023-03-10 19:48:24.538262Z],
airline_id: "4c3fad6a-ddb8-4158-9e21-ee7c1de41a90",
aggregates: %{},
calculations: %{},
__order__: nil,
...
>,
flash: %{"info" => "Successfully logged in"},
live_action: nil
},
transport_pid: #PID<0.6589.0>,
...
>
session flight watch: %{
"_csrf_token" => "H0Cun4K00npheuTF43gukRXp",
"tenant" => [], # <-- I just noticed the empty array for the tenant
"user" => "user?id=f6c8440a-d147-4e94-8e0c-b641b0148629"
}
And finally, in my layout:
<div class="w-screen h-full flex">
<%= live_render(@socket, MyAppWeb.Messaging, <-- messaging live_view here
id: "messaging",
sticky: true,
session: %{"current_user" => @current_user.id},
container: {:div, class: "w-96 border-r-2 border-info h-full p-2"}
) %>
<div class="flex flex-grow flex-col">
<.live_component
module={MyAppWeb.Components.AppHeader}
id="header"
page_title={@page_title}
current_user={@current_user}
/>
<%= @inner_content %> <-- main live_view from the router will be here
</div>
</div>
<div class="w-screen h-full flex">
<%= live_render(@socket, MyAppWeb.Messaging, <-- messaging live_view here
id: "messaging",
sticky: true,
session: %{"current_user" => @current_user.id},
container: {:div, class: "w-96 border-r-2 border-info h-full p-2"}
) %>
<div class="flex flex-grow flex-col">
<.live_component
module={MyAppWeb.Components.AppHeader}
id="header"
page_title={@page_title}
current_user={@current_user}
/>
<%= @inner_content %> <-- main live_view from the router will be here
</div>
</div>
ZachDaniel
ZachDaniel•3y ago
So lets try somthing here can you define a new module with the following contents:
defmodule Something do
import Phoenix.Component, only: [assign: 3]
import AshAuthentication.Phoenix.Components.Helpers
alias AshAuthentication.{Info, Phoenix.LiveSession}
alias Phoenix.LiveView.Socket

@doc """
Inspects the incoming session for any subject_name -> subject values and loads
them into the socket's assigns.

For example a session containing `{"user",
"user?id=aa6c179c-ee75-4d49-8796-528c2981b396"}` becomes an assign called
`current_user` with the loaded user as the value.
"""
@spec on_mount(
atom | {:set_otp_app, atom},
%{required(String.t()) => any},
%{required(String.t()) => any},
Socket.t()
) ::
{:cont | :halt, Socket.t()}
def on_mount({:set_otp_app, otp_app}, _params, _, socket) do
{:cont, assign(socket, :otp_app, otp_app)}
end

def on_mount(:default, _params, session, socket) do
otp_app = otp_app_from_socket(socket)

resources =
otp_app
|> AshAuthentication.authenticated_resources()
|> Stream.map(&{to_string(Info.authentication_subject_name!(&1)), &1})
|> Map.new()

socket =
session
|> Enum.reduce(socket, fn {key, value}, socket ->
with {:ok, resource} <- Map.fetch(resources, key),
{:ok, user} <-
AshAuthentication.subject_to_user(value, resource, tenant: session["tenant"]),
{:ok, subject_name} <-
Info.authentication_subject_name(resource) do
assign(socket, String.to_existing_atom("current_#{subject_name}"), user)
else
_ -> socket
end
end)

{:cont, socket}
end

def on_mount(_, _params, _session, socket), do: {:cont, socket}
end
defmodule Something do
import Phoenix.Component, only: [assign: 3]
import AshAuthentication.Phoenix.Components.Helpers
alias AshAuthentication.{Info, Phoenix.LiveSession}
alias Phoenix.LiveView.Socket

@doc """
Inspects the incoming session for any subject_name -> subject values and loads
them into the socket's assigns.

For example a session containing `{"user",
"user?id=aa6c179c-ee75-4d49-8796-528c2981b396"}` becomes an assign called
`current_user` with the loaded user as the value.
"""
@spec on_mount(
atom | {:set_otp_app, atom},
%{required(String.t()) => any},
%{required(String.t()) => any},
Socket.t()
) ::
{:cont | :halt, Socket.t()}
def on_mount({:set_otp_app, otp_app}, _params, _, socket) do
{:cont, assign(socket, :otp_app, otp_app)}
end

def on_mount(:default, _params, session, socket) do
otp_app = otp_app_from_socket(socket)

resources =
otp_app
|> AshAuthentication.authenticated_resources()
|> Stream.map(&{to_string(Info.authentication_subject_name!(&1)), &1})
|> Map.new()

socket =
session
|> Enum.reduce(socket, fn {key, value}, socket ->
with {:ok, resource} <- Map.fetch(resources, key),
{:ok, user} <-
AshAuthentication.subject_to_user(value, resource, tenant: session["tenant"]),
{:ok, subject_name} <-
Info.authentication_subject_name(resource) do
assign(socket, String.to_existing_atom("current_#{subject_name}"), user)
else
_ -> socket
end
end)

{:cont, socket}
end

def on_mount(_, _params, _session, socket), do: {:cont, socket}
end
And use that instead of the live session I gave you Thats just a copy of the imports/aliases and the body of the on_mount hook that we are using Then you can poke around and see where things are going wrong (IO.inspect everything 😆 )
richaard0
richaard0OP•3y ago
Sure thing, I'll investigate 🥸 😂
ZachDaniel
ZachDaniel•3y ago
🙂 Thanks for being such a good sport 😆
richaard0
richaard0OP•3y ago
looks like
AshAuthentication.subject_to_user(value, resource, tenant: session["tenant"])
AshAuthentication.subject_to_user(value, resource, tenant: session["tenant"])
returns an error
subject_to_user: {:error,
%Ash.Error.Query.NotFound{
primary_key: nil,
resource: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}}
subject_to_user: {:error,
%Ash.Error.Query.NotFound{
primary_key: nil,
resource: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}}
so the with statement fails In the session of my "main" liveview above, the tenant key was an empty array, could it be related?
ZachDaniel
ZachDaniel•3y ago
Yeah, that sounds like it may be the primary issue. what happens if you hard code a good tenant instead of session["tenant"]? Actually, you should probably be getting a different error there? Remind me: your user resource is multitenant, right?
richaard0
richaard0OP•3y ago
yes
ZachDaniel
ZachDaniel•3y ago
what is value there?
richaard0
richaard0OP•3y ago
key: "current_user"
value: "f6c8440a-d147-4e94-8e0c-b641b0148629"
key: "user"
value: "user?id=f6c8440a-d147-4e94-8e0c-b641b0148629"
key: "current_user"
value: "f6c8440a-d147-4e94-8e0c-b641b0148629"
key: "user"
value: "user?id=f6c8440a-d147-4e94-8e0c-b641b0148629"
ZachDaniel
ZachDaniel•3y ago
Okay, try replacing session["tenant"] with "a_literal_tenant_string" (the one this user is in) I'm not sure how tenant is ending up as an empty list though, that sounds pretty strange Is there anything you're doing to set the tenant in the session that way? We do this for the session:
|> Map.put("tenant", Ash.PlugHelpers.get_tenant(conn))
|> Map.put("tenant", Ash.PlugHelpers.get_tenant(conn))
So we just pull the tenant off of what is set for Ash.PlugHelpers.set_tenant
richaard0
richaard0OP•3y ago
I am using plug(:set_tenant) in my browser pipeline before plug(:load_from_session)
ZachDaniel
ZachDaniel•3y ago
Thats your own plug though right? Can I see it?
richaard0
richaard0OP•3y ago
No, that's from Ash.PlugHelpers , from this discussion here: https://discord.com/channels/711271361523351632/711271361523351636/1083898958566395986 But reading that again, I think I might have misread what you and jart meant, I was pretty tired 😅
ZachDaniel
ZachDaniel•3y ago
Ah, okay interesting. Not sure exactly what it would do if used that way 😆 Actually I can see exactly what it would do haha it would set the tenant to []
richaard0
richaard0OP•3y ago
Yeah, it does that quite well 😄
ZachDaniel
ZachDaniel•3y ago
How are you doing multitenancy? subdomains? paths?
richaard0
richaard0OP•3y ago
No not even that, the way I saw it, the user would be registered by either me or an admin of their org, and when logged in, their airline_id on their profile would serve as differentiator
ZachDaniel
ZachDaniel•3y ago
🤔 interesting So does your user resource have global? true in its multitenancy config?
richaard0
richaard0OP•3y ago
No, does it need it in that case?
ZachDaniel
ZachDaniel•3y ago
well, how can we know what tenant to find the user in if the user itself is the arbiter of what the tenant is? its a chicken and egg issue
richaard0
richaard0OP•3y ago
ah I see I guess I could have them enter their org's identifier while logging in
ZachDaniel
ZachDaniel•3y ago
and you're using attribute multitenancy?
richaard0
richaard0OP•3y ago
yes
ZachDaniel
ZachDaniel•3y ago
You don't necessarily need to have them enter it, but it sounds like what you actually want is something slightly different 1. if you add global? true to your resource, you will be allowed to query it with or without a tenant So then you don't need to give ash_authentication a tenant 2. then, add a plug after AshAuthentication's load_resource plug, that does something like Ash.PlugHelpers.set_tenant(conn, user.airline_id)
richaard0
richaard0OP•3y ago
Yeah that makes sense
ZachDaniel
ZachDaniel•3y ago
Then you can get the tenant out of the session in your liveviews and use that when making queries
richaard0
richaard0OP•3y ago
And this one resource would be the only one that needs to be global, since I'll have my tenant when making subsequent queries
ZachDaniel
ZachDaniel•3y ago
exactly
richaard0
richaard0OP•3y ago
Alright, sorry for all that trouble when it was a code 18 on my part 😄
ZachDaniel
ZachDaniel•3y ago
haha, not at all its really important for the core team to get hands on experience with the things that give users trouble and there wasn't really any guide to help you figure this kind of thing out (sounds like an excellent blog post/docs contribution.... 😆 )
richaard0
richaard0OP•3y ago
I saw the global? attribute for multitenancy but I didn't make the link at all in my mind that I would require that for my use case Thanks again for your help, I've learned quite a bit digging around and talking to you 🙂
ZachDaniel
ZachDaniel•3y ago
Happy to help! Let us know if you have any other questions 😄
richaard0
richaard0OP•3y ago
I don't mean to bump this post, but this is semi-related: Am I wrong by saying that set_tenant and get_tenant only work on conns and not on sockets? Meaning I won't be able to access the tenant and actors while manipulating the socket? (I'm assuming the same is true for get_actor and set_actor)
ZachDaniel
ZachDaniel•3y ago
Correct, it only works for the conn. However, ash_authentication_live_session sets the tenant to the session as well Something you could also do manually, i.e with an on_mount hook looking for the tenant assign and setting it into the session

Did you find this page helpful?