使用Cognito的AWS Lambda API网关 - 如何使用IdentityId访问和更新UserPool属性?

时间:2017-02-22 08:24:39

标签: aws-lambda aws-api-gateway amazon-cognito

好的我现在已经进入了这个阶段并取得了重大进展,但我仍然对基本面感到十分困惑。

我的应用程序使用Cognito用户池来创建和管理用户 - 这些在S3上通过IdentityId标识。我的每个用户都有自己的S3文件夹,AWS会自动为他们提供一个与用户的IdentityId相等的文件夹名称。

我需要将IdentityId与其他Cognito用户信息相关联,但无法确定如何使用。

我需要的关键是能够为给定的IdentityId识别用户名和其他认知用户属性 - 而且它非常难。

因此,第一场战斗是在Cognito用户通过AWS API Gateway发出请求时,弄清楚如何获取IdentityId。最后我得到了解决方案,现在我有一个Cognito用户,他向API网关发出请求,而我后面的Lambda函数现在有了IdentityId。这一点有效。

但我完全不知道如何访问存储在用户池中的Cognito用户信息。我找不到任何明确的信息,当然也没有代码,它们显示了如何使用IdentityId来获取Cognito用户的属性,用户名等。

如果我使用" Cognito用户池"为了在API网关中授权我的方法,然后可以使用正文映射模板将Cognito User信息(如sub,用户名和电子邮件地址)放入上下文中,但我没有获得IdentityId。

但是如果我使用AWS_IAM在API网关中授权我的方法,那么身体映射模板会反过来 - 它为我提供了IdentityId,但没有提供Cognito用户字段,例如sub和username以及email。 / p>

这让我发疯了 - 如何将IdentityId和所有Cognito用户字段和属性合并为一个数据结构?事实上,我似乎只能得到一个或另一个是没有意义的。

3 个答案:

答案 0 :(得分:32)

事实证明,要使用AWS Lambda / Cognito / API Gateway同时获取IdentityId AND用户详细信息,您需要使用COGNITO_USER_POOLS(不是use strict'; let util = require('util'); var jwt = require('jsonwebtoken'); var jwkToPem = require('jwk-to-pem'); var userPoolId = 'YOUR USERPOOL ID'; var region = 'YOUR REGION'; //e.g. us-east-1 var iss = 'https://cognito-idp.' + region + '.amazonaws.com/' + userPoolId; //https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html // DOWNLOAD FROM https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json let userPoolKeys = {PUT YOUR DOWNLOADED USER POOL KEYS JSON HERE}; var pems = {}; let convertKeysToPems = () => { var keys = userPoolKeys['keys']; for(var i = 0; i < keys.length; i++) { //Convert each key to PEM var key_id = keys[i].kid; var modulus = keys[i].n; var exponent = keys[i].e; var key_type = keys[i].kty; var jwk = { kty: key_type, n: modulus, e: exponent}; var pem = jwkToPem(jwk); pems[key_id] = pem; } } exports.handler = function(event, context) { convertKeysToPems() console.log(event); let token = event['body-json'].cognitoUserToken; console.log(event['body-json'].cognitoUserToken); ValidateToken(pems, event, context, token); }; let ValidateToken = (pems, event, context, token) => { //Fail if the token is not jwt var decodedJwt = jwt.decode(token, {complete: true}); console.log(decodedJwt) if (!decodedJwt) { console.log("Not a valid JWT token"); context.fail("Unauthorized"); return; } //Fail if token is not from your UserPool if (decodedJwt.payload.iss != iss) { console.log("invalid issuer"); context.fail("Unauthorized"); return; } //Reject the jwt if it's not an 'Access Token' if (decodedJwt.payload.token_use != 'id') { console.log("Not an id token"); context.fail("Unauthorized"); return; } //Get the kid from the token and retrieve corresponding PEM var kid = decodedJwt.header.kid; var pem = pems[kid]; if (!pem) { console.log(pems, 'pems'); console.log(kid, 'kid'); console.log('Invalid token'); context.fail("Unauthorized"); return; } //Verify the signature of the JWT token to ensure it's really coming from your User Pool jwt.verify(token, pem, { issuer: iss }, function(err, payload) { if(err) { context.fail("Unauthorized"); } else { let x = decodedJwt.payload x.identityId = context.identity.cognitoIdentityId //let x = {'identityId': context['cognito-identity-id'], 'decodedJwt': decodedJwt} console.log(x); context.succeed(x); } }); } 进行身份验证的Lambda函数),你必须发送你的请求AWS API网关,但它必须是一个签名的请求,你必须修改集成请求体映射模板,以便在事件中给你IdentityId(也许上下文?不记得了) 。现在你有了IdentityId。唷。现在,您必须从前端到后端提交客户端的Cognito ID令牌。验证令牌非常重要 - 如果您不验证令牌,则无法相信它没有被篡改。要解码和验证令牌,您必须从用户池获取密钥,将它们放入脚本中,确保您的AWS lambda zipfile中包含jwt解码库和签名验证库。现在,您的脚本必须验证从前端提交的令牌,然后您可以从令牌中获取用户详细信息。瞧!现在,您同时拥有IdentityId以及用户详细信息,例如子信息,用户名和电子邮件地址。太容易了。

以上是使用AWS Cognito / Lambda / API Gateway获取与IdentityId关联的用户名所需的操作。这花了我几天才能开始工作。

我可以向任何徘徊在这里的亚马逊员工说........很难获得与IdentityId相关的用户详细信息。你需要解决这个问题。让我感到愤怒的是,这太累了,我的时间太多了。

解决方案:

我是通过修改亚马逊员工自定义授权程序来完成的: https://s3.amazonaws.com/cup-resources/cup_custom_authorizer_lambda_function_blueprint.zip

在此处找到并描述: https://aws.amazon.com/blogs/mobile/integrating-amazon-cognito-user-pools-with-api-gateway/

{{1}}

答案 1 :(得分:1)

这个问题——在 S3 路径中使用用户的 sub 而不是他们的 identityId 的问题以及如何设置它——多亏了 @JesseDavda 的帮助,我 100% 解决了此问题中此问题的解决方案:https://github.com/aws-amplify/amplify-js/issues/54

对于一直试图在 lambdas 中获取 identityId 以便您在 S3 中的 Amplify 默认路径工作的所有开发人员 - 此解决方案最终只是完全忽略了 identityId - 这是一个解决方案根据 sub 而不是 identityId 在 S3 中设置路径。在此解决方案结束时,您将永远不必为您的用户处理多个 id,您将永远不必再次处理 identityId(希望如此)。

答案 2 :(得分:-1)

如果我正确理解了这一点,则希望将CognitoIdentityId和User属性放在同一位置。我们的操作方式如下:

从事件请求上下文中,我们获得IdentityId: event.requestContext.identity.cognitoIdentityId

也可以从请求上下文中获取用户的子: event.requestContext.identity.cognitoAuthenticationProvider.split(':CognitoSignIn:')[1]

然后通过子项,您可以通过以下方式请求其余属性:

  const AWS = require('aws-sdk');
  let cognito = new AWS.CognitoIdentityServiceProvider();
  let request = {
    Username: userSub,
    UserPoolId: process.env.userPoolId,
  };
  let result = await cognito.adminGetUser(request).promise();

  const userAttributes = result.UserAttributes.reduce((acc, attribute) => {
    const { Name, Value } = attribute;
    acc[Name] = Value;
    return acc;
  }, {});

  return userAttributes;