使用 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。
- 安装 cURL 和 jq。
- 按照入门指南使用 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 的用户池,请选择该用户池并继续下一节;否则,请创建一个用户池:
- 选择 Create user pool。
- 选择 Machine-to-machine application 作为应用类型。
- 输入
APISIX M2M Client作为应用名称。 - 选择 Create user directory。
Cognito 会创建用户池、机密应用客户端、用户池 Domain,以及自动生成的默认 Resource Server 和 Scope。以下步骤会把自动生成的 Scope 替换为可清晰标识受保护 API 的 Scope。
创建 Resource Server
在用户池中创建受保护的 API 及其自定义 Scope:
- 选择 Applications → Resource servers → Create resource server。
- 输入
APISIX Protected API作为 Resource Server 名称。 - 输入
https://apisix.example.com作为 Resource Server 标识符。 - 选择 Add custom scope。
- 输入
read作为 Scope 名称,并输入Read protected messages作为描述。 - 选择 Create resource server。
该标识符是 API 的逻辑名称,无需解析为网络端点。Cognito 会把标识符和 Scope 名称组合为访问 Token 中的 https://apisix.example.com/read。
配置 M2M 应用客户端
如果用户池是为其他应用创建的,请添加 M2M 应用客户端:
- 选择 Applications → App clients → Create app client。
- 选择 Machine-to-machine application。
- 输入
APISIX M2M Client作为应用名称。 - 保持选中 Automatically generate a new client secret。
- 选择 Create app client。
授权应用客户端请求受保护的 API Scope:
- 打开
APISIX M2M Client,然后选择 Login pages → Edit。 - 在 Custom scopes 下,如果存在自动生成的默认 Scope,请将其移除。
- 选择
https://apisix.example.com/read。 - 选择 Save changes。

应用客户端的 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 ID 和 Client 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 配置路由。
- 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_only 和 use_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_header、set_id_token_header 和 set_userinfo_header:设为 false,避免 APISIX 把访问 Token、ID Token 和 Token Claim 添加到上游请求头。
❺ proxy-rewrite.headers.remove:在 APISIX 代理请求前移除原始 Bearer Token。如果上游应用必须接收访问 Token 或其 Claim,请审查这些请求头设置。
创建包含相同路由配置的 adc.yaml 文件:
services:
- name: httpbin
routes:
- name: cognito-m2m
uris:
- /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:
- host: httpbin.org
port: 80
weight: 1
❶ bearer_only 和 use_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_header、set_id_token_header 和 set_userinfo_header:设为 false,避免 APISIX 把访问 Token、ID Token 和 Token Claim 添加到上游请求头。
❺ proxy-rewrite.headers.remove:在 APISIX 代理请求前移除原始 Bearer Token。如果上游应用必须接收访问 Token 或其 Claim,请审查这些请求头设置。
将配置同步到 APISIX:
adc sync -f adc.yaml
验证 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"
}
请求头值和报告的来源地址会随客户端及网络环境而变化。上游请求头不应包含 Authorization、X-Access-Token、X-Id-Token 或 X-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 插件参考。