无法在具有无服务器和 DynamoDB/Cognito/API 网关的 lambda 策略中使用 $cognito-identity.amazonaws.com:sub
Posted
技术标签:
【中文标题】无法在具有无服务器和 DynamoDB/Cognito/API 网关的 lambda 策略中使用 $cognito-identity.amazonaws.com:sub【英文标题】:Unable to use $cognito-identity.amazonaws.com:sub in lambda policy with serverless and DynamoDB/Cognito/API Gateway无法在具有无服务器和 DynamoDB/Cognito/API 网关的 lambda 策略中使用 $cognito-identity.amazonaws.com:sub 【发布时间】:2020-11-11 09:49:30 【问题描述】:目标:
-
使用 Cognito 进行身份验证(使用下面的 serverless.yml 配置)
点击经过身份验证的端点 GET /users 以触发 lambda 作业。
根据 IAM 策略,使用 LeadingKey Condition 限制对基于 cognito 用户
cognito-identity.amazonaws.com:sub
查询的 DynamoDB 表的访问。
问题:
我的策略似乎没有填充认知变量$cognito-identity.amazonaws.com:sub
。如果我用一个值手动指定dynamodb:LeadingKeys
,它就可以正常工作。所以看来我只需要 Cognito 来正确填充 sub 值,我到处找,找不到解决方案。
我的 lambda 角色/策略(将生成的版本从无服务器修改为具有 Cognito 和 DynamoDB 规则的信任策略):
"Version": "2012-10-17",
"Statement": [
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup"
],
"Resource": [
"arn:aws:logs:us-east-1:xxx:log-group:/aws/lambda/exeampleservice*:*"
],
"Effect": "Allow"
,
"Action": [
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:us-east-1:xxxx:log-group:/aws/lambda/exampleservice*:*:*"
],
"Effect": "Allow"
,
"Effect": "Allow",
"Action": [
"dynamodb:PutItem",
"dynamodb:GetItem",
"dynamodb:Query"
],
"Resource": "*",
"Condition":
"ForAllValues:StringEquals":
"dynamodb:LeadingKeys": "$cognito-identity.amazonaws.com:sub"
]
有信任关系:
"Version": "2012-10-17",
"Statement": [
"Effect": "Allow",
"Principal":
"Service": "lambda.amazonaws.com"
,
"Action": "sts:AssumeRole"
,
"Effect": "Allow",
"Principal":
"Federated": "cognito-identity.amazonaws.com"
,
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition":
"StringEquals":
"cognito-identity.amazonaws.com:aud": "us-east-1:<identity pool id>"
]
其他设置信息:
将 API 网关与 http 协议结合使用。 在下面的 serverless.yml 中创建了 userPool。 设置 Cognito 身份池(联合)。 创建了一个 userPool 组并为其分配了我的身份池 ID。 将池中的用户分配给组。 使用 Cognito 和 id 和访问令牌进行身份验证显示身份 id 令牌:
"sub": "xxxx",
"cognito:groups": [
"TestGroup"
],
"email_verified": true,
"iss": "https://cognito-idp.us-east-1.amazonaws.com/<poolid>",
"cognito:username": "xxx",
"cognito:roles": [
"arn:aws:iam::xxxx:role/Cognito_IdentityPoolAuth_Role"
],
"aud": "xxx",
"event_id": "xxx",
"token_use": "id",
"auth_time": 1595367712,
"exp": 1595371310,
"iat": 1595367710,
"email": "email@example.com"
我的简化 Serverless.yml
org: exampleorg
app: exampleapp
service: exampleservers
provider:
name: aws
stage: dev
runtime: nodejs12.x
iamManagedPolicies:
- 'arn:aws:iam::xxxx:policy/UserAccess'
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- 'Fn::ImportValue': '$self:provider.stage-UsersTableArn'
Condition:
'ForAllValues:StringEquals':
// use join to avoid conflict with serverless variable syntax. Ouputs
'dynamodb:LeadingKeys':
[Fn::Join: ['', ['$', 'cognito-identity.amazonaws.com:sub']]],
,
httpApi:
authorizers:
serviceAuthorizer:
identitySource: $request.header.Authorization
issuerUrl:
Fn::Join:
- ''
- - 'https://cognito-idp.'
- '$opt:region, self:provider.region'
- '.amazonaws.com/'
- Ref: serviceUserPool
audience:
- Ref: serviceUserPoolClient
functions:
# auth
login:
handler: auth/handler.login
events:
- httpApi:
method: POST
path: /auth/login
# authorizer: serviceAuthorizer
# user
getProfileInfo:
handler: user/handler.get
events:
- httpApi:
method: GET
path: /user/profile
authorizer: serviceAuthorizer
resources:
Resources:
HttpApi:
DependsOn: serviceUserPool
serviceUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: service-user-pool-$opt:stage, self:provider.stage
UsernameAttributes:
- email
AutoVerifiedAttributes:
- email
serviceUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: service-user-pool-client-$opt:stage, self:provider.stage
AllowedOAuthFlows:
- implicit
AllowedOAuthFlowsUserPoolClient: true
AllowedOAuthScopes:
- phone
- email
- openid
- profile
- aws.cognito.signin.user.admin
UserPoolId:
Ref: serviceUserPool
CallbackURLs:
- https://localhost:3000
ExplicitAuthFlows:
- ALLOW_USER_SRP_AUTH
- ALLOW_REFRESH_TOKEN_AUTH
GenerateSecret: false
SupportedIdentityProviders:
- COGNITO
serviceUserPoolDomain:
Type: AWS::Cognito::UserPoolDomain
Properties:
UserPoolId:
Ref: serviceUserPool
Domain: service-user-pool-domain-$opt:stage, self:provider.stage-$self:provider.environment.DOMAIN_SUFFIX
我已经尝试了几乎所有方法来在策略中获取变量 $cognito-identity.amazonaws.com:sub
,但似乎没有任何效果。
有人知道如何解决这个问题吗?或者我可能会错过什么。 (如果我错过了任何重要的信息,我会更新更多信息)。
谢谢!
编辑:(附加信息)
我的登录功能(lambda + HTTP API)在下面,我通过用户/密码授权用户,然后调用 CognitoIdentityCredentials 来“注册”我的身份并从池中获取我的 identityId。 (我验证我正在注册,因为身份池向用户显示)
然后我的登录调用以 accessToken、idToken、identityId 进行响应。
我的所有其他 API 调用在授权我的 Bearer Authorization 调用中使用 idToken,但似乎我的身份池的授权角色没有被假定,它正在使用我的 lambda 角色执行。
我在这里缺少什么?我认为 Cognito 会处理 Authenticated Identity 池的假定角色,但似乎整个 ?任何帮助表示赞赏!
我的请求上下文(来自我的登录函数,注意身份对象充满了空值):
requestContext:
accountId: 'xxx',
apiId: 'xxx',
domainName: 'xxxx.execute-api.us-east-1.amazonaws.com',
domainPrefix: 'xxx',
extendedRequestId: 'xxxx=',
httpMethod: 'POST',
identity:
accessKey: null,
accountId: null,
caller: null,
cognitoAuthenticationProvider: null,
cognitoAuthenticationType: null,
cognitoIdentityId: null,
cognitoIdentityPoolId: null,
principalOrgId: null,
sourceIp: 'xxxx',
user: null,
userAgent: 'PostmanRuntime/7.26.1',
userArn: null
,
我的登录功能
const AWS = require('aws-sdk');
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
global.fetch = require('node-fetch').default; // .default for webpack.
const USER_POOL_ID = process.env.USER_POOL_ID;
const USER_POOL_CLIENT_ID = process.env.USER_POOL_CLIENT_ID;
const USER_POOL_IDENTITY_ID = process.env.USER_POOL_IDENTITY_ID;
console.log('USER_POOL_ID', USER_POOL_ID);
console.log('USER_POOL_CLIENT_ID', USER_POOL_CLIENT_ID);
console.log('USER_POOL_CLIENT_ID', USER_POOL_IDENTITY_ID);
const poolData =
UserPoolId: USER_POOL_ID,
ClientId: USER_POOL_CLIENT_ID,
;
const poolRegion = 'us-east-1';
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
function login(Username, Password)
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(
Username,
Password,
);
var userData =
Username,
Pool: userPool,
;
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
return new Promise((resolve, reject) =>
cognitoUser.authenticateUser(authenticationDetails,
onSuccess: function (result)
AWS.config.credentials = new AWS.CognitoIdentityCredentials(
IdentityPoolId: USER_POOL_IDENTITY_ID, // your identity pool id here
Logins:
// Change the key below according to the specific region your user pool is in.
[`cognito-idp.$poolRegion.amazonaws.com/$USER_POOL_ID`]: result
.getIdToken()
.getJwtToken(),
,
);
//refreshes credentials using AWS.CognitoIdentity.getCredentialsForIdentity()
AWS.config.credentials.refresh((error) =>
if (error)
console.error(error);
else
// Instantiate aws sdk service objects now that the credentials have been updated.
// example: var s3 = new AWS.S3();
console.log('Successfully Refreshed!');
AWS.config.credentials.get(() =>
// return back all tokens and identityId in login call response body.
const identityId = AWS.config.credentials.identityId;
const tokens =
accessToken: result.getAccessToken().getJwtToken(),
idToken: result.getIdToken().getJwtToken(),
refreshToken: result.getRefreshToken().getToken(),
identityId,
;
resolve(tokens);
);
);
,
onFailure: (err) =>
console.log(err);
reject(err);
,
);
);
module.exports =
login,
;
【问题讨论】:
【参考方案1】:我不太清楚你是否假设了一个身份(将你的 ID 令牌从用户池交换为 STS 令牌)。
令人困惑的是,cognito-identity.amazonaws.com:sub 解析为 ID 池身份 ID,而不是来自用户池的 ID 令牌中的主题 ID。请参阅此页面上的注释部分:https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_s3_cognito-bucket.html
要获取身份凭证,请查看https://docs.aws.amazon.com/cognitoidentity/latest/APIReference/API_GetCredentialsForIdentity.html
【讨论】:
@JDClark 感谢您的回复。澄清一下,我正在获取身份凭据并在我的登录 lambda 函数中获取 identityId。但我不确定下一步是什么。看来我的 IdToken 和访问令牌即使在之后保持不变,最重要的是,它似乎没有 cognito 承担我的身份验证提供的角色,因为它似乎仍在使用他承担的我的 lambda 执行策略的角色。如果您有时间看一下,这是我的登录功能。 forums.aws.amazon.com/thread.jspa?messageID=950278#950278 在上面的登录函数中,我登录并获取凭据,但由于我在邮递员中执行此操作,因此我只是在所有呼叫中使用 Bearer事实证明,如果您将 AWS Gateway 与 Lambda 结合使用,您不能使用这些变量。
您必须使用 IAM auth(使用 aws amplify 之类的东西)直接从您注册身份的客户端应用程序访问 DynamoDB。
我最终使用 STS 在我的 lambda 函数中承担 Cognito 的组身份验证角色,并完全绕过身份池。
【讨论】:
以上是关于无法在具有无服务器和 DynamoDB/Cognito/API 网关的 lambda 策略中使用 $cognito-identity.amazonaws.com:sub的主要内容,如果未能解决你的问题,请参考以下文章
具有无状态服务器且无服务器端呈现的 cookie 中的 JWT 身份验证
Cloudera安装未能检测到CentOS上的root特权我试图将新主机添加到CentOS集群中。安装失败,状态为“安装失败。无法检测root特权”。我知道Cloudera需要用户具有无密码特权(“需