I am trying to implement AWS Cognito authentication in a web app (Angular, Node JS, Serverless) that uses hasura graphql APIs for CRUD operations and am following this article for reference. I have setup a user pool, created an app client, created a lambda function to add jwt_claims
and also added a pre token generation trigger for the lambda function exactly as mentioned in the article.
In the JS app I am using amazon-cognito-identity-js
library to authenticate my users as follows:
login(username: string, password: string): Promise<any> {
let _this = this;
let promise = new Promise((resolve, reject) => {
const authenticationData = {
Username: username,
Password: password,
};
const authenticationDetails = new AuthenticationDetails(authenticationData);
const poolData = {
UserPoolId: this.UserPoolId,
ClientId: this.ClientId,
};
const userPool = new CognitoUserPool(poolData);
const userData = {
Username: username,
Pool: userPool,
};
const cognitoUser = new CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function(result) {
const token = {
access_token: result.getAccessToken().getJwtToken(),
id_token: result.getIdToken().getJwtToken(),
refresh_token: result.getRefreshToken().getToken(),
};
_this.setCredentials(token, true);
_this.router.navigate(['dashboard']);
return of(result);
},
onFailure: function(err) {
return resolve(err);
},
newPasswordRequired: function(userAttributes, requiredAttributes) {
console.log(userAttributes);
console.log(requiredAttributes);
let newPassword = prompt('Please enter a new password: ', '') || '';
cognitoUser.completeNewPasswordChallenge(newPassword, {}, this);
}
});
});
return promise;
}
But When the above method is called and an authentication request is sent to cognito, the AWS lambda function errors out with the following output.
{"__type":"UserLambdaValidationException","message":"PreTokenGeneration failed with error exports is not defined in ES module scope."}
I have tried updating the lambda function from
exports.handler = (event, context, callback) => {
event.response = {
claimsOverrideDetails: {
claimsToAddOrOverride: {
"https://hasura.io/jwt/claims": JSON.stringify({
"x-hasura-user-id": event.request.userAttributes.sub,
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": ["user"],
}),
},
},
};
callback(null, event);
};
to
export const handler = (event, context, callback) => {
event.response = {
claimsOverrideDetails: {
claimsToAddOrOverride: {
"https://hasura.io/jwt/claims": JSON.stringify({
"x-hasura-user-id": event.request.userAttributes.sub,
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": ["user"],
}),
},
},
};
callback(null, event);
};
and cognito returns the access_token
, id_token
& refresh_token
as expected but seems like the claim data is not being added to the response for some reason as when I use the access_token I received from cognito after authentication in the Authorization header to access data via hasura graphql API, Hasura gives me the following error:
{
"errors": [
{
"message": "claims key: 'https://hasura.io/jwt/claims' not found",
"extensions": {
"path": "$",
"code": "jwt-invalid-claims"
}
}
]
}
I do not understand what I am doing wrong here. This has already taken more time than expected for me and hence I am looking out for some help. I would really appreciate any directions, suggestions or solutions to this.
(Update: Added More Information below)
Just to add some more hasura config information, I have also added the HASURA_GRAPHQL_JWT_SECRET
environment variable to hasura.
NOTE: I have replaced my actual user pool id from the code below for security purposes and replaced it with <user_pool_id>. In the congifguration on hasura end the jwk_url
contains the actual user pool id and I am also able to view the public key information when I visit the jwk_url
It looks like this:
{
"claims_format": "stringified_json",
"jwk_url": "https://cognito-idp.us-east-1.amazonaws.com/<user_pool_id>/.well-known/jwks.json",
"type": "RS256"
}