View a markdown version of this page

验证 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) 的过程。

先决条件

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

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

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

使用 aws-jwt-verify 验证令牌

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

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

注意

当令牌中的iss(颁发者)声明CognitoJwtVerifier与您配置的用户池不匹配时,验证将失败,并显示为ParameterValidationError。当向不同用户池或 Amazon 区域的验证者提供令牌时,通常会发生这种情况。确认userPoolId您传递给的与您的应用程序收到的令牌的发行者CognitoJwtVerifier.create()相匹配。

了解和检查令牌

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

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

确认 JWT 的结构

JSON 网络令牌 (JWT) 包括三个部分,中间有一个.(点)分隔符。

标题

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

有效载荷

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

签名

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

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

以下示例应用程序使用 aws-jwt-verify 验证用户池令牌。

// cognito-verify.js // Usage example: node cognito-verify.js eyJra789ghiEXAMPLE const { CognitoJwtVerifier } = require('aws-jwt-verify'); // Replace with your Amazon Cognito user pool ID const userPoolId = 'us-west-2_EXAMPLE'; async function verifyJWT(token) { try { const verifier = CognitoJwtVerifier.create({ userPoolId, tokenUse: 'access', // or 'id' for ID tokens clientId: '1example23456789', // you must verify the token audience }); const payload = await verifier.verify(token); console.log('Decoded JWT:', payload); } catch (err) { console.error('Error verifying JWT:', err); } } // Example usage if (process.argv.length < 3) { console.error('Please provide a JWT token as an argument.'); process.exit(1); } const MyToken = process.argv[2]; verifyJWT(MyToken);

验证 JWT

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

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

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

    您还可以使用 Amazon Lambda 解码用户池 JWT。有关更多信息,请参阅使用解码和验证 Amazon Cognito J WT 令牌。 Amazon Lambda

  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 加密算法,该算法是 RSA 签名。 SHA-256有关 RSA 的更多信息,请参阅 RSA 加密。

      密钥类型(kty)

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

      RSA 指数(e)

      e 参数包含 RSA 公有密钥的指数值。它表示为一个 Base64urlUInt-encoded 值。

      RSA 模数(n)

      n 参数包含 RSA 公有密钥的模数值。它表示为一个 Base64urlUInt-encoded 值。

      使用(use)

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

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

验证声明

验证 JWT 声明
  1. 确认令牌尚未过期。对令牌解码并将 exp 声明与当前时间进行比较。由于用户池 JWT 是独立的,并且有自己的到期时间,因此您可以离线检查到期时间,无需网络调用 Amazon Cognito。

    注意

    诸如GetUser或userInfo 端点端点之类的 API 操作会为过期的访问令牌返回错误,但被拒绝的请求是授权的副作用,而不是令牌验证方法。不要使用这些网络调用来检查过期时间:改为解码令牌并比较exp声明。

  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 声明必须为 id 或 access。

  5. 确认令牌尚未被撤销。具有有效签名且exp声明未过期的令牌仍可以在其到期时间之前被撤销,例如,当用户注销或您调用 API 操作时。RevokeToken离线签名和到期检查无法自行检测到撤销。如果您的应用程序必须在令牌到期之前执行撤销,请根据 Amazon Cognito 验证令牌的状态(例如,通过授权的 API 请求),或者缩短访问令牌的生命周期。有关更多信息,请参阅 通过令牌撤销来结束用户会话。

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

注意

有些人 Amazon Web Services 服务 可以为你验证 Amazon Cognito JWT,而不是在你的应用程序代码中验证。如果您使用 Amazon Cognito 作为 Amazon API Gateway HTTP API 的 JWT 授权方,则您在授权方配置中提供相同的颁发者和受众值。将颁发者设置为您的用户池端点https://cognito-idp.<Region>.amazonaws.com/<userPoolId>,这也是您的用户池/.well-known/openid-configuration文档中该issuer字段的值。将受众设置为您的应用程序客户端 ID。

API Gateway 将受众与代币aud的主张进行匹配,或者在代币没有client_id声明时将受众与aud索赔进行匹配。Amazon Cognito ID 令牌随身携带aud,访问令牌携带client_id,aud除非请求包含资源服务器绑定,否则不是。对于 ID 令牌或没有资源绑定的访问令牌,请将受众设置为您的应用程序客户端 ID。当您的应用程序请求绑定到资源服务器的访问令牌时,该令牌的aud声明将设置为资源标识符(资源服务器网址),而不是应用程序客户端 ID;在这种情况下,请将资源服务器 URL 配置为受众,以便 API Gateway 与之匹配。有关 API Gateway 配置步骤,请参阅使用 Amazon Cognito 用户池作为授权方控制对 REST API 的访问和在 API Gateway 中使用 JWT 授权人控制对 HTTP API 的访问。

Application Load Balancer 还可以使用您的用户池对用户进行身份验证。负载均衡器通过应用程序验证的 JWT-formatted x-amzn-oidc-data HTTP 标头将签名的用户声明传递给您的目标。有关更多信息,请参阅使用 Application Load Balancer 对用户进行身份验证。