为什么我在使用 Cognito 的 RevokeToken 后仍然可以授权对 API 网关的请求?

Why can I still authorise requests to API Gateway after using Cognito's RevokeToken?

我正在使用 AWS Lambda 函数 (Node.js 14.x) 调用 Cognito revokeToken 函数来撤销刷新令牌。

根据 official document,“revokeToken”将:

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.

虽然我可以成功撤销刷新令牌和关联的访问令牌,但之前创建的访问令牌仍可用于访问 API 网关资源(启用 Cognito 用户池身份验证)。

为什么在使用 Cognito 的 RevokeToken 方法撤销访问令牌后我仍然可以授权对 API 网关的请求?

您的刷新令牌和使用该刷新令牌创建的访问令牌正在被撤销,但是,API 网关将不会接收此信息并且仍然允许访问。

原因是API Gateway uses the identity token to authorize API calls,而不是访问令牌

文档(我必须承认非常不清楚)提到:

After the token is revoked, you can not use the revoked token to access Cognito authenticated APIs.

在这种情况下,上面的意思是 API actions that actually belong to Cognito 的令牌被撤销,而不是 使用 Cognito 进行身份验证的服务。

这就是您仍然可以使用 API 网关进行身份验证的原因,因为它将在内部使用未撤销的 ID 令牌。


为了演示这一点,典型的 Cognito 身份验证结果如下所示:

{
   "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"
   },
   "..."
}

这包含 3 个部分:

  1. AccessToken - 用于检查对 Cognito 身份验证的访问 APIs
  2. RefreshToken - 用于刷新访问令牌
  3. IdToken - 通常由 CUP(Congito 用户池)以外的服务使用的不记名 JWT 令牌

AccessToken & RefreshToken 正在被撤销,但 JWT ID 令牌(API GW 使用)没有被撤销。

AWS 员工在此 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.

其他人也指出了这个问题,例如here, here & here

这并不是亚马逊的错,因为 JWT 令牌的性质意味着它们是无状态的。授权服务器不需要存储任何状态来验证 JWT 令牌 - 令牌本身就是验证令牌持有者授权所需的全部。

这可能在 docs 的另一部分中突出显示得更清楚一些:

Revoked tokens will still be valid if they are verified using any JWT library that verifies the signature and expiration of the token.


从本质上讲,坏消息是您遇到了 Amazon Cognito 限制。

稍微好一点的是,如果您的解决方案允许,您可以将 ID 令牌的到期时间设置为 minimum of 1 hour for Cognito ID tokens. While the Cognito Quotas page 提及 ID 令牌至少 5 分钟,控制台实际上会抛出一个 Cannot be greater than refresh token expiration.错误5分钟。您必须将其设置为目前为 60 分钟的最小刷新令牌过期时间。

更好的说法是 [AdminUserGlobalSignOut][9] 的文档如何措辞:

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.

好消息是 AWS Amplify 贡献者在 2 月份提供了更新,他们是 working on it(相同的“修复”将适用于 API 网关,希望作为外部服务Congito).

“最好”的消息是,可以自己实施“真正的”令牌撤销,以防止 ID 令牌仍在使用。您将需要自定义 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.

直到 Amazon 允许您也“撤销”ID 令牌,即管理将与撤销的刷新令牌相关联的 ID 令牌添加到其他服务可用于验证的阻止列表中,您将必须实施此操作你自己。