web-dev-qa-db-fra.com

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

OK, j'en suis maintenant à quelques jours et j'ai fait des progrès significatifs mais je suis toujours complètement perplexe quant aux fondamentaux.

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

J'ai besoin de relier l'IdentityId aux autres informations utilisateur de Cognito, mais je ne sais pas comment.

La chose clé dont j'ai besoin est de pouvoir identifier le nom d'utilisateur ainsi que d'autres attributs utilisateur cognito pour un IdentityId donné - et c'est incroyablement difficile.

La première bataille a donc consisté à déterminer comment obtenir l'IdentityId lorsqu'un utilisateur Cognito effectue une demande via AWS API Gateway. Enfin, j'ai compris, 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. Ça marche.

Mais je suis complètement perplexe quant à la façon d'accéder maintenant aux informations de l'utilisateur Cognito qui sont stockées dans le pool d'utilisateurs. Je ne trouve aucune information claire, et certainement pas de code, qui montre comment utiliser l'IdentityId pour obtenir les attributs, le nom d'utilisateur, etc. de Cognito.

Il semble que si j'utilise un "pool d'utilisateurs Cognito" pour autoriser ma méthode dans API Gateway, le modèle de mappage corporel peut être utilisé pour placer les informations utilisateur Cognito telles que le sous-utilisateur et le nom d'utilisateur et l'adresse e-mail dans le contexte, MAIS je le fais PAS obtenir l'IdentityId.

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

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

32
Duke Dougal

Il s'avère que pour obtenir en même temps les identifiants IdentityId ET utilisateur à l'aide d'AWS Lambda/Cognito/API Gateway, vous devez disposer d'une fonction Lambda authentifiée à l'aide de AWS_IAM (NE PAS COGNITO_USER_POOLS), vous devez envoyer votre demande à l'AWS API Gateway, MAIS cela DOIT être une demande signée, vous devez ensuite modifier les modèles de mappage du corps de la demande d'intégration afin que vous obteniez l'IdentityId dans l'événement (peut-être le contexte? ne peut pas rappelles toi). Vous avez maintenant l'IdentityId. Phew. Vous devez maintenant soumettre le jeton Cognito ID du client de l'avant vers l'arrière. Il est important de valider le jeton - vous ne pouvez pas croire 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 pool d'utilisateurs, les mettre dans votre script, vous assurer que vous disposez de bibliothèques de décodage jwt ainsi que de 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, puis vous pouvez extraire les détails de l'utilisateur du jeton. Voila! Maintenant, vous avez à la fois IdentityId plus les détails de l'utilisateur tels que leur sous, nom d'utilisateur et adresse e-mail. Si facile.

Ce qui précède est ce qui est nécessaire pour obtenir le nom d'utilisateur associé à un IdentityId à l'aide d'AWS Cognito/Lambda/API Gateway. Cela m'a pris des jours pour me mettre au travail.

Puis-je s'il vous plaît dire à tous les employés d'Amazon qui se promènent à travers cela ... Eh bien, il est bien trop difficile d'obtenir les détails de l'utilisateur associés à un IdentityId. Vous devez résoudre ce problème. Cela me mettait en colère que cela soit si dur et brûle tellement de mon temps.

La solution:

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

comme 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);
      }
    });
}
36
Duke Dougal