How do I protect certain fields with Slash GraphQL

HI All,

I am new here.
Last few days I spent a lot of time reading and watching videos about DGraph and Slash. I have a starter account setup on Slash and we are trying to get a POC rolling for a big project that we are developing.

So my setup is that the Frontend is going to be connected to the Slash Graphql directly and there are no other middle servers.

I am failing to understand a very basic question. Which is a very common usecase IMO, but I did not find anything on this, so I will ask here.

The high level feature is that we have users and each user will have a wallet with money in it and they can spend this money which is in their wallet to Subscribe to content creators (a diff kind of user).

My question is how should I even begin to setup the schema so that users are

  • A: not able to modify their own wallet balances by sending graphql queries
  • B: paying from their wallet when subscribing to content creators.

I have got the auth working through firebase, I looked at the Lambda resolvers and mutations, and I think that is the way to go, and I was trying to wrap my head around how to achieve this usecase.

What I am trying is:

type User {
  id: ID!
  wallet: Wallet @hasInverse(field: user)
}

type Wallet @auth(
  update: <ONLY_ADMINS> # I dont want users to update their own wallet without going through payment processing
){
  user: User!
  balance: Int 
}

type Mutation {
  addBalanceToWallet( input: AddBalanceInput!) : Boolean @lambda 
# Add the balance for the user's wallet somehow  (This is my main struggle, if I get a solution to do for this use case, I can reuse that solution everywhere)
}

If I do something like above, a few questions:

  1. would the lambda run as the same user as the @auth ? In other words, the query coming in from Frontend will have logged-in userā€™s token, would lambda be able to run another mutation that updates the Wallet fields where user doesnā€™t have permission to update?
  2. within lambda the request will have to go to a payment gateway but I think that should be straightforward right ?
  3. When taking money out from one account, I want to credit that to other userā€™s wallet
  4. how to write the schema so that users are not able to change their subscription to other users by themselves via gql requests (think like a ā€œfollowā€ feature on twitter, but a paid one).

Overall I am very happy with what have learned in last couple of weeks and I look forward to any help.

please help!

regards,
Elyn

Assuming users can only query their own wallet as wellā€¦ in the lambda, I would make a graphql query to read available funds and the wallet id, then using DQL subtract from the wallet and add to the other wallet. This can be done with upserts. You will also need to understand how the GraphQL schema is mapped to the DQL schema though. Reserve DQL for in lambda or very restrictive operations and not client side as DQL bypasses all auth rules.

Ok let me confirm if I got this right,
Within the lambda, I first query for wallet which will honor the auth rules so as to identify if it is the same user who is issuing the mutation from the frontend, and after that use DQL to upsert to bypass auth rules and hence update the field that the user does not have permission to update?

Is it possible to read the jwt or claims within the lambda?

1 Like

Hi @elyn_T.G , Welcome to Dgraph community!!
you can read jwt in lamda on slash now. Itā€™s added recently.

@gja , can you please provide some more context.

2 Likes

@gja Are there docs on this, or any complete example somewhere ?
it would help if there is an example implementation.

Donā€™t think the docs have been updated just yet.

However, you may do the following:

function myHandler({parents, authHeader}) {
  const headerName = authHeader.key;
  const headerValue = authHeader.value;
  const [algo, claimsBase64, signature] = headerValue.split(".")
  const claims = JSON.parse(atob(claimsBase64));
}
4 Likes

thanks!

How do you actually edit the jwt in the lamda before you run a mutation?

If you have a custom mutation, you want to block the regular mutation from the user, and only allow the custom mutation. In his case, he needs to block any update to his Wallet by changing the jwt to admin from the allowed addBalanceToWallet mutation.

Just reading the jwt in the lambda does not help, how do you change it to show it is a server-side call?

@jdgamble555
I am going to try it by tomorrow. I looked at the code, and the graphql call (within lambda) accepts an auth header.
My plan is to generate a new token within the Lambda with admin role.
I will post here how it goes.

Two Options to do this.

GraphQL only

  1. Donā€™t allow anonymous access to the addBalanceToWallet mutation. This will not allow any browsers to use this endpoint without an explicit api token (note: not the JWT token. itā€™s a different token with the DG-Auth Header)
  2. Call addBalanceToWallet from the lambda.

DQL

  1. Make 'addBalancetoWallet` a lambda
  2. Do some parsing of JWT and such, verify the user
  3. Use DQL to update the balance via dql.mutate

Tejas

Still not clear @gja on how to do it only with graphql inside lambda

Is it possible to get an example?

Donā€™t allow anonymous access to the addBalanceToWallet mutation. This will not allow any browsers to use this endpoint without an explicit api token (note: not the JWT token. itā€™s a different token with the DG-Auth Header)

How exactly to prevent anonymous access to a mutation?

Iā€™ll see if @anand or @verneleem can help you with an example / recipe here. But the answer to your second question, here are the docs https://dgraph.io/docs/slash-graphql/security/. Weā€™ve recently added a new tab called ā€˜Accessā€™ on the schema tab.

@gja
Ok, so if I turn off anonymous access to Write for a type from Permissions under Access Control, I can still call that mutation from the Lambda without passing any additional headers OR do I have to pass the token from the Lambda ?

If I do have to pass the token from within the lambda then that raises another question on how to Add it in the Lambda as a SECRET. (note that our code will be on github so we donā€™t want access/api keys to be sitting in the codebase)

I have some docs written for this locally and will create a PR for them today.


PR Link in review

2 Likes
const claims = JSON.parse(atob(claimsBase64));

// do something with claims here...

Will the JWT be verified in authHeader so if you strip out the claims, the signature has been verified?

Iā€™ll need to test it again, but I believe dgraph will reject the request if it gets a jwt with a bad signature, so it cannot be passed to lambda

1 Like