简体   繁体   English

AWS + 无服务器 - 如何获取 Cognito 用户池生成的密钥

[英]AWS + Serverless - how to get at the secret key generated by cognito user pool

I've been following the serverless tutorial at https://serverless-stack.com/chapters/configure-cognito-user-pool-in-serverless.html我一直在关注https 上的无服务器教程://serverless-stack.com/chapters/configure-cognito-user-pool-in-serverless.html

I've got the following serverless yaml snippit我有以下无服务器 yaml 片段

Resources:
  CognitoUserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      # Generate a name based on the stage
      UserPoolName: ${self:custom.stage}-moochless-user-pool
      # Set email as an alias
      UsernameAttributes:
      - email
      AutoVerifiedAttributes:
      - email

  CognitoUserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      # Generate an app client name based on the stage
      ClientName: ${self:custom.stage}-user-pool-client
      UserPoolId:
        Ref: CognitoUserPool
      ExplicitAuthFlows:
      - ADMIN_NO_SRP_AUTH
      # >>>>> HOW DO I GET THIS VALUE IN OUTPUT <<<<<
      GenerateSecret: true

# Print out the Id of the User Pool that is created
Outputs:
  UserPoolId:
    Value:
      Ref: CognitoUserPool

  UserPoolClientId:
    Value:
      Ref: CognitoUserPoolClient
  #UserPoolSecret:
  #   WHAT GOES HERE?

I'm exporting all my other config variables to a json file (to be consumed by a mobile app, so I need the secret key).我将所有其他配置变量导出到 json 文件(由移动应用程序使用,因此我需要密钥)。

How do I get the secret key generated to appear in my output list?如何让生成的密钥出现在我的 output 列表中?

The ideal way to retrieve the secret key is to use "CognitoUserPoolClient.ClientSecret" in your cloudformation template. 检索密钥的理想方法是在您的cloudformation模板中使用“CognitoUserPoolClient.ClientSecret”。

UserPoolClientIdSecret:
  Value:    
   !GetAtt CognitoUserPoolClient.ClientSecret

But it is not supported as explained here and gives message as shown in the image: 但它不支持,如此处所述并提供如图所示的消息: 不支持 You can run below CLI command to retrieve the secret key as a work around: 您可以在CLI命令下运行以检索密钥作为解决方法:

aws cognito-idp describe-user-pool-client --user-pool-id "us-west-XXXXXX"  --region us-west-2 --client-id "XXXXXXXXXXXXX" --query 'UserPoolClient.ClientSecret' --output text

As Prabhakar Reddy points out, currently you can't get the Cognito client secret using !GetAtt in your CloudFormation template.正如 Prabhakar Reddy 指出的那样,目前您无法在 CloudFormation 模板中使用!GetAtt获取 Cognito 客户端机密。 However, there is a way to avoid the manual step of using the AWS command line to get the secret.但是,有一种方法可以避免使用 AWS 命令行获取密钥的手动步骤。 The AWS Command Runner utility for CloudFormation allows you to run AWS CLI commands from your CloudFormation templates, so you can run the CLI command to get the secret in the CloudFormation template and then use the output of the command elsewhere in your template using !GetAtt .适用于 CloudFormation 的 AWS Command Runner实用程序允许您从 CloudFormation 模板运行 AWS CLI 命令,因此您可以运行 CLI 命令以获取 CloudFormation 模板中的密钥,然后使用!GetAtt Basically CommandRunner spins up an EC2 instance and runs the command you specify and saves the output of the command to a file on the instance while the CloudFormation template is running so that it can be retrieved later using !GetAtt .基本上,CommandRunner 启动一个 EC2 实例并运行您指定的命令,并在 CloudFormation 模板运行时将命令的 output 保存到实例上的一个文件中,以便稍后可以使用!GetAtt检索它。 Note that CommandRunner is a special custom CloudFormation type that needs to be installed for the AWS account as a separate step.请注意,CommandRunner 是一种特殊的自定义 CloudFormation 类型,需要作为单独的步骤为 AWS 帐户安装 Below is an example CloudFormation template that will get a Cognito client secret and save it to AWS Secrets manager.下面是一个示例 CloudFormation 模板,它将获取 Cognito 客户端密钥并将其保存到 AWS Secrets Manager。

Resources:

  CommandRunnerRole:
    Type: AWS::IAM::Role
    Properties:
      # the AssumeRolePolicyDocument specifies which services can assume this role, for CommandRunner this needs to be ec2
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action: 'sts:AssumeRole'
      Path: /
      Policies:
        - PolicyName: CommandRunnerPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:CancelUploadArchive'
                  - 'logs:GetBranch'
                  - 'logs:GetCommit'
                  - 'cognito-idp:*'
                Resource: '*'

  CommandRunnerInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref CommandRunnerRole

  GetCognitoClientSecretCommand:
    Type: AWSUtility::CloudFormation::CommandRunner
    Properties:
      Command: aws cognito-idp describe-user-pool-client --user-pool-id <user_pool_id> --region us-east-2 --client-id <client_id> --query UserPoolClient.ClientSecret --output text > /command-output.txt
      Role: !Ref CommandRunnerInstanceProfile
      InstanceType: "t2.nano"
      LogGroup: command-runner-logs

  CognitoClientSecret:
    Type: AWS::SecretsManager::Secret
    DependsOn: GetCognitoClientSecretCommand
    Properties:
      Name: "command-runner-secret"
      SecretString: !GetAtt GetCognitoClientSecretCommand.Output

Note that you will need to replace the <user_pool_id> and <client_id> with your user pool and client pool id.请注意,您需要将<user_pool_id><client_id>替换为您的用户池和客户端池 ID。 A complete CloudFormation template would likely create the Cognito User Pool and User Pool Client and the user pool & client id values could be retrieved from those resources using !Ref as part of a !Join statement that creates the entire command, eg完整的 CloudFormation 模板可能会创建 Cognito 用户池和用户池客户端,并且可以使用!Ref作为创建整个命令的!Join语句的一部分从这些资源中检索用户池和客户端 ID 值,例如

Command: !Join [' ', ['aws cognito-idp describe-user-pool-client --user-pool-id', !Ref CognitoUserPool, '--region', !Ref AWS::Region, '--client-id',  !Ref CognitoUserPoolClient, '--query UserPoolClient.ClientSecret --output text > /command-output.txt']]

One final note, depending on your operating system, the installation/registration of CommandRunner may fail trying to create the S3 bucket it needs.最后请注意,根据您的操作系统,CommandRunner 的安装/注册可能无法尝试创建所需的 S3 存储桶。 This is because it tries to generate a bucket name using uuidgen and will fail if uuidgen isn't installed.这是因为它会尝试使用uuidgen uuidgen失败。 I have opened an issue on the CommandRunner GitHub repo for this.为此,我在 CommandRunner GitHub 回购协议上开了一个问题 Until the issue is resolved, you can get around this by modifying the /scripts/register.sh script to use a static bucket name.在问题解决之前,您可以通过修改/scripts/register.sh脚本以使用 static 存储桶名称来解决此问题。

As it is still not possible to get the secret of a Cognito User Pool Client using !GetAtt in a CloudFormation Template I was looking for an alternative solution without manual steps so the infrastructure can get deployed automatically.由于仍然无法在 CloudFormation 模板中使用!GetAtt获取 Cognito 用户池客户端的秘密,因此我一直在寻找无需手动步骤的替代解决方案,以便可以自动部署基础设施。

I like clav's solution but it requires the Command Runner to be installed first.我喜欢 clav 的解决方案,但它需要先安装Command Runner

So, what I did in the end was using a Lambda-backed custom resource .所以,我最后所做的是使用Lambda 支持的自定义资源 I wrote it in JavaScript but you can also write it in Python.我写在JavaScript,你也可以写在Python。

Here is an overview of the 3 steps you need to follow:以下是您需要遵循的 3 个步骤的概述:

  1. Create IAM Policy and add it to the Lambda function execution role .创建IAM 策略并将其添加到Lambda function 执行角色
  2. Add creation of In-Line Lambda function to CloudFormation Template.In-Line Lambda function的创建添加到 CloudFormation 模板。
  3. Add creation of Lambda-backed custom resource to CloudFormation Template.Lambda 支持的自定义资源的创建添加到 CloudFormation 模板。
  4. Get the output from the custom Ressource via !GetAtt通过!GetAtt

And here are the details:以下是详细信息:

  1. Create IAM Policy and add it to the Lambda function execution role .创建IAM 策略并将其添加到Lambda function 执行角色
  # IAM: Policy to describe user pool clients of Cognito user pools
  CognitoDescribeUserPoolClientsPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties: 
      Description: 'Allows describing Cognito user pool clients.'
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - 'cognito-idp:DescribeUserPoolClient'
            Resource: 
              - !Sub 'arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/*'

If necessary only allow it for certain resources.如有必要,只允许它用于某些资源。

  1. Add creation of In-Line Lambda function to CloudFormation Template.In-Line Lambda function的创建添加到 CloudFormation 模板。
  # Lambda: Function to get the secret of a Cognito User Pool Client
  LambdaFunctionGetCognitoUserPoolClientSecret:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: 'GetCognitoUserPoolClientSecret'
      Description: 'Lambda function to get the secret of a Cognito User Pool Client.'
      Handler: index.lambda_handler
      Role: !Ref LambdaFunctionExecutionRoleArn
      Runtime: nodejs14.x
      Timeout: '30'
      Code:
        ZipFile: |
          // Import required modules
          const response = require('cfn-response');
          const { CognitoIdentityServiceProvider } = require('aws-sdk');

          // FUNCTION: Lambda Handler
          exports.lambda_handler = function(event, context) {
            console.log("Request received:\n" + JSON.stringify(event));

            // Read data from input parameters
            let userPoolId = event.ResourceProperties.UserPoolId;
            let userPoolClientId = event.ResourceProperties.UserPoolClientId;

            // Set physical ID
            let physicalId = `${userPoolId}-${userPoolClientId}-secret`;

            let errorMessage = `Error at getting secret from cognito user pool client:`;
            try {
              let requestType = event.RequestType;
              if(requestType === 'Create') {
                console.log(`Request is of type '${requestType}'. Get secret from cognito user pool client.`);

                // Get secret from cognito user pool client
                let cognitoIdp = new CognitoIdentityServiceProvider();
                cognitoIdp.describeUserPoolClient({
                  UserPoolId: userPoolId,
                  ClientId: userPoolClientId
                }).promise()
                .then(result => {
                  let secret = result.UserPoolClient.ClientSecret;
                  response.send(event, context, response.SUCCESS, {Status: response.SUCCESS, Error: 'No Error', Secret: secret}, physicalId);
                }).catch(error => {
                  // Error
                  console.log(`${errorMessage}:${error}`);
                  response.send(event, context, response.FAILED, {Status: response.FAILED, Error: error}, physicalId);
                });

              } else {
                console.log(`Request is of type '${requestType}'. Not doing anything.`);
                response.send(event, context, response.SUCCESS, {Status: response.SUCCESS, Error: 'No Error'}, physicalId);
              }
            } catch (error){
                // Error
                console.log(`${errorMessage}:${error}`);
                response.send(event, context, response.FAILED, {Status: response.FAILED, Error: error}, physicalId); 
            }
          };

Make sure you pass the right Lambda Execution Role to the parameter Role .确保将正确的 Lambda Execution Role 传递给参数Role It should contain the policy created in step 1.它应该包含在步骤 1 中创建的策略。

  1. Add creation of Lambda-backed custom resource to CloudFormation Template.Lambda 支持的自定义资源的创建添加到 CloudFormation 模板。
  # Custom: Cognito user pool client secret
  UserPoolClientSecret:
    Type: Custom::UserPoolClientSecret
    Properties:
      ServiceToken: !Ref LambdaFunctionGetCognitoUserPoolClientSecret
      UserPoolId: !Ref UserPool
      UserPoolClientId: !Ref UserPoolClient

Make sure you pass the Lambda function created in step 2 as ServiceToken .确保将步骤 2 中创建的 Lambda function 作为ServiceToken传递。 Also make sure you pass in the right values for the parameters UserPoolId and UserPoolClientId .还要确保为参数UserPoolIdUserPoolClientId传递正确的值。 They should be taken from the Cognito User Pool and the Cognito User Pool Client.它们应该取自 Cognito 用户池和 Cognito 用户池客户端。

  1. Get the output from the custom Ressource via !GetAtt通过!GetAtt
!GetAtt UserPoolClientSecret.Secret

You can do this anywhere you want.您可以在任何地方执行此操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从 AWS Cognito 上的托管 UI 获取用户池令牌 - How to get the User pool token from Hosted UI on AWS Cognito AWS Cognito 用户池:给定一个应用程序客户端 ID,如何获取其池 ID? - AWS Cognito User Pool : Given an App client id, how to get its Pool Id? 当配置无服务器框架并且用户使用 AWS Amplify 注册时,AWS Cognito 用户池不发送验证 email - AWS Cognito user pool does not send verification email when configured with Serverless framework and user signs up with AWS Amplify 如何获取 Flutter 中的 AWS Cognito 用户组? - How to get the AWS Cognito User group in Flutter? 如何以编程方式为 Java 中 AWS Cognito 用户池中的登录用户启用或禁用 MFA? - How to programmatically enable or disable MFA for a logged user in AWS Cognito user pool in Java? 如何在 Cognito 用户池组中添加用户? - How to add a user in Cognito User Pool group? 如何在创建 AWS Cognito 用户池时更改 phone_number 格式? - How to change phone_number format upon creation of a AWS Cognito User Pool? AWS Cognito 从用户池中删除所有用户 - AWS Cognito delete all users from a user pool AWS Cognito 用户池检查是否存在具有特定 Email 的用户 - AWS Cognito User Pool check if there is a user with a specific Email 如何禁用通过身份提供商创建的 AWS Cognito 用户池帐户? - How to disable AWS Cognito User Pool account created via Identity Provider?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM