graphql-engine icon indicating copy to clipboard operation
graphql-engine copied to clipboard

Use session variables in GraphQL queries

Open macalinao opened this issue 6 years ago • 25 comments

Hi all, it would be great to inject my X-Hasura-User-Id into my GraphQL query so I can use it to filter through data. E.g, fetching the tasks assigned to my currently authenticated user. Are there any plans to build this?

macalinao avatar Aug 20 '19 23:08 macalinao

@macalinao Hasura has buit-in ACL which serves this exact purpose. You just need to pass the JWT token containing X-Hasura-User-Id and use it to filter the data. This link may help: https://docs.hasura.io/1.0/graphql/manual/auth/authorization/basics.html

victorferreira avatar Aug 21 '19 00:08 victorferreira

@macalinao I'm not sure how this can be done while adhering to the graphql spec. Any ideas?

@victorferreira I think what the op was suggesting is a case like this: Suppose you have a table/view called followers where everyone can read all rows. Now suppose you want to fetch your own followers you would do something like this:

query getFollowersOf($user_id: Int!) {
  followers(where: {user_id: {_eq: $user_id}}) {
    follower_id
  }
}

with variables being {"user_id": your_user_id}. However it would be nice if you can somehow specify that $user_id should come from a session variable than having to explicitly specify it.

0x777 avatar Aug 21 '19 08:08 0x777

@0x777 one idea is to inject all hasura env vars as optional query variables. I'm not sure what the security implications are, but it could look like this:

query getFollowersOf($x_hasura_user_id: String) {
  followers(where: {user_id: {_eq: $x_hasura_user_id}}) {
    follower_id
  }
}

These could get whitelisted and configured in the Hasura console

macalinao avatar Aug 23 '19 01:08 macalinao

Hey @macalinao, would love to hop on a short call to discuss possible implementations for this! If this works for you, could you please schedule a chat here?

dsandip avatar Aug 29 '19 12:08 dsandip

@dsandip Did you ever look into this issue?

@macalinao This is the way I do it in my current application. As long as the injection is done after validating the token, in my opinion this raises security by not allowing the caller to specify any invalid query variables.

tmansson avatar May 06 '20 06:05 tmansson

I'd be glad to hop on a call to discuss possible implementations for this @dsandip!

This would be extremely valuable for my project.

buzinas avatar Jun 12 '20 23:06 buzinas

This would be also useful if you want to update the auth user, e.g.: update_user_by_pk(pk_columns: {id: $userId}, _set: $changes) {...}

marcel-happyfloat avatar Aug 12 '20 08:08 marcel-happyfloat

Any update on this? This feature is really useful.

mdovn avatar Oct 05 '20 12:10 mdovn

Still waiting an update..

alfari16 avatar Jan 26 '21 11:01 alfari16

This would be amazing. Badly needed.

bluetoken-luke avatar Jan 27 '21 02:01 bluetoken-luke

I'd like to see this implement for security reasons as well. One scenario is that I want to add a record (mutation) - and enforce that the userId is set on the server side, not on the client side. This way, if any hacker ends up XSS the site, or breakpoint the minified code (somehow.... not sure how...but who knows).... They cannot create records on behalf of another user.

The only way I can imagine this done correctly would be to setup a server proxy that extracts the userId form the jwt token, and injects the userId into every query where $loggedInUsderId is defined, and forward this to the Hasura server - which is a bit of work.

tony-pond avatar Nov 03 '21 00:11 tony-pond

@tony-pond unless I'm not understanding well your point, you can already do this for mutations. When you define insert permissions, you remove the check from the "user_id" column (or your version of it) and define it as a preset based on the session variable.

insert permissions

Of course, this is still a great missing feature for queries, for example my user might be allowed to see content created by others, but I might want to create a page only with their content. I can't wait to see this feature implemented, so I can clean up a bit of code from the client.

emilioschepis avatar Nov 04 '21 07:11 emilioschepis

It's 2021, still waiting for this feature!

rohitnick avatar Nov 18 '21 16:11 rohitnick

It's 2021, still waiting for this feature!

Same!!

Kae7in avatar Nov 19 '21 19:11 Kae7in

Hi team, I'd like to raise the priority of this issue. It seems currently we are not able to perform such query without compromising security:

query getFollowerInfo($user_id: int = "") {
  user(where: {id: {_eq: $user_id}}) {
    id
    email
    userRelationshipsByFollowee {
      userByFollower {
        id
        email
      }
    }
  }
}

It's not recommendded to specify user_id in a query varilable, which introduces a risk where a malicious user will forge the request payload, set it as another user_id and read the data they shouldn't.

Instead, for user_id such variable, they should always come from a session variable (jwt token).

I am not sure how currently Hasura users come up with a workaround for this but I recommend we reprioritize this issue. Thanks!

p-null avatar Dec 04 '21 02:12 p-null

@p-null that should not be a big security concern, since the data readable by each user is still decided by the permissions on the table itself. For example, if I forge the request with another user's id, even though I'm able to execute the query I will see an empty list since your table will probably have a rule like (allow read if) table.user_id = X-Hasura-User-Id.

With that said, I'd really like to see this feature implemented for ease of use on the calling site, so I hope the Hasura team will deliver!

emilioschepis avatar Dec 04 '21 07:12 emilioschepis

Hi @emilioschepis, thanks for your comment. Please check out the query I just updated in my comment. That should be more intuitive.

For example, if I forge the request with another user's id, even though I'm able to execute the query I will see an empty list since your table will probably have a rule like (allow read if) table.user_id = X-Hasura-User-Id.

Yes, and that's exactly my point. If you define such rule for user table, you will get an empty list. But if you don't define such rule, there will be a security risk.

p-null avatar Dec 04 '21 08:12 p-null

Are any updates on this issue ?

adrianhdezm avatar Jul 19 '22 22:07 adrianhdezm

Hey, is there a workaround for this issues?

Artkoch avatar Dec 17 '22 14:12 Artkoch

👀

mbrimmer83 avatar Dec 25 '22 00:12 mbrimmer83

this is a useful feature

lwsbox avatar Mar 08 '23 08:03 lwsbox

I'm trying to find a way to retrieve the object of the logged-in user. Using JWT mode, frontend could unpack the JWT token returned from auth backend and use the user id (must be returned) to issue a query feeding the user id as a variable.

Where it gets tricky is when I want to return a common set of user fields to everyone, but for the logged-in user asking for his own object I want to return some extra fields, I guess that's another issue though.

Quick edit: https://hasura.io/docs/latest/auth/authorization/role-multiple-rules/ this guide instructs on using views for custom column selection.

nourspace avatar Mar 13 '23 18:03 nourspace

Any possibilities in 2024?

rayhantr avatar Feb 27 '24 11:02 rayhantr

Not sure if I fully grasp the problem, but I have a quite complex multi role app and was able to combat all permission logic with the methods outlined here: https://hasura.io/docs/latest/auth/authorization/permissions/common-roles-auth-examples/

Zerebokep avatar Feb 27 '24 16:02 Zerebokep

I think it's not so much the permission logic as the query logic. Imagine a situation like ...

  • I should have read access to all details in the user table
  • However, in one particular query I want to get details for the currently logged in user.

A simple example might be to display a user list like

You are: User C

User list:

  1. User A
  2. User B
  3. User C

So you could have a query like ....

query getUserDetails ($currentUser: uuid!) {
  me: user(where: {id: {_eq: $currentUser } }) {
    id
    name
  }

  user {
    id
    name
  }
}

query vars

{
  currentUser: { session: 'x-hasura-user-id' }
}

OR even include this in the query itself

AlgyTaylor avatar Nov 14 '25 10:11 AlgyTaylor