Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support multiple JWT secrets #2208

Closed
kkshyu opened this issue May 16, 2019 · 46 comments
Closed

support multiple JWT secrets #2208

kkshyu opened this issue May 16, 2019 · 46 comments
Labels
a/authn Issues related to "authentication" and capturing session claims from an API call k/ideas Discuss new ideas / pre-proposals / roadmap p/medium non-urgent issues/features that are candidates for being included in one of the upcoming sprints

Comments

@kkshyu
Copy link

kkshyu commented May 16, 2019

for admin secret, we can set in the environment variable: HASURA_GRAPHQL_ADMIN_SECRET and pass x-hasura-admin-secret in header to authorize the permission.

in my scenario, i have two applications sharing the same Hasura API server. Theses two applications have their own JWT secret (specifically different jwk_url). I don't want to duplicate the Hasura server to handle these two applications.

is it possible to set multiple JWT secrets into Hasura server?
or pass the jwt secret to Hasura in header like x-hasura-jwt-secret?
or specifically select the jwk_url in run-time?

@revskill10
Copy link

I made a proposal on these problems here: #1995

I think it relates to your issue here.

@kkshyu
Copy link
Author

kkshyu commented May 17, 2019

@revskill10 i've seen your proposal, it is related but i'm not sure whether the secret key management includes JWT secret or not.
because the format of JWT secret is {type, jwk_url, claims_format} and it is different from the other secret (admin-secret and secret key from your proposal are string)

if these two kind of formats can be merged, it will be great~

@kkshyu
Copy link
Author

kkshyu commented May 17, 2019

one more different part is that:
in your proposal, it focuses on grant access to clients to access Hasura console as admin
but my scenario is that I have multiple auth servers to grant access to clients to access Hasura API as user

@MeixnerTobias
Copy link

I am looking for the same solution.
We run a multi-tenant app with ideally one Hasura instance to serve all tenants.
Every tenant is using a different Cognito user pool as a result the current implementation of HASURA_GRAPHQL_JWT_SECRET does not cover this use-case.
Ideally, I can think of 2 solutions here

  1. We can assign a different jwt-secret based on a parameter in the header i.e. x-hasura-org-id
    which then routes the jwt-secret based on the x-hasura-org-id
    HASURA_GRAPHQL_JWT_SECRET: {
    tenant1: tenant1Url
    tenant2: tenant2Url
    }
    whereas tenant1 = x-hasura-org-id
  2. Decode the jwt and route to a standard iss provided by the token (Cognito always has the same URL format)

@shahidhk shahidhk changed the title multiple JWT secrets or pass HASURA_GRAPHQL_JWT_SECRET in header support multiple JWT secrets May 17, 2019
@shahidhk shahidhk added the k/ideas Discuss new ideas / pre-proposals / roadmap label May 17, 2019
@kkshyu
Copy link
Author

kkshyu commented May 18, 2019

I am looking for the same solution.
We run a multi-tenant app with ideally one Hasura instance to serve all tenants.
Every tenant is using a different Cognito user pool as a result the current implementation of HASURA_GRAPHQL_JWT_SECRET does not cover this use-case.
Ideally, I can think of 2 solutions here

  1. We can assign a different jwt-secret based on a parameter in the header i.e. x-hasura-org-id
    which then routes the jwt-secret based on the x-hasura-org-id
    HASURA_GRAPHQL_JWT_SECRET: {
    tenant1: tenant1Url
    tenant2: tenant2Url
    }
    whereas tenant1 = x-hasura-org-id
  2. Decode the jwt and route to a standard iss provided by the token (Cognito always has the same URL format)

Thanks for @MeixnerTobias explanations. These two methods are great!
My scenario is exactly the same as you. We also use multiple Cognito User Pools for the multi-tenant implementation and would like to share the same Hasura API server.
Currently, we can only write some scripts to launch docker container for Hasura after a new tenant registered. We don't like this method and would like to configure once and use for all.

How about sending the jwt-secret on header for each request?

{
    'x-hasura-jwt-secret': '{"type": "SHA256", ...}'
}

if we send it from the browser, is it unsecured?
i thought the jwk can be public

@MeixnerTobias
Copy link

Yes, the format of of jwks urls follow always the same format under .well-known which we can consider public, so I setting it in the http header can work too.

@cthurston
Copy link

cthurston commented Aug 6, 2019

My need case for this feature is for firebase. If you create a custom token through firebase it is signed with a different issuer (service account) and requires a different JWT_SECRET. Would be great if I could just add an array of acceptable secrets to try.

[
  {
    "type":"RS256",
    "jwk_url": "https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com",
    "audience": "<firebase-project-id>",
    "issuer": "https://securetoken.google.com/<firebase-project-id>"
  },
  {
    "type": "RS256",
    "jwk_url": "https://www.googleapis.com/service_accounts/v1/jwk/firebase-adminsdk-xxxxx%40<firebase-project-id>.iam.gserviceaccount.com",
    "audience": "<firebase-project-id>",
    "issuer": "firebase-adminsdk-xxxxx@<firebase-project-id>.iam.gserviceaccount.com",
  }
]

I did find a work around in the firebase docs where you can exchange a custom token for a secureToken using this endpoint: https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=[apiKey]
So this issue is not blocking my workflow at least.

@kesavkolla
Copy link

My usecase also very similar. I've single keycloak server where each tenant is one single realm. I want to use single hasura server for all tenants. For keycloak jwks url for each tenant/realm is different. I want an ability to inject the x-hasura-org-id in jwks url.

@smunozp
Copy link

smunozp commented Mar 30, 2020

Hello

There is any update in this? We have the same situation, we want to serve multitennant with the same server

@NorthJaw
Copy link

NorthJaw commented Jun 7, 2020

This can be done using the webhook mode instead of the jwt mode for authentication in Hasura: https://hasura.io/docs/1.0/graphql/manual/auth/authentication/webhook.html

The jwt token along with other headers are passed by Hasura to this webhook which will handle the authentication part and sends back just the session variables to Hasura to use.

A sample auth webhook for Firebase has been provided in the same docs page: https://github.com/hasura/graphql-engine/blob/master/community/boilerplates/auth-webhooks/nodejs-firebase/firebase/firebaseHandler.js

@Nomad-Go
Copy link
Contributor

Nomad-Go commented Jul 24, 2020

This can be done using the webhook mode instead of the jwt mode for authentication in Hasura: https://hasura.io/docs/1.0/graphql/manual/auth/authentication/webhook.html

The jwt token along with other headers are passed by Hasura to this webhook which will handle the authentication part and sends back just the session variables to Hasura to use.

A sample auth webhook for Firebase has been provided in the same docs page: https://github.com/hasura/graphql-engine/blob/master/community/boilerplates/auth-webhooks/nodejs-firebase/firebase/firebaseHandler.js

I would call this a workaround. No one wants that extra round trip. Every millisecond counts.

@osaxma
Copy link

osaxma commented Jan 16, 2021

@cthurston did you find a way around this? I'm having the exact problem (i.e., multiple firebase apps + tokens generated from the admin sdk).

@cthurston
Copy link

@osaxma Found this:
https://firebase.google.com/docs/auth/web/custom-auth

Then wrote this:

const axios = require('axios')

async function firebaseExchangeToken (token, apiKey) {
 return axios.request({
   url: `https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${apiKey}`,
   headers: {
     'Content-Type': 'application/json',
   },
   method: 'POST',
   data: {
     token,
     returnSecureToken: true,
   },
 })
}

module.exports = firebaseExchangeToken

Then I exchange my tokens to use the project id that I have registered with my hasura instance.

@robnewton
Copy link

Being able to have more than one JWT pub cert at a time would prove helpful for migrating from one identify system to another without disruption. Is there any chance what @cthurston suggested will be taken up by the Hasura team and implemented? I'd like to be able to plan accordingly one way or another.

@m4rcelofs
Copy link

Being able to have more than one JWT pub cert at a time would prove helpful for migrating from one identify system to another without disruption. Is there any chance what @cthurston suggested will be taken up by the Hasura team and implemented? I'd like to be able to plan accordingly one way or another.

That's exactly our current situation - we need to support both Firebase and Auth0 for a while. Having HASURA_GRAPHQL_JWT_SECRET accept an array of configs would certainly make our life easier - the only way out currently seems to use webhook auth? That's not exactly great.

@brunobraga95
Copy link

brunobraga95 commented Feb 4, 2021

+1 exact same situation here. I have multiple firebase projects (where each is a different issuer) and would like to keep only one hasura database.

@andrewbeckman
Copy link

andrewbeckman commented Mar 17, 2021

@rikinsk I noticed you labeled this a while ago. Any updates on this issue?

@keremgocen
Copy link

we need this as well:) My team wants to re-use the same Hasura API layer for 2 separate user pools, I guess for now we can make do with using separate groups within the same pool. It sounds logical to be able to authenticate Hasura from different auth sources and not deploy one for each.

@andrewbeckman
Copy link

@keremgocen in case it's helpful, I talked to Hasura support and confirmed that we have 2 options currently: 1) use webhooks or 2) create a second Hasura project pointed at the same database. Importantly, the second option requires both projects to be on Hasura GraphQL Engine version 2.0 or greater. I ended up creating a second project to avoid the downsides of webhooks. Let me know if you have any questions!

@keremgocen
Copy link

@andrewbeckman thanks that's helpful, I now just need to convince my team that it's not possible to create a single Cognito user pool as a proxy and link multiple pools to it from separate domains in order to use a single jwt_url.

I don't think webhooks will work for us. We don't have a lot of users yet and we will possibly never end up paying for the round-trip if we use Lambda for the webhook, but the fact that each request needs to make that round-trip does not feel right to me as an engineer, while we have a better, more convenient way.

@andrewbeckman
Copy link

@andrewbeckman thanks that's helpful, I now just need to convince my team that it's not possible to create a single Cognito user pool as a proxy and link multiple pools to it from separate domains in order to use a single jwt_url.

I don't think webhooks will work for us. We don't have a lot of users yet and we will possibly never end up paying for the round-trip if we use Lambda for the webhook, but the fact that each request needs to make that round-trip does not feel right to me as an engineer, while we have a better, more convenient way.

My thoughts exactly. Even though it creates a bit of a headache to manage keeping the two projects in sync, I'd rather have the headache than degrade the experience for users.

@josephnaber
Copy link

We are also seeking a way to integrate with multiple AWS cognito user pools. we would like to see HASURA_GRAPHQL_JWT_SECRET with an array of objects, rather than a single object

@vaishnavigvs vaishnavigvs added the a/authz Issues related to "authorization" and the policy engine after session claims are procesed label Jun 25, 2021
@andrewbeckman
Copy link

@vaishnavigvs @0x777 is there an ETA for this feature?

@tirumaraiselvan
Copy link
Contributor

Hey folks, is it okay to assume that the issuer will be different for each of the JWT secrets?

@coco98 coco98 added a/authn Issues related to "authentication" and capturing session claims from an API call p/medium non-urgent issues/features that are candidates for being included in one of the upcoming sprints and removed c/auth a/authz Issues related to "authorization" and the policy engine after session claims are procesed labels Oct 7, 2021
@solomon-b
Copy link
Contributor

@vaishnavigvs @0x777 is there an ETA for this feature?

The feature has been merged into HGE's main branch! Next, Infra needs to make a small update for Cloud support.

@ota42y
Copy link

ota42y commented Dec 28, 2021

Hello!
I find this feature very important and eagerly awaiting.

This feature merged master, but it has been reverted by this commit, and it doesn't seem to be in v2.1.1.
Did something go wrong?

@carterventures
Copy link

We have the same issue regarding the use of Keycloak and multiple realms to handle multi-tenant auth. Would be great to assign a tenant to the audience field and use that for selecting the appropriate key (and get audience validation in the process).

@cassus
Copy link

cassus commented Jan 4, 2022

I found a possible workaround in blog post about how we could combine JWKS URLs with a lambda function, and supposedly hasura supports multiple keys from a JWKS URLs. I haven't tried it yet.

https://adhikary.net/en/2021/09/27/hasura-jwks-jwt-w-multiple-auth0-tenants/

@solomon-b
Copy link
Contributor

Just a heads up to everyone. The feature revert is temporary and we are prioritizing a fix now. I'll update this issue when we are ready to merge it back in.

@vasily-suslov
Copy link

vasily-suslov commented Jan 21, 2022

Hello! Would it be possible to share an approximate ETA for reverting the revert (if there is one :)?

@cassus
Copy link

cassus commented Jan 22, 2022

As a workaround I'm successfully using multiple JWT secrets with a JWKS url.

Based on this article: https://adhikary.net/en/2021/09/27/hasura-jwks-jwt-w-multiple-auth0-tenants/

@Shalashtein
Copy link

This is indeed a much needed feature, I've got different apps for the customer side of things and the admin side, they use different Cognito User Pools, and I'd like to have just one Hasura instance to manage everything

@marsouin
Copy link

marsouin commented Mar 2, 2022

Hi! any news on this? I am also using multiple Cognito User Pools... ;) thanks!

@AThilenius
Copy link

AThilenius commented Mar 3, 2022

A note to anyone lurking in this issue like me: the JWKs URL workaround isn't a performance bottleneck; Hasura calls the JWK URL once on startup then caches the value. I don't know what it does if an attacker spams random kids though.

Unrelated... I'm immensely impressed with Hasura, so kudos and love to the devs! A few warts (this and that Remote Schema "ACLs" aren't particularly useful) but the core GQL functionality is fantastic ❤️

Edit: learned something new

@Nomad-Go
Copy link
Contributor

Nomad-Go commented Mar 3, 2022

I consider our company an initial Hasura user. I first ran the image on EC2 in early 2019. @coco98 personally helped me debug some scalability issues over a zoom call. I still need this feature as I have to go multi cloud soon and I need a different auth workflow for Hardware. Amazing product, would not be where we are today with out them.

@bkniffler
Copy link
Contributor

bkniffler commented Mar 10, 2022

Just in case anyone missed it, this feature was mentioned in last months email

Bildschirmfoto 2022-03-10 um 22 24 00

No mentions in their 2.4.0beta1 release though :(

@franzwilhelm
Copy link

@tirumaraiselvan @sordina any progress on this, as it's mentioned in the community call (https://youtu.be/8LRx3uVBzJg?t=1744), and is already available to cloud users. We are now considering doing a lot of workarounds to implement this on our own. Would be a shame if this feature is kept as a "pro" feature. What I imagine will happen is that people will start forking this repo and implement their own multiple jwt solution, so why not just release what you have to the world?

@franzwilhelm
Copy link

Just a heads up to everyone. The feature revert is temporary and we are prioritizing a fix now. I'll update this issue when we are ready to merge it back in.

@solomon-b what is the status on this?

@madmod
Copy link

madmod commented Apr 14, 2022

When is this going to be available in Hasura Cloud?

@tirumaraiselvan
Copy link
Contributor

Hey folks, this is available in Hasura Cloud (v2.3 onwards). The docs are available here: https://hasura.io/docs/latest/graphql/cloud/security/multiple-jwt-secrets/

If you are not on Cloud, there is a simple workaround:

  1. Create a new endpoint that combines the result of all your JWK providers.
  2. Use this endpoint as the jwk_url in a single JWT Secret configuration.

@cleivson
Copy link

Does that mean this feature is not going to be ported to the on premises version?

@dlebech
Copy link

dlebech commented Aug 25, 2022

We managed to make Hasura accept two types of Firebase tokens using the jwk_url suggestion. We use both Firebase session tokens (for service-side rendering) and client tokens (for calling the API from the browser) for authenticating with Hasura.

In order to make this work, we created a "dumb" Cloud Function that simply combines the results of the different Firebase JWT keys and returns them from a single endpoint. Then we can point jwk_url to this cloud function like @tirumaraiselvan suggests, and it works ok.

I have shared more instructions in this repository, I hope it helps someone: https://github.com/exitplanner/firebase-jwts

(Sidenote: It would be nice if the open source Hasura just supported multiple JWTs, but it seems clear by now that this is not going to happen, so if you have the infrastructure for it, the above is an almost-effortless version)

@stevehenderson
Copy link

When trying to handle JWT from both Auth0 and Firebase (or Cloud Identity), you may run into an error:

"Could not verify JWT: Multiple JWTs found"

The issue is that the Auth0 suggested JWT documentation is specified as a key, where Firebase suggested JWT is a json with jwt_url, audience, and issuer

I ended up solving this by using a key, type, and issuer for both:

[
    {
        "key": "-----BEGIN PUBLIC KEY----AUTH0PUBKEY-----END PUBLIC KEY-----",
        "type": "RS256",
        "issuer": "https://auth0-issuer-url-from-your-tenant.com/"
    },
    {
        "key": "-----BEGIN CERTIFICATE-----GOOGLE_FIREBASE_CERT-----END CERTIFICATE-----\n",
        "type": "RS256",
        "issuer": "https://securetoken.google.com/<your-project-id>"
    }
]

The first entry is the PUBLIC KEY Auth0 suggested JWT entry and corresponds to the official documentation PLUS an added "issuer" key that is the iss of your Auth0 tenant...if you don't know this, post one of your Auth0 JWT to jwt.io and look at iss

The second entry is a PUBLIC KEY copied from https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com. There are multiple public keys, so you need the kid You can see this by pasting a firebase JWT in jwt.io and looking at the decoded header. Then use that key from google.

It's important that both keys retain their placement of newline symbols \n

@RexGalicie
Copy link

@stevehenderson Hey mate
Did you manage to sort

When trying to handle JWT from both Auth0 and Firebase (or Cloud Identity), you may run into an error:

"Could not verify JWT: Multiple JWTs found"

The issue is that the Auth0 suggested JWT documentation is specified as a key, where Firebase suggested JWT is a json with jwt_url, audience, and issuer

I ended up solving this by using a key, type, and issuer for both:

[
    {
        "key": "-----BEGIN PUBLIC KEY----AUTH0PUBKEY-----END PUBLIC KEY-----",
        "type": "RS256",
        "issuer": "https://auth0-issuer-url-from-your-tenant.com/"
    },
    {
        "key": "-----BEGIN CERTIFICATE-----GOOGLE_FIREBASE_CERT-----END CERTIFICATE-----\n",
        "type": "RS256",
        "issuer": "https://securetoken.google.com/<your-project-id>"
    }
]

The first entry is the PUBLIC KEY Auth0 suggested JWT entry and corresponds to the official documentation PLUS an added "issuer" key that is the iss of your Auth0 tenant...if you don't know this, post one of your Auth0 JWT to jwt.io and look at iss

The second entry is a PUBLIC KEY copied from https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com. There are multiple public keys, so you need the kid You can see this by pasting a firebase JWT in jwt.io and looking at the decoded header. Then use that key from google.

It's important that both keys retain their placement of newline symbols \n

hey @stevehenderson
Did you manage that issue.
I faced same when use multiple issuers

@nosovk
Copy link

nosovk commented Dec 21, 2022

It would be nice to have that feature in community edition, it helps to test multiple environments. Extremely useful when you stick to Firebase auth.

@vymarkov
Copy link

It would be nice to have that feature in community edition, it helps to test multiple environments. Extremely useful when you stick to Firebase auth.

+1 extremely useful feature

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a/authn Issues related to "authentication" and capturing session claims from an API call k/ideas Discuss new ideas / pre-proposals / roadmap p/medium non-urgent issues/features that are candidates for being included in one of the upcoming sprints
Projects
None yet
Development

No branches or pull requests