41 votes

Passerelle API AWS Lambda avec Cognito - comment utiliser IdentityId pour accéder et mettre à jour les attributs UserPool ?

J'ai fait des progrès significatifs, mais je suis encore complètement perdue sur les principes de base.

Mon application utilise des pools d'utilisateurs Cognito pour créer et gérer les utilisateurs - ceux-ci sont identifiés sur S3, semble-t-il, par leur IdentityId. Chacun de mes utilisateurs a son propre dossier S3, et AWS leur donne automatiquement un nom de dossier qui est égal à l'IdentityId de l'utilisateur.

J'ai besoin de relier l'IdentityId aux autres informations de l'utilisateur Cognito, mais je n'arrive pas à trouver comment.

La chose essentielle dont j'ai besoin est d'être capable d'identifier le nom d'utilisateur et les autres attributs de l'utilisateur de Cognito pour un IdentityId donné - et c'est incroyablement difficile.

La première bataille a donc été de trouver comment obtenir l'IdentityId lorsqu'un utilisateur de Cognito fait une demande via la passerelle API AWS. Finalement, j'ai réussi à résoudre ce problème, et maintenant j'ai un utilisateur Cognito, qui fait une demande à la passerelle API, et ma fonction Lambda derrière cela a maintenant l'IdentityId. Cette partie fonctionne.

Mais je ne sais pas du tout comment accéder aux informations de l'utilisateur Cognito qui sont stockées dans le pool d'utilisateurs. Je ne trouve aucune information claire, et certainement aucun code, qui montre comment utiliser l'IdentityId pour obtenir les attributs de l'utilisateur Cognito, son nom d'utilisateur, etc.

Il semble que si j'utilise un "pool d'utilisateurs Cognito" pour autoriser ma méthode dans API Gateway, alors le modèle de mappage du corps peut être utilisé pour mettre les informations de l'utilisateur Cognito telles que le sous, le nom d'utilisateur et l'adresse électronique dans le contexte, MAIS je n'obtiens PAS l'IdentityId.

MAIS si j'utilise le AWS_IAM pour autoriser ma méthode dans la passerelle API, le modèle de mappage du corps fait l'inverse - il me donne l'IdentityId mais pas les champs de l'utilisateur Cognito tels que sub, username et email.

Cela me rend fou - comment puis-je rassembler l'IdentityId et tous les champs et attributs des utilisateurs de Cognito dans une seule structure de données ? Le fait que je ne puisse obtenir que l'un ou l'autre n'a aucun sens.

46voto

Duke Dougal Points 932

Il s'avère que pour obtenir l'IdentityId ET les détails de l'utilisateur en même temps en utilisant AWS Lambda/Cognito/API Gateway, vous devez avoir une fonction Lambda qui est authentifiée à l'aide des éléments suivants AWS_IAM (PAS COGNITO_USER_POOLS ), vous devez envoyer votre demande à la passerelle API AWS, MAIS il DOIT s'agir d'une demande signée, vous devez ensuite modifier les modèles de mappage du corps de la demande d'intégration de manière à ce que l'IdentityId vous soit fourni dans l'événement (peut-être le contexte ? je ne me souviens plus). Maintenant vous avez l'IdentityId. Ouf. Vous devez maintenant soumettre le jeton d'identification Cognito du client du front-end au back-end. Il est important de valider le jeton - vous ne pouvez pas être sûr qu'il n'a pas été falsifié si vous ne le validez pas. Pour décoder et valider le jeton, vous devez obtenir les clés de votre userpool, les mettre dans votre script, vous assurer que vous avez les bibliothèques de décodage jwt plus les bibliothèques de validation de signature incluses dans votre fichier zip AWS lambda. Maintenant, votre script doit valider le jeton soumis depuis le front-end et ensuite vous pouvez obtenir les détails de l'utilisateur à partir du jeton. Voilà ! Maintenant, vous avez à la fois IdentityId et les détails de l'utilisateur tels que son sous, son nom d'utilisateur et son adresse e-mail. C'est si simple.

Ce qui précède est ce qu'il faut pour obtenir le nom d'utilisateur associé à un IdentityId en utilisant AWS Cognito/Lambda/API Gateway. Il m'a fallu plusieurs jours pour que cela fonctionne.

Puis-je dire à tout employé d'Amazon qui tomberait sur ce site ........ qu'il est BEAUCOUP trop difficile d'obtenir les détails de l'utilisateur associés à un IdentityId. Vous devez corriger cela. Cela m'a mis en colère que ce soit si difficile et que cela me fasse perdre autant de temps.

La solution :

J'ai fait cela en modifiant un authorizer personnalisé des employés d'Amazon ici : https://s3.amazonaws.com/cup-resources/cup_custom_authorizer_lambda_function_blueprint.zip

tel que trouvé et décrit ici : https://aws.amazon.com/blogs/mobile/integrating-amazon-cognito-user-pools-with-api-gateway/

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

2 votes

Je suis d'accord avec vous sur la difficulté, avez-vous eu l'occasion d'essayer de créer des politiques IAM S3 qui correspondent au sous-ensemble fourni ?

3 votes

Il est absolument scandaleux qu'AWS ait rendu les choses si difficiles et n'ait toujours pas apporté de véritable solution au problème.

3 votes

Je cherche encore ceci

2voto

mountainbot Points 21

Ce problème -- le problème de l'utilisation de l'utilisateur sub au lieu de leur identityId dans les chemins S3 et comment le configurer -- est 100% résolu pour moi grâce à l'aide de la solution de @JesseDavda à ce problème dans ce numéro : https://github.com/aws-amplify/amplify-js/issues/54

Pour tous les développeurs qui ont essayé d'obtenir l'accès à l'Internet. identityId dans les lambdas afin que les chemins par défaut d'Amplify dans S3 fonctionnent - cette solution finit simplement par ignorer identityId Il s'agit d'une solution qui configure les chemins d'accès dans S3 en se basant sur sub au lieu de la identityId . A la fin de cette solution, vous n'aurez plus à vous occuper de plus d'un identifiant pour vos utilisateurs, vous n'aurez plus à vous occuper de identityId (espérons-le) plus jamais.

-4voto

8vius Points 3076

Si je comprends bien, vous voulez que les CognitoIdentityId et les attributs de l'utilisateur au même endroit. Nous procédons de la manière suivante :

A partir du contexte de la demande d'événement, nous obtenons l'IdentityId : event.requestContext.identity.cognitoIdentityId

Le contexte de la demande nous permet également d'obtenir le pseudo de l'utilisateur : event.requestContext.identity.cognitoAuthenticationProvider.split(':CognitoSignIn:')[1]

Ensuite, avec le sous-marin, vous pouvez demander le reste des attributs de la manière suivante :

  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;

0 votes

UserName n'est pas le même que sous . J'ai trouvé cette réponse qui fonctionne - stackoverflow.com/a/47968938/5702727 .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X