Graphql-engine: Custom JWT tokens with Firebase

Created on 19 Apr 2020  路  1Comment  路  Source: hasura/graphql-engine

I have a bit unusual app.

  • New user fills a simple form with basic info
  • Collected info is sent to the backend for the signUp process
  • User is authenticated with token returned from the backend
  • Later they can decide to persist account by linking to some real provider

I am trying to approach this by utilizing the actions feature. That gives me a custom mutation that accepts collected info and the handler should run some queries/mutations back at Hasura to create all necessary structures.

Since the user is anonymous at that point, I don't have any auth for communicating with Hasura. I was thinking about using webhook auth, but it feels too much back & forth especially in this actions scenario. Another obvious option is using admin secret key, but that solves only part of the problem.

Firebase custom token

I've decided to use Firebase Auth here. I followed the blog post but that doesn't work here. I need to call auth.signInWithCustomToken on the client-side with a token obtained from the action run.

And that's where it gets problematic. A token created with Firebase Admin SDK and function createCustomToken looks like this.

  const token = await app.auth().createCustomToken(user.uid, {
    hasura: {
      'x-hasura-default-role': 'rocket',
      'x-hasura-allowed-roles': ['rocket'],
    },
  })

image

Obviously, that does work (error JWSInvalidSignature). You may notice the nested claims/hasura key. Unfortunately, that's done by createCustomToken and for some reason, it completely ignores claims stored on the user with setCustomUserClaims(). It's a weird inconsistency, but whatever.

So I did change claims_namespace_path in jwt_secret.

{
  "type":"RS256",
  "jwk_url":"https://www.googleapis.com/service_accounts/v1/jwk/[email protected]",
  "audience": "projectId",
  "issuer": "https://securetoken.google.com/projectId",
  "claims_namespace_path": "$.claims.hasura"
}

Unfortunately, that's not a root of a problem and error is the same. It seems that Hasura has a problem validating a signature of that token. It looks like that createCustomToken user a different signature than the token that's created on the client-side. But I am kinda lost in the whole JWK mechanics and how to find out what's actually used.

Most helpful comment

Alright, turns out I was using it all wrong thanks to @leoalves from Discord. Apologize for all the rumble here.

The createCustomToken is meant only for consumption on the client and signInWithCustomToken method. That creates a correct JWT token for connecting back to Hasura. No other scenario is possible.

As for connecting from the action handlers server-side, there doesn't seem to be a way to utilize Firebase here. It would be kinda nice if similar JWT could be created server-side to carry the information about Hasura role, but I haven't found any specification for that, especially regarding the key used to sign.

Instead the admin secret has to be used. It's not great to have such important value spread all over the place, but it's bearable I guess. And if I want to limit permissions (ie. to prevent code injection attacks), it's as easy as passing X-Hasura-Role header which will be respected.

>All comments

Alright, turns out I was using it all wrong thanks to @leoalves from Discord. Apologize for all the rumble here.

The createCustomToken is meant only for consumption on the client and signInWithCustomToken method. That creates a correct JWT token for connecting back to Hasura. No other scenario is possible.

As for connecting from the action handlers server-side, there doesn't seem to be a way to utilize Firebase here. It would be kinda nice if similar JWT could be created server-side to carry the information about Hasura role, but I haven't found any specification for that, especially regarding the key used to sign.

Instead the admin secret has to be used. It's not great to have such important value spread all over the place, but it's bearable I guess. And if I want to limit permissions (ie. to prevent code injection attacks), it's as easy as passing X-Hasura-Role header which will be respected.

Was this page helpful?
0 / 5 - 0 ratings