跳到主要内容

使用 Amazon Cognito 为 M2M 请求授权

Amazon Cognito 用户池可以向机密应用客户端颁发访问 Token。后台服务、计划任务、命令行工具和自动化程序可以使用这些 Token 调用 API,无需最终用户登录。

OAuth 2.0 客户端凭证授权 专为这种机器到机器(M2M)通信设计。Apache APISIX 可以在代理请求前验证每个 Token 的签名、Issuer、应用客户端标识符和自定义 Scope,从而保护目标 API。

本指南介绍如何为 APISIX 配置 Cognito Resource Server 和 M2M 应用客户端。应用客户端会获得自定义 Scope,并使用客户端凭证请求访问 Token。APISIX 通过 Cognito 的 JSON Web Key Set(JWKS)在本地验证 Token,并在把请求转发到示例上游前移除身份认证数据。

前置条件

  • 安装 Docker
  • 安装 cURLjq
  • 按照入门指南使用 Docker 启动 APISIX。
  • 拥有 AWS 账户,并具有管理 Amazon Cognito 用户池、Resource Server 和应用客户端的权限。
  • 如果计划使用 ADC,请先安装并配置 ADC
警告

Amazon Cognito 会对成功的 M2M Token 响应收费。请查看 Amazon Cognito 定价,并在现有 Token 仍有效时避免请求新 Token。

配置 Amazon Cognito

使用带 Domain 的 Cognito 用户池,然后注册 Resource Server 和 M2M 应用客户端。

创建或选择用户池

登录 AWS 管理控制台,然后打开 Amazon Cognito → User pools

如果已有合适且配置了 Domain 的用户池,请选择该用户池并继续下一节;否则,请创建一个用户池:

  1. 选择 Create user pool
  2. 选择 Machine-to-machine application 作为应用类型。
  3. 输入 APISIX M2M Client 作为应用名称。
  4. 选择 Create user directory

Cognito 会创建用户池、机密应用客户端、用户池 Domain,以及自动生成的默认 Resource Server 和 Scope。以下步骤会把自动生成的 Scope 替换为可清晰标识受保护 API 的 Scope。

创建 Resource Server

在用户池中创建受保护的 API 及其自定义 Scope:

  1. 选择 Applications → Resource servers → Create resource server
  2. 输入 APISIX Protected API 作为 Resource Server 名称。
  3. 输入 https://apisix.example.com 作为 Resource Server 标识符。
  4. 选择 Add custom scope
  5. 输入 read 作为 Scope 名称,并输入 Read protected messages 作为描述。
  6. 选择 Create resource server

该标识符是 API 的逻辑名称,无需解析为网络端点。Cognito 会把标识符和 Scope 名称组合为访问 Token 中的 https://apisix.example.com/read

配置 M2M 应用客户端

如果用户池是为其他应用创建的,请添加 M2M 应用客户端:

  1. 选择 Applications → App clients → Create app client
  2. 选择 Machine-to-machine application
  3. 输入 APISIX M2M Client 作为应用名称。
  4. 保持选中 Automatically generate a new client secret
  5. 选择 Create app client

授权应用客户端请求受保护的 API Scope:

  1. 打开 APISIX M2M Client,然后选择 Login pages → Edit
  2. Custom scopes 下,如果存在自动生成的默认 Scope,请将其移除。
  3. 选择 https://apisix.example.com/read
  4. 选择 Save changes

为 Cognito M2M 应用客户端授予受保护 API Scope

应用客户端的 OAuth Grant Type 应显示为 Client credentials grant,自定义 Scope 应显示为 https://apisix.example.com/read。在同一个 Cognito 应用客户端中,Client Credentials 不能与 Authorization Code 或 Implicit Grant 组合使用。

托管登录状态会显示为 Unavailable,因为 M2M 应用客户端不使用交互式登录页面。OAuth Token 端点仍可通过用户池 Domain 访问。

保存 OAuth 配置

在用户池 Overview 页面记录 User pool ID。在 Branding → Domain 下记录 Cognito Domain。打开 M2M 应用客户端,并记录其 Client IDClient secret

将示例替换为实际值,并保存为环境变量:

export COGNITO_REGION=ap-southeast-2
export COGNITO_USER_POOL_ID=ap-southeast-2_example
export COGNITO_DOMAIN=your-prefix.auth.ap-southeast-2.amazoncognito.com
export COGNITO_M2M_CLIENT_ID=replace-with-your-client-id
export COGNITO_M2M_CLIENT_SECRET=replace-with-your-client-secret
export COGNITO_M2M_SCOPE=https://apisix.example.com/read
export COGNITO_ISSUER="https://cognito-idp.${COGNITO_REGION}.amazonaws.com/${COGNITO_USER_POOL_ID}"
export COGNITO_DISCOVERY="${COGNITO_ISSUER}/.well-known/openid-configuration"

请对客户端密钥保密。生产凭证应存储在密钥管理器中,并按照组织的凭证轮换策略进行轮换。

配置 APISIX

配置一个路由,使其在把请求转发到公共 HTTP 请求和响应服务 httpbin.org 前接受 Cognito Bearer Token。/anything/m2m/* 端点会返回请求详情以供验证。

选择使用 Admin API 或 ADC 配置路由。

创建一个使用 Cognito JWKS 在本地验证 Bearer Token 的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes/cognito-m2m" -X PUT \
--data-binary @- <<EOF
{
"uri": "/anything/m2m/*",
"plugins": {
"openid-connect": {
"client_id": "$COGNITO_M2M_CLIENT_ID",
"discovery": "$COGNITO_DISCOVERY",
"bearer_only": true,
"use_jwks": true,
"claim_validator": {
"audience": {
"claim": "client_id",
"required": true,
"match_with_client_id": true
}
},
"required_scopes": ["$COGNITO_M2M_SCOPE"],
"set_access_token_header": false,
"set_id_token_header": false,
"set_userinfo_header": false
},
"proxy-rewrite": {
"headers": {
"remove": ["Authorization"]
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}
EOF

bearer_onlyuse_jwks:要求提供 Bearer 访问 Token,并通过 Cognito 发布的公钥在本地验证其 JWT 签名。

claim_validator.audience:读取 Cognito 访问 Token 的 client_id Claim,并要求它与配置为 client_id 的 M2M 应用客户端匹配。Cognito 客户端凭证 Token 不包含 API Audience Claim。

required_scopes:要求访问 Token 包含分配给 M2M 应用客户端的自定义 Scope。

set_access_token_headerset_id_token_headerset_userinfo_header:设为 false,避免 APISIX 把访问 Token、ID Token 和 Token Claim 添加到上游请求头。

proxy-rewrite.headers.remove:在 APISIX 代理请求前移除原始 Bearer Token。如果上游应用必须接收访问 Token 或其 Claim,请审查这些请求头设置。

验证 M2M 授权

从 Cognito 请求受保护 API 的访问 Token。--user 选项使用 HTTP Basic 身份认证发送客户端 ID 和密钥:

export COGNITO_ACCESS_TOKEN="$(
curl -sS "https://${COGNITO_DOMAIN}/oauth2/token" \
--user "${COGNITO_M2M_CLIENT_ID}:${COGNITO_M2M_CLIENT_SECRET}" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "scope=${COGNITO_M2M_SCOPE}" | \
jq -er '.access_token'
)"

将访问 Token 发送到受保护的路由:

curl -i "http://127.0.0.1:9080/anything/m2m/get" \
-H "Authorization: Bearer ${COGNITO_ACCESS_TOKEN}"

HTTP/1.1 200 OK 响应表示 APISIX 已接受来自所配置应用客户端、且包含所需自定义 Scope 的 Cognito 访问 Token。响应体应包含类似字段:

{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Host": "127.0.0.1",
"User-Agent": "curl/8.7.1",
"X-Amzn-Trace-Id": "Root=1-...",
"X-Forwarded-Host": "127.0.0.1:9080"
},
"json": null,
"method": "GET",
"origin": "192.168.155.1, xxx.xxx.xxx.xxx",
"url": "http://127.0.0.1:9080/anything/m2m/get"
}

请求头值和报告的来源地址会随客户端及网络环境而变化。上游请求头不应包含 AuthorizationX-Access-TokenX-Id-TokenX-Userinfo,因为该路由已阻止代理这些请求头。

不携带 Token 发送相同请求:

curl -i "http://127.0.0.1:9080/anything/m2m/get"

由于该路由要求 Bearer Token,APISIX 应返回 HTTP/1.1 401 Unauthorized

后续步骤

你现在已配置 APISIX,使用 Amazon Cognito 颁发的访问 Token 和自定义 Scope 为 M2M 请求授权。如需配置基于浏览器的用户身份认证,请参阅使用 Amazon Cognito 配置 SSO。有关更多 Token 验证和授权选项,请参阅 openid-connect 插件参考