In the world of web development, JSON Web Tokens (JWTs) have become a popular choice for securely...
Remove sensitive data from Amazon Cognito JWT
Amazon Cognito User Pools are capable of issuing identity tokens in the form of JSON Web Tokens (JWTs), which are widely recognised and commonly utilised in authentication and authorisation processes.
However, by default, a JWT issued by Cognito includes personal user data within its payload. If this JWT is not encrypted, the personal data contained within it is susceptible to unauthorised access and theft.
Here is an example JWT issues by Amazon Cognito:
{
"sub": "xxxxxxxx-bf5c-4e50-bxxf-xxxxxxxxxxxx",
"email_verified": true,
"iss": "https://cognito-idp.eu-west-1.amazonaws.com/eu-west-2_xxxxxxxxx",
"phone_number_verified": true,
"cognito:username": "xxxxxxxx-bf5c-4exx-bdbf-xxxxxxxxxxxx",
"origin_jti": "xxxxxxxx-d4f1-4a0d-846f-xxxxxxxxxxxx",
"aud": "xxxxe4u684i5f6xxxxxxxxxxxx",
"event_id": "xxxxxxxx-d08b-4008-b795-xxxxxxxxxxxx",
"token_use": "id",
"auth_time": 1723111111,
"name": "Mary Omondi",
"phone_number": "+447000000000",
"email": "mary.omondi@company.uk"
"exp": 1724000000
"iat": 1723111111,
"jti": "xxxxxxxx-1cxx-4ae1-b40d-xxxxxxxxxxxx"
}
The personal user data in this token is:
- name
- phone_number
If personal user data must remain in the JWT payload, it is advisable to encrypt the JWT to protect the information. However, if encryption is not an option or the data does not need to be in the JWT, alternative steps should be taken to safeguard the information by removing it from the payload.
Amazon Cognito has a trigger called Pre Token Generation. This is a Lambda trigger which is invoked just before Amazon Cognito generates tokens, giving you the opportunity to modify or add claims stored in the token.
Here is an example infrastructure-as-code to provision the trigger, using Serverless Inc.'s Serverless Framework
preTokenGeneration:
handler: path/to/file.handler
events:
- cognitoUserPool:
pool: UserPool
trigger: PreTokenGeneration
existing: true
And here is the lambda code:
def handler(event: Dict, context) -> Dict:
try:
event["response"]["claimsOverrideDetails"] = {
"claimsToSuppress": ["email", "phone_number", "name"],
}
return event
except Exception as e:
logging.error(str(e))
return event
When the token is created, it will not contain the user's name, email address or phone number.