Multitenancy plug
Hi. I am trying to create a multitenant system eith AshAuthentication and AshGraphql. However, I could not get actor put in context, so the tenant too. In my case single actor can have access to multiple tenants.
Let's assume there is a function get_selected_tenant(conn) and the User resource has_many Tenants (resource) with a role. I want to call PlugHelpers.put_tenant if the actor has any role in their selected tenant. Currently I could not even get the actor from conn (using :load_from_bearer plug, before my plug which will set the tenant)
33 Replies
could you give a couple more details on how you are testing this and how you are trying to get the actor from the con in your plug
Sorry, just tried different graphql playground and I believe the default (
:simple
) one did not send the header, Now it correctly picked up the actor
Is there a way to store the permission info in JWT? I think it is not a good idea to query database on every request
I mean I just want to load one relation before generating the token, and I want that relation to be saved in JWTThat's something @jart might be able to answer once the sun is rising in NZ
There is a lot of additional complications if you go that route
you have to invalidate JWTs that contain incorrect information if something about a user's permissions change for example
Are you sure you want to deal w/ that headache?
I have already implemented that in TypeScript. and I was migrating my project to Ash.
I understand that I will need to support older structures in JWT payload in case it changes. I need to invalidate the old ones in case user gains permission in a mew org or change in the existing
I just think it would be more performant to have JWT contain the information every route and every action will need
Also. the frontend will have a source of permissions and orgs in JWT (both client and server side)
I though just preloading some relations to be included in the JWT will be a couple line change. I just don't know how to approach the problem to not change a big part of generated Ash Authentication code
AFAIK there are ways to add custom claims to the token, but lets chat w/ @James Harton and see what he says
I haven't looked at that specific code in a while
I wasn't trying to say we should stop you from doing that, ultimately you should be able to use the token how you want 🙂
I was just making sure you knew what you were getting into
There is definitely code to add custom claims to the token, but maybe we'll need to add a callback for you to introduce custom claims into tokens generated for users. It would be a pretty easy change for us to make.
Yeah. I like that about Ash a lot that it does not stop me doing anything, it just hase some idiomatic ways of handling stuff so you can expect some project to project or domain to domain consistency
Are you using LiveView? or is this just for an API?
Just for API
Gotcha. So there is one troublesome issue here which only affects LiveView, which is basically "transferring" these claims adds extra work
But for the other things its easy
I just wonder if I only change the get_token part to preload some relation (through a manually overriden module, nor some idiomatic way) would it be enough?
ah, nvm we have the same problem
So, after
load_from_bearer
I will look into some minor changes in Authentication lib, submit my work to #showcase and make a PR if you like what I made
you could add your own plug that looks at the tokens and then modifies the assigned user
thats the easiest way for your current desires
but having something in AA itself would make perfect sense
Sounds great!
just a config that says what else to preload into Token, before putting in JWT
It depends, not everyone has tokens being stored etc.
We want to configure a function of user -> extra claims
I saw something called extra claims. I will look into it. Maybe I will only need to submit a documentation update
GitHub
improvement: allow configuring extra claims for a user by zachdanie...
This is only half done. We still need to do one thing which is to modify the store_in_session helper to
take opts and store the claims in the session so that they can be added to the user in LV ha...
This is the first half of the work
But I don't have time to finish it up
Perhaps you can get it over the line 😅
I didn't dig a lot into LiveView to fully cover that part. I will work on it to support basic API routes. Probably will need a bit tuning in the end
But it is a very good starting point for me. thanks.
I think what we want to do is add some options to the GenerateTokenChange to allow extra claims to be specified in the changeset somehow. Maybe private arguments? Not sure.
In terms of validating it you can replace load_from_bearer with your own version that calls the verify token code in AA and then verifies your extra claims and puts them in the conn.
If you want specific relationships or calculations loaded at verify time you can augment the generated get_by_subject action.
🤔 thats an interesting way to do it. Did you see the (partial) approach in my PR that lets you configure it?
Not yet. I’ve added it to my list of things to look at this morning.
this is my only change from your PR that got it working. It was actually ready do be specified like this
I am guessing you wanted the extra claims to be inside Token.extra_data, which did not work :)))))
No need to have it in extra_data
So that should work for you but before we actually merge that PR we have to handle saving the extra claims in the session
I just don't understand why do we need a TokenResource for JWT's, how should get_token work if I chose
store_all_tokens false
So if I don't need to save it in extra_data, then they are only accessible when decoding JWT right?The entire JWT is stored in the database
if you're using
store_all_tokens? true
if not then correct they would only be available then
but also: our choice of using JWTs was not because we believe that storing permissions authoritatively in JWTs is a good idea for most use cases.And how should we get that from TokenResource? something like
:get_extra_claims
action on token?I always store all tokens, which allows for very important security related features like:
1. list active sessions
2. revoke all sessions (critical security feature)
3. (with your own custom logic added) detecting suspicious sessions
@Vahagn I'm a bit confused
The token gets handed to us from the client
We shouldn't need to get the claims from the token should we?
I thought I can get all the claims from token, to not make any DB calls
In that case I will need refresh tokens to revoke all sessions (will work with a delay, after JWT expires)
What I mean is that the token is in the header
You shouldn't need to extract the claims from the token resource
I think I will move along with just loading user memberships when I resolve the actor, it will be easier to manage in the future. I don't think I will need the extra_claims for now