无法在无服务器和 DynamoDB/Cognito/API 网关的 lambda 策略中使用 ${cognito-identity.amazonaws.com:sub}

2023-12-13

客观的:

  1. 使用 Cognito 进行身份验证(使用下面的 serverless.yml 配置)
  2. 点击经过身份验证的端点 GET /users 以触发 lambda 作业。
  3. 基于IAM策略,限制基于cognito用户查询的DynamoDB表的访问cognito-identity.amazonaws.com:sub使用LeadingKey条件。

问题:我的策略似乎没有填充认知变量${cognito-identity.amazonaws.com:sub}。如果我手动指定dynamodb:LeadingKeys有了一个值,它就可以正常工作。所以看来我只需要 Cognito 正确填充子值,我已经到处寻找但找不到解决方案。

我的 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>"
        }
      }
    }
  ]
}

附加设置信息:

  • 使用带有http协议的API网关。
  • 在下面的 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 protected]"
}
  • 我的简化 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}在政策中,但似乎没有任何作用。

有谁知道如何解决这个问题?或者我可能会错过什么。 (如果我错过了任何重要的内容,我将更新更多信息)。

Thanks!

编辑:(附加信息)

我的登录函数(lambda + HTTP API)如下,我通过用户/密码授权User,然后调用CognitoIdentityCredentials“注册”我的身份并从池中获取我的identityId。 (我验证我正在注册,因为身份池显示用户)

然后,我的登录调用会使用 accessToken、idToken、identityId 进行响应。

我的所有其他 API 调用都在对我进行授权的承载授权调用中使用 idToken,但似乎并未假定我的身份池的授权角色,而是使用我的 lambda 角色来执行。

我在这里缺少什么?我认为 Cognito 会处理身份验证池的假设角色,但看起来整个?任何帮助表示赞赏!

我的请求上下文(来自我的登录函数,请注意身份对象充满空值):

 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,
};

我不太清楚您是否已假定身份(将用户池中的 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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

无法在无服务器和 DynamoDB/Cognito/API 网关的 lambda 策略中使用 ${cognito-identity.amazonaws.com:sub} 的相关文章

  • 决策树和规则引擎 (Drools)

    In the application that I m working on right now I need to periodically check eligibility of tens of thousands of object
  • 我可以通过在 Android Activity 中声明适当的成员“静态”来提高效率吗

    如果一个 Activity 在实践中是单例 我认为我可以通过声明适当的成员 静态 来获得一些效率 且风险为零 是的 The Android 文档说 http developer android com guide topics fundam
  • Spark scala 模拟 Spark.implicits 用于单元测试

    当尝试使用 Spark 和 Scala 简化单元测试时 我使用 scala test 和mockito scala 以及mockito Sugar 这只是让你做这样的事情 val sparkSessionMock mock SparkSes
  • Chrome 调试器注入 javascript

    我有这样的好奇心 是否可以以某种方式在我的页面中注入 javascript 并执行它并调试它 正如您在控制台中所做的那样 但在控制台中您无法暂停并观察变量 是否可以调试我通过控制台输入的代码 为什么无法调试通过 XHR 接收的代码 Than
  • 使用 Ruby aws-sdk 跟踪文件到 S3 的上传进度

    首先 我知道SO中有很多与此类似的问题 在过去的一周里 我读了大部分 如果不是全部 但我仍然无法让这项工作为我工作 我正在开发一个 Ruby on Rails 应用程序 允许用户将 mp3 文件上传到 Amazon S3 上传本身工作正常
  • Matplotlib loglog 的错误刻度/标签(双轴)

    我正在使用 matplotlib 创建对数图 如下图所示 默认刻度选择得很糟糕 充其量是这样 右边的 y 轴甚至根本没有 在线性等效中确实如此 而两个 x 轴都只有一个 有没有办法获得合理数量的带有标签的刻度 without为每个情节手动指
  • 在DialogFragment中,onCreate应该做什么?

    我目前正在摆弄 DialogFragment 以学习使用它 我假设相比onCreateView onCreate 可以这样做 public void onCreate Bundle savedInstanceState super onCr
  • 即使在急切加载之后,belongs_to 关联也会单独加载

    我有以下关联 class Picture lt ActiveRecord Base belongs to user end class User lt ActiveRecord Base has many pictures end 在我的
  • 如何通过点击复制 folium 地图上的标记位置?

    I am able to print the location of a given marker on the map using folium plugins MousePosition class GeoMap def update
  • 如何禁用 solr 管理页面

    对于生产来说 拥有一个甚至不要求登录凭据的 solr 管理员感觉不安全 如何禁用默认的 solr 管理页面 我只是希望我的 web 应用程序使用 Solr 进行搜索词索引 我强烈建议保留管理页面用于调试目的 它在很多情况下拯救了我 有多种方
  • 进程被杀死后不会调用 onActivityResult

    我有一个主要活动 Main 和另一个活动 Sub 由 Main 调用 startActivityForResult new Intent this SubActivity class 25 当我在 Sub 时 我终止该进程 使用任务管理器或
  • 在成为FirstResponder或resignFirstResponder的情况下将对象保持在键盘顶部?

    我目前在键盘顶部有一个 UITextField 当您点击它时 它应该粘在键盘顶部并平滑地向上移动 我不知道键盘的具体时长和动画类型 所以确实很坎坷 这是我所拥有的 theTextView resignFirstResponder UIVie
  • 水平和垂直居中 div 位于页面中间,页眉和页脚粘在页面顶部和底部

    我正在尝试制作一个具有固定高度页眉和页脚的页面 页眉位于屏幕顶部 100 宽度 页脚位于底部 100 宽度 我想将一个具有可变高度内容的 div 居中放置在页眉和页脚之间的空间中 在下面的 jsfiddle 中 如果内容比空格短 它会起作用
  • Rails - 渲染:目标锚标记的操作?

    我希望像这样使用渲染 render action gt page form 我也尝试过这个 render template gt site page form 那也没用 这个特定页面上的表单位于最底部 如果提交时发生任何错误 我不希望用户被
  • java中void的作用是什么?

    返回类型 方法返回值的数据类型 如果方法不返回值 则返回 void http download oracle com javase tutorial java javaOO methods html http download oracle
  • 是否可以使用 Dapper 流式传输大型 SQL Server 数据库结果集?

    我需要从数据库返回大约 500K 行 请不要问为什么 然后 我需要将这些结果保存为 XML 更紧急 并将该文件通过 ftp 传输到某个神奇的地方 我还需要转换结果集中的每一行 现在 这就是我正在做的事情 TOP 100结果 使用 Dappe
  • 使用 paramiko 运行 Sudo 命令

    我正在尝试执行sudo使用 python paramiko 在远程计算机上运行命令 我尝试了这段代码 import paramiko ssh paramiko SSHClient ssh set missing host key polic
  • 对象指针值作为字典的键

    我想使用对象的引用值作为字典的键 而不是对象值的副本 因此 我本质上想在字典中存储与另一个对象的特定实例关联的对象 并稍后检索该值 这可能吗 是不是完全违背了NSDictionary的理念 我可以看出我可能以错误的方式处理这个问题 因为字典
  • 通过 Telnet 运行应用程序

    我需要创建一个 BAT 文件来通过 telnet 运行应用程序 但据我所知 在 DOS 上无法执行此操作 Telnet 不允许在连接的瞬间向远程计算机发送任何命令 并且 BAT 文件中的每个后续命令只有在 telnet 停止后才会执行 这段
  • R data.table 1.9.2 关于 setkey 的问题

    这似乎是 1 8 10 后引入的一个错误 与包含列表的 DT 的 setkey 相关 运行下面两个代码来查看问题 library data table dtl lt list dtl 1 lt data table scenario 1 p

随机推荐