如何让Lambda扮演Cognito用户角色调用带IAM授权的REST接口?
我来帮你搞定这个需求,核心思路是让调用方Lambda临时「变身」成发起请求的Cognito用户——也就是获取该用户对应的IAM角色临时凭证,再用这个凭证签名调用目标API Gateway。具体步骤拆解如下:
1. 确保调用方Lambda能拿到用户的Cognito身份凭证
因为前端是通过Amplify+Cognito调用这个Lambda的,你需要让前端把用户的Cognito ID Token传递给Lambda(比如放在请求的Authorization header里,格式一般是Bearer {id-token})。同时,Lambda的事件上下文里也能拿到用户的Cognito身份ID(event.requestContext.identity.cognitoIdentityId),不过ID Token是后续获取临时角色凭证的关键。
2. 给调用方Lambda的执行角色添加权限
需要允许调用方Lambda调用STS的AssumeRoleWithWebIdentity接口,用来获取用户对应的Identity Pool角色的临时凭证。给Lambda的IAM执行角色添加如下政策:
{ "Effect": "Allow", "Action": "sts:AssumeRoleWithWebIdentity", "Resource": "arn:aws:iam::你的AWS账号ID:role/你的IdentityPool认证角色ARN" }
这里的Resource要替换成你在Cognito Identity Pool里配置的Authenticated Role(如果是未登录用户则用Unauthenticated Role)的ARN,也就是前端调用API时使用的那个角色。
3. 在Lambda中实现角色切换与API调用
用AWS SDK调用STS获取临时凭证,再用凭证签名请求目标API。以下是Node.js环境的代码示例:
const { STSClient, AssumeRoleWithWebIdentityCommand } = require("@aws-sdk/client-sts"); const { SignatureV4 } = require("@aws-sdk/signature-v4"); const { HttpRequest } = require("@aws-sdk/protocol-http"); const fetch = require("node-fetch"); exports.handler = async (event) => { // 从请求头中提取Cognito ID Token const authHeader = event.headers.Authorization; if (!authHeader) throw new Error("Missing authorization token"); const idToken = authHeader.split(' ')[1]; const stsClient = new STSClient({ region: "你的AWS区域" }); // 构造STS调用参数 const assumeRoleParams = { RoleArn: "arn:aws:iam::你的账号ID:role/IdentityPool-AuthenticatedRole", // 替换为你的认证角色ARN RoleSessionName: `cognito-user-${Date.now()}`, // 唯一会话名称,可自定义 WebIdentityToken: idToken, DurationSeconds: 3600 // 临时凭证有效期,最长1小时 }; // 获取用户角色的临时凭证 const assumeRoleResult = await stsClient.send( new AssumeRoleWithWebIdentityCommand(assumeRoleParams) ); const { AccessKeyId, SecretAccessKey, SessionToken } = assumeRoleResult.Credentials; // 构造目标API请求并签名(IAM Authorizer要求请求必须用SigV4签名) const apiHost = "你的API Gateway域名(比如xxxx.execute-api.us-east-1.amazonaws.com)"; const apiPath = "/你的API阶段/目标路径"; const request = new HttpRequest({ method: "GET", // 根据你的API调整请求方法 hostname: apiHost, path: apiPath, headers: { "Content-Type": "application/json", host: apiHost } }); // 用临时凭证签名请求 const signer = new SignatureV4({ credentials: { accessKeyId: AccessKeyId, secretAccessKey: SecretAccessKey, sessionToken: SessionToken }, region: "你的AWS区域", service: "execute-api" }); const signedRequest = await signer.sign(request); // 发送请求并返回结果 const apiResponse = await fetch(`https://${apiHost}${apiPath}`, { method: signedRequest.method, headers: signedRequest.headers, body: signedRequest.body }); const responseData = await apiResponse.json(); return { statusCode: apiResponse.status, body: JSON.stringify(responseData) }; };
4. 确保目标API允许用户角色调用
给Identity Pool的Authenticated Role添加允许调用目标API的政策,这样临时凭证才有权限访问:
{ "Effect": "Allow", "Action": "execute-api:Invoke", "Resource": "arn:aws:execute-api:你的区域:你的账号ID:你的API ID/*/*/目标路径" }
额外注意事项
- 建议在Lambda中验证ID Token的有效性(比如用Cognito的JWKS公钥验证签名),防止伪造的Token被用来获取角色凭证。
- 临时凭证的有效期遵循最小必要原则,不要设置超过实际需要的时长。
- 如果你的API和Lambda在同一账号下,不要图省事直接允许Lambda角色调用API——那样就不是以用户身份调用了,不符合你的需求。
内容的提问来源于stack exchange,提问作者Eitan

