AWS CDK v2配置Cognito双认证流后API Gateway客户端凭证授权失败
问题
我的API需要同时支持OAuth用户认证流(网页用户注册)和客户端凭证流(如Grafana等应用直接调用),已通过AWS CDK v2部署基础设施,但仅用户登录能正常授权,使用客户端凭证获取的access_token调用API Gateway的/my-resource GET端点时返回401 Unauthorized。请问如何解决该问题?是否与ResourceServerScope配置有关?遗漏了什么?
AuthStack 代码
export class AuthStack extends Stack { public userPool!: UserPool; private userPoolClient!: UserPoolClient; constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); this.userPool = new UserPool(this, "MyUserPool", { selfSignUpEnabled: true, signInAliases: { username: true, email: true } }); this.userPool.addDomain("MyUserPoolDomain", { cognitoDomain: { domainPrefix: "my-auth-prototype" } }); new CfnUserPoolGroup(this, "MyAdminPoolGroup", { userPoolId: this.userPool.userPoolId, groupName: "admin" }); const clientReadServerScope = new ResourceServerScope({ scopeName: "client.read", scopeDescription: "client read scope", }); const resourceServer = new UserPoolResourceServer(this, "ClientCredentialsResourceServer", { identifier: "client-credentials-resource-server", userPool: this.userPool, scopes: [clientReadServerScope], }); this.userPoolClient = this.userPool.addClient("MyUserPoolClient", { accessTokenValidity: Duration.minutes(60), generateSecret: true, refreshTokenValidity: Duration.days(1), enableTokenRevocation: true, oAuth: { flows: { clientCredentials: true, }, scopes: [OAuthScope.resourceServer(resourceServer, clientReadServerScope)], }, authFlows: { adminUserPassword: true, custom: true, userPassword: true, userSrp: true } }); } }
ApiStack 代码
export class ApiStack extends Stack { constructor(scope: Construct, id: string, props: ApiStackProps) { super(scope, id, props); const api = new RestApi(this, "MyApi", { binaryMediaTypes: ["*/*"] }); //Cognito authorizor const authorizer = new CognitoUserPoolsAuthorizer(this, "MyApiAuthorizor", { cognitoUserPools: [props.userPool], identitySource: "method.request.header.Authorization" }); authorizer._attachToApi(api); //Cognito options const optionsWithAuth: MethodOptions = { authorizationType: AuthorizationType.COGNITO, authorizer: { authorizerId: authorizer.authorizerId } }; //CORS options const optionsWithCors: ResourceOptions = { defaultCorsPreflightOptions: { allowOrigins: Cors.ALL_ORIGINS, allowMethods: Cors.ALL_METHODS } }; //base resource const dataPipelineResource = api.root.addResource("my-resource", optionsWithCors); dataPipelineResource.addMethod("GET", props.lambdaIntegration, optionsWithAuth); } }
获取 access_token 的代码
export const getClientToken = async (clientId: string, clientSecret: string): Promise<string | null> => { const authEndpoint = "https://my-auth-prototype.auth.us-east-1.amazoncognito.com/oauth2/token"; const res = await fetch(authEndpoint, { method: "POST", body: new URLSearchParams({ "grant_type": "client_credentials", }), headers: { "Content-Type": "application/x-www-form-urlencoded", "Authorization": "Basic " + Buffer.from(`${clientId}:${clientSecret}`).toString("base64") } }); const data = await res.json(); return data.access_token; };
解决方案
问题出在两个核心环节:API Gateway授权器未验证客户端凭证流的token权限,以及获取token时未指定所需的资源服务器scope。以下是具体修复步骤:
1. 为API Gateway授权器添加Scope验证
当前的Cognito授权器仅验证token是否来自指定用户池,未校验客户端凭证流token包含的资源服务器权限范围。需要修改ApiStack中的授权器配置,添加scopes参数:
const authorizer = new CognitoUserPoolsAuthorizer(this, "MyApiAuthorizor", { cognitoUserPools: [props.userPool], identitySource: "method.request.header.Authorization", // 添加Scope验证,格式为「资源服务器identifier/scopeName」 scopes: ["client-credentials-resource-server/client.read"] });
2. 获取客户端凭证Token时指定Scope
当前的token请求未包含所需的Scope参数,导致返回的access_token未关联资源服务器权限。需要修改getClientToken函数的请求Body,添加scope字段:
export const getClientToken = async (clientId: string, clientSecret: string): Promise<string | null> => { const authEndpoint = "https://my-auth-prototype.auth.us-east-1.amazoncognito.com/oauth2/token"; const res = await fetch(authEndpoint, { method: "POST", body: new URLSearchParams({ "grant_type": "client_credentials", // 添加Scope参数,与资源服务器配置一致 "scope": "client-credentials-resource-server/client.read" }), headers: { "Content-Type": "application/x-www-form-urlencoded", "Authorization": "Basic " + Buffer.from(`${clientId}:${clientSecret}`).toString("base64") } }); const data = await res.json(); return data.access_token; };
3. 验证用户池客户端权限配置(可选但重要)
确认用户池客户端的OAuth配置已正确关联资源服务器Scope,你的现有代码已配置正确,但可在AWS控制台的Cognito用户池客户端设置中检查:
- 「客户端凭证」流已启用
- 已关联
client-credentials-resource-server/client.read这个Scope
修复逻辑说明
- 客户端凭证流的Token必须包含资源服务器的Scope,才能被API Gateway授权器识别为有权访问指定资源
- API Gateway的Cognito授权器需要明确指定要验证的Scope,否则只会校验Token有效性,不会校验权限范围
- 两个环节的Scope格式必须完全一致:
资源服务器identifier/scopeName
内容的提问来源于stack exchange,提问作者Tsar Bomba
相关产品推荐
相关产品推荐

