This is half bug, half feature request, but I have decided that it fits better as a "bug" due to reasons listed below.
Describe the bug
The issue is described here, but I will summarize it below.
When using Appsync with a cognito user pool with email enabled, $ctx.identity.claims.email is missing in the resolver for a mutation if that mutation is called using the amplify-js library. The issue does NOT exist when called using the query editor in Appsync itself.
This causes problems when using a cognito user pool where users sign up by email address rather than specifying a custom username. Since the email address is the human-readable identifying factor in an account, it is desired that it be stored on the profile level. However, in this use case, the $ctx.identity.username field is identical to the $ctx.identity.sub field (the account's UUID), so to obtain a user's email address without obtaining the entire profile, it must be passed via claims.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
$context.identity.claims.email should be populated with the cognito user's email address
Cause of issue
The issue is caused by the passing of the Access Token as authorization, while as claims are supposed to be obtained from the ID Token. See here (pointed out by cy6581 in the above stack overflow thread).
Using the Access token for claims is not in line with the cognito user pool documentation, which is why I classify this as a "bug". See here:
The ID Token contains claims about the identity of the authenticated user such as name, email, and phone_number.
The Access Token grants access to authorized resources.
I had a similar issue with the mock API: the mock API passes the id token but the real API passes the access token. I argued the opposite: both should use the access token by default.
https://github.com/aws-amplify/amplify-cli/issues/3062
Either way, you're right that all of the methods (mock API, Appsync console, real API) should be consistent in their use of either the id token or the access token.
Hi @atlowell-smpl
What @dantasfiles points out is correct, the console uses the ID token and Amplify uses the acces token.
With code like this you can make Amplify use the ID token too:
const Amplify = require('aws-amplify').default;
Amplify.configure({
API: {
aws_appsync_graphqlEndpoint: 'https://xxxxxxxxxxxxxxxxxxxxxxxxxx.appsync-api.us-east-1.amazonaws.com/graphql',
aws_appsync_region: 'us-east-1',
aws_appsync_authenticationType: 'NONE',
graphql_headers: async () => ({
'Authorization': (await Auth.currentSession()).getIdToken().getJwtToken()
})
}
});
Amplify.API.graphql({
query: 'query { someType { someField } }',
}).then(console.log, console.warn);
I hope this helps.
@manueliglesias that is actually what we have been doing to get this to work, but it seems more like a workaround than a fix. Considering appsync has support for claims, and cognito claims expect the id token, I think the default token passed to appsync by amplify should be the id token. But as @dantasfiles also mentioned, there should at least be consistent use of tokens across services.
@manueliglesias
Worked for us :)
+1 for at least making this consistent. Wasted many hours diagnosing why same exact user query would work in app sync console but fail as unauthorized in actual client. The reason is console uses id token and Amplify client uses access token, which does NOT include custom attributes like the id token does. Above fix resolved for me (thanks!), but feels like a hack. If it is supposed to be access token for better security, then the app sync console should also use it, and custom attributes should be included in the access token so that we can write authorization rules against custom attributes.
+1 for always using id token (or at least to support both)