验证 JSON Web 令牌 - Amazon Cognito
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

验证 JSON Web 令牌

JSON Web 令牌(JWT)可以轻松解码、读取和修改。修改过的访问令牌会带来权限提升的风险。修改过的 ID 令牌会带来冒充身份的风险。您的应用程序信任您的用户池作为令牌颁发者,但如果用户拦截了传输中的令牌,该怎么办? 您必须确保应用程序收到的令牌与 Amazon Cognito 发放的令牌一致。

Amazon Cognito 发放令牌,这些令牌使用 OpenID Connect(OIDC)规范的某些完整性和保密性特征。用户池令牌通过到期时间、颁发者和数字签名等对象来指示令牌的有效性。签名(. 分隔的 JWT 的第三个也是最后一个分段)是令牌验证的关键组件。恶意用户会修改令牌,但如果您的应用程序检索公钥并比较签名,则该令牌将不匹配。处理来自 OIDC 身份验证的 JWT 的任何应用程序都必须在每次登录时执行此验证操作。

在此页上,我们为 JWT 的验证提出了一些一般性和具体的建议。在应用程序开发过程中,开发人员需要使用各种编程语言和平台。由于 Amazon Cognito 实施的 OIDC 足够接近公共规范,因此选择开发人员环境中任何声誉良好的 JWT 库都可以满足您的验证要求。

这些步骤描述了验证用户池 JSON Web 令牌(JWT)的过程。

先决条件

您的库、开发工具包或软件框架可能已处理本部分中的任务。AmazonSDK 提供在应用程序中处理和管理 Amazon Cognito 用户群体令牌的工具。Amazon Amplify 包括检索和刷新 Amazon Cognito 令牌的功能。

有关更多信息,请参阅以下页面。

许多库可用于解码和验证 JSON Web 令牌 (JWT)。如果您要手动处理用于服务器端 API 处理的令牌,或者您使用的是其他编程语言,则这些库可以为您提供帮助。请参阅用于处理 JWT 令牌库的 OpenID Foundation 列表

使用 aws-jwt-verify 验证令牌

在 Node.js 应用程序中,Amazon 建议使用 aws-jwt-verify 库来验证用户传递给您的应用程序的令牌中的参数。使用 aws-jwt-verify,您可以使用要为一个或多个用户群体验证的声明值填充 CognitoJwtVerifier。它可以检查的一些值包括以下内容。

有关更多信息和您可以在 Node.js 应用程序或 Amazon Lambda 授权方中使用的示例代码,请参阅 GitHub 上的 aws-jwt-verify

了解和检查令牌

在将令牌检查与应用程序集成之前,请考虑 Amazon Cognito 如何组装 JWT。从用户群体中检索示例令牌。解码并详细检查它们,以了解它们的特征,并确定要验证的内容和时间。例如,您可能希望检查一个场景中的组成员资格,而在另一个场景中,您可能想要检查范围。

以下部分描述在准备应用程序时手动检查 Amazon Cognito JWT 的过程。

确认 JWT 的结构

JSON Web 令牌 (JWT) 包括三个部分,各部分之间有一个 .(圆点)分隔符。

标题

Amazon Cognito 用来对令牌进行签名的密钥 ID kid 和 RSA 算法 alg。Amazon Cognito 使用 alg (RS256) 对令牌进行签名。kid 是对您的用户池持有的 2048 位 RSA 私有签名密钥的截断引用。

有效负载

令牌声明。在 ID 令牌中,声明包括用户属性和有关用户群体 iss 和应用程序客户端 aud 的信息。在访问令牌中,有效负载包括范围、组成员资格、用户群体身份 (iss) 和应用程序客户端 (client_id)。

签名

签名不是像标头和有效负载那样的可解码 base64。它是一个 RSA256 标识符,派生自签名密钥和参数(您可以在 JWKS URI 上观察到)。

标头和有效负载是以 base64 编码的 JSON。您可以通过解码为起始字符 eyJ 的开头字符 { 来识别它们。如果用户向您的应用程序提供了以 base64 编码的 JWT,但其格式不是 [JSON Header].[JSON Payload].[Signature],则它不是有效的 Amazon Cognito 令牌,您可以将其丢弃。

验证 JWT

JWT 签名是标头和负载的哈希组合。Amazon Cognito 为每个用户池生成两对 RSA 加密密钥。一个私有密钥对访问令牌进行签名,另一个私有密钥对 ID 令牌进行签名。

验证 JWT 令牌的签名
  1. 解码 ID 令牌。

    OpenID Foundation 还维护用于处理 JWT 令牌的库列表

    您也可以使用 Amazon Lambda 解码用户群体 JWT。有关更多信息,请参阅使用 Amazon Lambda 解码并验证 Amazon Cognito JWT 令牌

  2. 将本地密钥 ID (kid) 与公有 kid 进行比较。

    1. 下载并存储适用于用户池的对应的公有 JSON Web Key(JWK)。它可作为 JSON Web Key Set (JWKS) 的一部分提供。您可以通过为您的环境构建以下 jwks_uri URL 来找到它:

      https://cognito-idp.<Region>.amazonaws.com/<userPoolId>/.well-known/jwks.json

      有关更多 JWK 和 JWK 集的更多信息,请参阅 JSON Web Key (JWK)

      注意

      Amazon Cognito 可能会轮换用户群体中的签名密钥。最佳做法是使用 kid 作为缓存密钥在应用程序中缓存公有密钥,并定期刷新缓存。将您的应用程序收到的令牌中的 kid 与缓存进行比较。

      如果您收到的令牌的颁发者是正确的,但 kid 不同,则 Amazon Cognito 可能已经轮换了签名密钥。从您的用户群体 jwks_uri 端点刷新缓存。

      这是个 jwks.json 文件示例:

      { "keys": [{ "kid": "1234example=", "alg": "RS256", "kty": "RSA", "e": "AQAB", "n": "1234567890", "use": "sig" }, { "kid": "5678example=", "alg": "RS256", "kty": "RSA", "e": "AQAB", "n": "987654321", "use": "sig" }] }
      密钥 ID(kid

      kid 是一个提示,指示哪个密钥用于保护令牌的 JSON Web 签名(JWS)。

      算法(alg

      alg 标头参数表示用于保护 ID 令牌的加密算法。用户池使用 RS256 加密算法,这是一种采用 SHA-256 的 RSA 签名。有关 RSA 的更多信息,请参阅 RSA 加密

      密钥类型(kty

      kty 参数标识与密钥结合使用的加密算法系列,例如,在本示例中为“RSA”。

      RSA 指数(e

      e 参数包含 RSA 公有密钥的指数值。它表示为采用 Base64urlUInt 编码的值。

      RSA 模数(n

      n 参数包含 RSA 公有密钥的模数值。它表示为采用 Base64urlUInt 编码的值。

      使用(use

      use 参数描述了公有密钥的预期用途。在本示例中,usesig 表示签名。

    2. 搜索与您 JWT 的 kid 相匹配的 kid 的公有 JSON Web 密钥。

  3. 使用 JWT 库将颁发者的签名与令牌中的签名进行比较。发布者签名来自在 jwks.json 中的 kid 公有密钥(RSA 模数 "n"),该公有密钥与令牌 kid 匹配。您可能首先需要将 JWK 转换为 PEM 格式。以下示例采用 JWT 和 JWK 格式,并且使用 Node.js 库 jsonwebtoken 来验证 JWT 签名:

    Node.js
    var jwt = require('jsonwebtoken'); var jwkToPem = require('jwk-to-pem'); var pem = jwkToPem(jwk); jwt.verify(token, pem, { algorithms: ['RS256'] }, function(err, decodedToken) { });

验证声明

验证 JWT 声明
  1. 通过以下方法之一,验证令牌是否未过期。

    1. 对令牌解码并将 exp 声明与当前时间进行比较。

    2. 如果访问令牌包含 aws.cognito.signin.user.admin 声明,请向 GetUser 等 API 发送请求。如果令牌已过期,您使用访问令牌进行授权的 API 请求会返回错误。

    3. 在针对userInfo 端点的请求中提供您的访问令牌。如果您的令牌已过期,则请求会返回错误。

  2. ID 令牌中的 aud 声明和访问令牌中的 client_id 声明应与在 Amazon Cognito 用户池中创建的应用程序客户端 ID 匹配。

  3. 发布者 (iss) 声明应与您的用户池匹配。例如,在 us-east-1 区域中创建的用户池将有下列 iss 值:

    https://cognito-idp.us-east-1.amazonaws.com/<userpoolID>.

  4. 检查 token_use 声明。

    • 如果您在 Web API 操作中只接受访问令牌,则其值必须为 access

    • 如果您只使用 ID 令牌,则其值必须为 id

    • 如果您同时使用 ID 令牌和访问令牌,则 token_use 声明必须为 idaccess

您现在可以信任该令牌内的声明。