I am using an AWS Lambda function (Node.js 14.x) to call Cognito revokeToken
function to revoke a refresh token.
According to the official document, "revokeToken" will:
Revokes all of the access tokens generated by the specified refresh token. After the token is revoked, you can not use the revoked token to access Cognito authenticated APIs.
While I can successfully revoke the refresh token & associated access tokens, previously created access tokens can still be used to access API Gateway resources (with Cognito User Pool authentication enabled).
Why can I still authorise requests to API Gateway after using Cognito's RevokeToken
method to revoke access tokens?
CodePudding user response:
Your refresh token & access tokens created using that refresh token are being revoked however, API Gateway will not pick up on this & still allow access.
The reason for this is that API Gateway uses the identity token to authorize API calls, not the access token.
The documentation (very unclearly I must admit) mentions:
After the token is revoked, you can not use the revoked token to access Cognito authenticated APIs.
In this case, what is meant by the above is that the token is revoked for API actions that actually belong to Cognito, not services which use Cognito for authentication.
This is why you can still authenticate with API Gateway as it will internally use the not-revoked ID token.
To demonstrate this, the typical Cognito authentication result will look like this:
{
"AuthenticationResult":{
"AccessToken":"eyJraWQiOiJvZ2JFM2xXN0FWeEpPZjJWRU50MW9RNnRrY3ZOdVRJUUNTZkJpczlBWDFBPSIsImFsZyI6IlJTMjU2In0.eyJvcmlnaW5fanRpIjoiOThjM2QyNTgtYWU1NC00MzBiLWEwNjktYjg4OGZhNWQ5YzkwIiwic3ViIjoiZWE5MGQ2MjktMWI3NS00Y2ZiLThkOWQtZGRmZWJhZGZlZGYzIiwiZXZlbnRfaWQiOiJlODMxZWI1MS1mMjM3LTRhNDctYTM3YS1jNTk5MzBkNzFkZDgiLCJ0b2tlbl91c2UiOiJhY2Nlc3MiLCJzY29wZSI6ImF3cy5jb2duaXRvLnNpZ25pbi51c2VyLmFkbWluIiwiYXV0aF90aW1lIjoxNjM0NDcxOTA1jsda56dsf8sd7f7sdf9sdf6hdf8gfdf8dg89dfg8dfgIiwiZXhwIjoxNjM0NTU4MzA1LCJpYXQiOjE2MzQ0NzE5MDUsImp0aSI6IjNlMWQ1ZmJjLTUwNjMtNGZmNS1iZDIxLWFlZTk1ZjAyZTVjZCIsImNsaWVudF9pZCI6IjNiZHBsZzNxYmwyNWJhczM1aWRuOWU4YWdqIiwidXNlcm5hbWUiOiJxbDAzIn0.FU8fv7bXDFLhUku_A11bLiw2kCdLCIepZ0l4E5t8okC_KgABGE4G_VFZ5E34VYAokuy-npWQaP84PKksnPR-S17phEQ-CWyoL5OM7t5sqkJseikqrgxzMoAgnSn34RUY4FJDhmuM9F5ejNhaKp-uDhDnvYaWe8Qcuz1TfBlgLUwARE1eBMaxqusmPOyJpZOvKcaeiOfqduv_rnN36UjIRaOeeDkht54n0066H9vBYnE1kwkVLlLagCI7kF2agHV6Kkl-cTVZTZjqCYzhOuAba_ZhdedsLn9xrQcY14-qgxfYiBxc-m1CVSZ-ZUTlmRShFrG6aHZDYSlWP38bRgQD8g",
"ExpiresIn":86400,
"TokenType":"Bearer",
"RefreshToken":"eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.ODcmlgBQ4TWaUudE5edD5Y0Fx4LQQ6LnmNjfBBcfS14s4Q9MmiD9YQZZSyKTu9UoeR3SpQkGEVvkI6t7rhtyheMqwtJiCxmitCcKVm8RhLrjgEIq2wbBTegINEOKFQKpf3IHsolssm2UuebYpaXxxB68swMwDdBC-By51aTdAaZpJWGiviqZiCNnuzvuKNfhnrZVk482ctrBdu0AgGj-YxKsnVEQozXvCHiojnQE7YfJW048ctYUBgti0wpKvNI2_PbavT11W8cD0x093uQhARZtSBazv4mkqtbPpdv6GSzIE6PHETfKIxJIMaDzLJKAnbOHCEquHfYD1KouZO93Cw.IopkTsaLXan0zWOW.jWxoHQPORSNVHQKfypBQ23BWtq3hzwkDDasd65asd7a8sfg8sd8gsd78gs9gdf8asdc9asdfdsfFXw-hfLA1uhCIeZebRNcTzmVcR_Kd0g75tCzH8FJw4TXMPvQ9Qg9NcJI1JsA_DLC1B9m0hwPXtUib0Pz7k5Z2l7fwCUUfFfT4VNiPrhsuz35XlbNJc1fq1kfN88f5sGZ0UocYwl_CtPf-0FwMeGJkhyyKIWAguV0z5FsfaVWojDPcGkw5JqILUwYxKZUW3mSORI5tXrgVloLoF48xaFoXpK5T2xPHfSaUZMJBsFMK24MdDRgLIfy6XS-21upJsi30O6yyc96A1vYYpk-eD871WC9156AlB3BskCsmgPKRSjPaDQ6Dfuc_xDR4ZLYb5XSaFtEC8q5eGeq-N2DjS0eDQbsUyMFY8ddY7BVNWIv0X1_HRKz6Nilrveimmc4OfaQ3aTHj32VDkxJb5BZgylEgLtaO_HRnqjnPD1Ic-XlxH0oXLgVm2aN3SSdXuEr3BdtCoRtnGfAkLAlK686L-3Ryo2Xg1oR61gNJXBljeFQVeTeUSNuYBKyc_swv4pwBUW_Ff_iOiR_ddhMuzqattTEtGxyXDKIVls3tDoyerJsD5e-_igpKkw5cks9Il6XI1I0Mq2jnso7xFeZEBztt3qcXJ5w8OdV1Qsc79SnAIA8yF9K_8zwZvMfFU5ODJSkvY56lReBiHDImQfDiImkgShDSKu-4y8IP10Ba3jr55b7rebgk3fO9yV9JcZOx9C2JtAHKFaTVCYz2YRA3fChUXlRHcqJfc2cYYAx5wD9fJLR4FiVkgkapaNadYT0pc4LPnylyijtUXgxL7tpDG4i9yJiQ1hT3kHV4o9NZeXyPV-VDN2XWeCOEYhpXASnj7nyRpzH4wPmD9xa6N-4mDzqDVXel7c527eecN7ZfU3MPXJ2fHnTGTzDjPCCPG1Ur0LE-9CJMWMMxbFDV9RR6paKMU8fno-cgczR-HZsWIgGzXudYEyb03OVDmRgdKxW_oWL8yWx0KEMtibHH.5jIZnrXB7RFLn3LS1bJqGA",
"IdToken":"eyJraWQiOiJlZFRQVFU0cXNOdFQyOEd2TWlXakg1aUExZjZFOVwvekw3OEZLYzdWU1VoWT0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJlYTkwZDYyOS0xYjc1LTRjZmItOGQ5ZC1kZGZlYmFkZmVkZjMiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImlzcyI6Imh0dHBzOlwvXC9jb2dadfgsdf6asdf87sd6fsd8f89fjpITrZUOH58mhfm-ToGQznHcWxd5I48W-uHckz1F9dLP9YLZpI0e3BGPPPeEOf2lAIa0dSfzKhl-SZgSLRBd5qX066jx1_6SvnTMpDgRE9JZmFM_n3cI2_jGoShxUwr7NpGaUCo2r2LPXx9Rs-KqmU33mqTFD8F-CezracE9xuAuDiTNCwlBA9_LB1FPQhvzkkgSR5vouIyUYzg"
},
"..."
}
This contains 3 parts:
AccessToken
- used for checking access to Cognito authenticated APIsRefreshToken
- used for refreshing the access tokenIdToken
- a bearer JWT token generally used by services outside of CUP (Congito User Pools)
AccessToken
& RefreshToken
are being revoked but the JWT ID token (that API GW uses) is not being revoked.
This is confirmed by an AWS employee in this GitHub issue:
Because IdToken is represented as a JSON Web Key Token, it's signed with a secret or private/public key pairs, which means even if you revoke the IdToken, there is no way to revoke the distributed public key.
Others also have indicated this problem, here, here & here for example.
This is not really Amazon's fault as the nature of JWT tokens means that they are stateless. The authorizing server doesn't need any state stored to be able to validate the JWT token - the token itself is all that is needed to verify a token bearer's authorization.
This is perhaps highlighted a bit clearer in another section of the docs:
Revoked tokens will still be valid if they are verified using any JWT library that verifies the signature and expiration of the token.
Essentially, the bad news is that you've hit an Amazon Cognito limitation.
The slightly good news is that if your solutions permits, you can set the expiry of the ID token to a minimum of 1 hour for Cognito ID tokens. While the Cognito Quotas page mentions a minimum of 5 minutes for ID tokens, the console will actually throw a Cannot be greater than refresh token expiration.
error for 5 minutes. You will have to set it to the minimum refresh token expiration duration which currently sits at 60 minutes.
A better way to say this is how the documentation for [AdminUserGlobalSignOut][9]
has worded it:
Signs out users from all devices, as an administrator. It also invalidates all refresh tokens issued to a user. The user's current access and Id tokens remain valid until their expiry. Access and Id tokens expire one hour after they are issued.
The good news is that an update was given by an AWS Amplify contributor in February that they were working on it (the same "fix" would apply to API Gateway hopefully as a service external to Congito).
The "best" news is that it is possible to implement "real" token revocation yourself to prevent the ID tokens from still being used. You will need to have a custom API Gateway Lambda authoriser (also this guide may be useful) to validate the token with Cognito but then to also check the token to see if it's been revoked by e.g. checking a blocklist
DynamoDB table. Read more about the methodology in my answer here.
Until Amazon allows you to "revoke" the ID token too i.e. manages adding of ID tokens associated with the revoked refresh token to a block list on their side which other services can use for verification, you will have to implement this yourself.