Hello, and thank you for Hasura!
Version: v1.0.0-alpha31
TL;DR: There is now a known issue with AWS Cognito that prevents configuring Hasura with JWTs and Cognito User Pools. Possible short term workaround on the Hasura side would involve allowing Hasura to be configured to use a flat JWT token (no nested namespace).
I have been experimenting with running Hasura on AWS. I handle user management with Cognito User Pools. This process allows one to use a Lambda function to contribute JWT claims during the late stages of the login process. This JWT-claim-adding mechanism works fine in general, but it does not work when nested claims are involved. AWS support has just confirmed this finding. Details down below.
What follows below:
Editorial notes: I have edited each of the messages to remove greetings and the name of the AWS support person. I have also bolded the key statement from AWS regarding the known problem with Cognito.
I am working with Cognito User Pools and JWT. I have a simple Lambda to add claims (response.claimsOverrideDetails.claimsToAddOrOverride). The claims are generated fine by the Lambda.
It appears that Cognito will not accept nested JWT claims... that it requires all JWT claims to be strings, and not nested JSON objects. Is this a known limitation of the service?
For example, the handler below works (Cognito logins work). If I uncomment the nested claim, the Lambda runs OK, but Cognito cannot interpret the Lambda output.
exports.handler = (event, context, callback) => {
event.response = {
claimsOverrideDetails: {
claimsToAddOrOverride: {
testKey: "testValue",
// "https://hasura.io/jwt/claims": {
// "x-hasura-allowed-roles": ["editor","user", "mod"],
// "x-hasura-default-role": "admin",
// "x-hasura-user-id": "1234567890",
// "x-hasura-org-id": "123",
// "x-hasura-custom": "custom-value"
// }
}
// "claimsToSuppress": ["email"]
}
References:
Custom claims and scopes: https://auth0.com/docs/api-auth/tutorials/adoption/scope-custom-claims
OIDC spec: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
I understand that you are using Pre Token Generation Lambda Trigger[1] in Amazon Cognito to add new claims in the Identity token. I also understand that when the claims are added as a normal key:value pair with the value set as a string in the Lambda function, Cognito recognizes the token and allows the user to be signed in successfully. But when nested claims are added(i.e. the value in key:value pair is a JSON object), the Lambda function does execute successfully and it adds the claims in the token. However, Cognito does not recognize the token and authentication fails with "HTTP ERROR 405”.
I have replicated the setup in our labs and was able to reproduce the issue at my end. When the claims are added as the nested JSON Object, Cognito Authentication fails with an HTTP error “405”. It seems that currently, it is not possible to add the nested JSON Object in the identity token. However, for further clarification and confirmation about this behaviour, I have reached out to the Amazon Cognito Engineering Team. Please be assured that I will inform you as soon as I get any further updates around this from our team. Your patience is kindly anticipated.
[1]. Pre Token Generation Lambda Trigger: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html
Thanks @willmitchell for reporting this!
We implemented the nested claims because the OIDC spec[1] and Auth0 (another popular identity provider) [2] enforces collision-resistant identifiers for custom claims. Which means either prepending each of the custom claim "key" with some namespacing or nesting the custom claims. It was easier (and logical) for us to nest the custom claims under a namespace. As the JWT payload is a JSON document we did not think nesting would be a problem (with any other provider).
I'm not sure what can be the possible solutions to this. But we'll definitely try to come up with something.
@coco98 @0x777 @shahidhk thoughts?
--
My suggestion is to make the this configurable. Let the user set if they want to nest the claims or not. Maybe a value . for the claim namespace puts all the claims at root? Or it can be a different setting altogether.
@willmitchell if we support an extra flag/configuration which will allow you to string-encode the JSON object containing the hasura claims. Will that work for you?
Something like :
{
testKey: "testValue",
sub: 1234567890,
"https://hasura.io/jwt/claims": JSON.stringify({
"x-hasura-allowed-roles": ["editor","user", "mod"],
"x-hasura-default-role": "admin",
"x-hasura-user-id": "1234567890",
"x-hasura-org-id": "123",
"x-hasura-custom": "custom-value"
})
}
Thanks to both @ecthiender and @shahidhk for your thoughtful analysis of the issue.
@ecthiender, your observation that the current Hasura approach is compliant with OIDC spec is well taken.
I think that either proposed solution would be great!
One thing that I like about the flat namespace approach (@shahidhk) is that the claims remain directly accessible to the client. @ecthiender 's suggestion involving string-encoding is also very clever.
Thank you both for your help with this. Looking forward to publishing some results with Hasura on AWS.
@tirumaraiselvan This might be helpful for #1180
I need to use Cognito as my Auth system also. Plus 1 for priority on this.
@willmitchell Were there any follow up from the Cognito team?
@shahidhk yes, on 13 December, there was a followup, but it did not provide any sign of progress on the AWS Cognito side. Key paragraphs in the message pasted below:
I have received an update from the internal team. The team has let me know that unfortunately, currently, "claimsToAddOverride" can only have "string" : "string" key value pairs as described in the parameter list in the documentation[1].
Having said that, if the use case permits, you can consider giving the JSON as a string like "{\"key1\" : \"value1\"}" but then, you would have to handle this accordingly when parsing the claims to convert the string back to JSON.
+1 for this feature.
I am glad to see, that you have added it to milestone.
@willmitchell @romcok @Nomad-Go We have added support for stringified JSON in Hasura claims section with v1.0.0-alpha38. Can you try it out and let us know the feedback?
Here are the docs: https://docs.hasura.io/1.0/graphql/manual/auth/authentication/jwt.html#claims-format
Yes, IT WORKS! Thank you veeery much.
Thank you @shahidhk @ecthiender I am working on a proper test case. I appreciate it and I will keep you posted on progress!
Thanks! This works for setting custom claims for Firebase admin using go. It only supports string claims like AWS Cognito.
Most helpful comment
@willmitchell @romcok @Nomad-Go We have added support for stringified JSON in Hasura claims section with
v1.0.0-alpha38. Can you try it out and let us know the feedback?Here are the docs: https://docs.hasura.io/1.0/graphql/manual/auth/authentication/jwt.html#claims-format