Skip to content

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
  • email

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.