AppSync: получение информации о пользователе в $ context при использовании аутентификации AWS_IAM - PullRequest
0 голосов
/ 29 апреля 2018

В AppSync, когда вы используете в качестве своей авторизации Cognito User Pools, вы получаете

identity: 
   { sub: 'bcb5cd53-315a-40df-a41b-1db02a4c1bd9',
     issuer: 'https://cognito-idp.us-west-2.amazonaws.com/us-west-2_oicu812',
     username: 'skillet',
     claims: 
      { sub: 'bcb5cd53-315a-40df-a41b-1db02a4c1bd9',
        aud: '7re1oap5fhm3ngpje9r81vgpoe',
        email_verified: true,
        event_id: 'bb65ba5d-4689-11e8-bee7-2d0da8da81ab',
        token_use: 'id',
        auth_time: 1524441800,
        iss: 'https://cognito-idp.us-west-2.amazonaws.com/us-west-2_oicu812',
        'cognito:username': 'skillet',
        exp: 1524459387,
        iat: 1524455787,
        email: 'myemail@nope.com' },
     sourceIp: [ '11.222.33.200' ],
     defaultAuthStrategy: 'ALLOW',
     groups: null }

Однако, когда вы используете AWS_IAM auth, вы получаете

identity:
{ accountId: '12121212121', //<--- my amazon account ID
  cognitoIdentityPoolId: 'us-west-2:39b1f3e4-330e-40f6-b738-266682302b59',
  cognitoIdentityId: 'us-west-2:a458498b-b1ac-46c1-9c5e-bf932bad0d95',
  sourceIp: [ '33.222.11.200' ],
  username: 'AROAJGBZT5A433EVW6O3Q:CognitoIdentityCredentials',
  userArn: 'arn:aws:sts::454227793445:assumed-role/MEMORYCARDS-CognitoAuthorizedRole-dev/CognitoIdentityCredentials',
  cognitoIdentityAuthType: 'authenticated',
  cognitoIdentityAuthProvider: '"cognito-idp.us-west-2.amazonaws.com/us-west-2_HighBob","cognito-idp.us-west-2.amazonaws.com/us-west-2_HighBob:CognitoSignIn:1a072f08-5c61-4c89-807e-417d22702eb7"' }

Документы говорят, что это ожидается, https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html. Однако, если вы используете AWS_IAM, подключенный к Cognito (который необходим для доступа без аутентификации), как вы должны получить имя пользователя, адрес электронной почты, саб и т. Д.? Мне нужен доступ к заявлениям пользователя при использовании AWS_IAM типа Auth.

Ответы [ 4 ]

0 голосов
/ 08 сентября 2018

Если вы используете AWS Amplify, то, чтобы обойти это, я установил настраиваемый заголовок username, как объяснено здесь , например:

Amplify.configure({
 API: {
   graphql_headers: async () => ({
    // 'My-Custom-Header': 'my value'
     username: 'myUsername'
   })
 }
});

тогда в моем преобразователе у меня будет доступ к заголовку с:

 $context.request.headers.username

Как объяснено в документации AppSync здесь в разделе Заголовки запроса доступа

0 голосов
/ 01 мая 2018

Вот плохой ответ, который работает. Я заметил, что cognitoIdentityAuthProvider: '"cognito-idp.us-west-2.amazonaws.com/us-west-2_HighBob","cognito-idp.us-west-2.amazonaws.com/us-west-2_HighBob:CognitoSignIn:1a072f08-5c61-4c89-807e-417d22702eb7" содержит подпрограмму пользователя Cognito (большое после CognitoSignIn). Вы можете извлечь это с помощью регулярного выражения и использовать aws-sdk для получения информации о пользователе из пула пользователей Cognito.

///////RETRIEVE THE AUTHENTICATED USER'S INFORMATION//////////
if(event.context.identity.cognitoIdentityAuthType === 'authenticated'){
    let cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
    //Extract the user's sub (ID) from one of the context indentity fields
    //the REGEX in match looks for the strings btwn 'CognitoSignIn:' and '"', which represents the user sub
    let userSub = event.context.identity.cognitoIdentityAuthProvider.match(/CognitoSignIn:(.*?)"/)[1];
    let filter = 'sub = \"'+userSub+'\"'    // string with format = 'sub = \"1a072f08-5c61-4c89-807e-417d22702eb7\"'
    let usersData = await cognitoidentityserviceprovider.listUsers( {Filter:  filter, UserPoolId: "us-west-2_KsyTKrQ2M",Limit: 1}).promise()
    event.context.identity.user=usersData.Users[0]; 

}

Это плохой ответ, потому что вы пропингуете базу данных User Pool вместо того, чтобы просто декодировать JWT.

0 голосов
/ 23 мая 2018

Вот мой ответ. В клиентской библиотеке appSync была ошибка, которая перезаписывала все пользовательские заголовки. Это было с тех пор исправлено. Теперь вы можете передавать пользовательские заголовки, которые сделают его полностью доступным для ваших распознавателей, которые я передаю своим лямбда-функциям (опять же, обратите внимание, что я использую lambda datasourcres и не использую DynamoDB).

Таким образом, я присоединяю мой зарегистрированный JWT на стороне клиента и, на стороне сервера, в своей функции лямбда, я декодирую ее. Вам нужен открытый ключ, созданный cognito для проверки JWT. (Вам НЕ НУЖЕН СЕКРЕТНЫЙ КЛЮЧ.) URL-адрес «общеизвестного ключа» связан с каждым пулом пользователей, который я проверяю при первом запуске моего lambda, но, как и мое соединение mongoDB, он сохраняется между лямбда-вызовами ( хотя бы на время.)

Вот лямбда-резольвер ...

const mongoose = require('mongoose');
const jwt = require('jsonwebtoken');
const jwkToPem = require('jwk-to-pem');
const request = require('request-promise-native');
const _ = require('lodash')

//ITEMS THAT SHOULD BE PERSISTED BETWEEN LAMBDA EXECUTIONS
let conn = null; //MONGODB CONNECTION
let pem = null;  //PROCESSED JWT PUBLIC KEY FOR OUR COGNITO USER POOL, SAME FOR EVERY USER

exports.graphqlHandler =  async (event, lambdaContext) => {
    // Make sure to add this so you can re-use `conn` between function calls.
    // See https://www.mongodb.com/blog/post/serverless-development-with-nodejs-aws-lambda-mongodb-atlas
    lambdaContext.callbackWaitsForEmptyEventLoop = false; 

    try{
        ////////////////// AUTHORIZATION/USER INFO /////////////////////////
        //ADD USER INFO, IF A LOGGED IN USER WITH VALID JWT MAKES THE REQUEST
        var token = _.get(event,'context.request.headers.jwt'); //equivalen to "token = event.context.re; quest.headers.alexauthorization;" but fails gracefully
        if(token){
            //GET THE ID OF THE PUBLIC KEY (KID) FROM THE TOKEN HEADER
            var decodedToken = jwt.decode(token, {complete: true});
            // GET THE PUBLIC KEY TO NEEDED TO VERIFY THE SIGNATURE (no private/secret key needed)
            if(!pem){ 
                await request({ //blocking, waits for public key if you don't already have it
                    uri:`https://cognito-idp.${process.env.REGION}.amazonaws.com/${process.env.USER_POOL_ID}/.well-known/jwks.json`,
                    resolveWithFullResponse: true //Otherwise only the responce body would be returned
                })
                    .then(function ( resp) {
                        if(resp.statusCode != 200){
                            throw new Error(resp.statusCode,`Request of JWT key with unexpected statusCode: expecting 200, received ${resp.statusCode}`);
                        }
                        let {body} = resp; //GET THE REPSONCE BODY
                        body = JSON.parse(body);  //body is a string, convert it to JSON
                        // body is an array of more than one JW keys.  User the key id in the JWT header to select the correct key object
                        var keyObject = _.find(body.keys,{"kid":decodedToken.header.kid});
                        pem = jwkToPem(keyObject);//convert jwk to pem
                    });
            }
            //VERIFY THE JWT SIGNATURE. IF THE SIGNATURE IS VALID, THEN ADD THE JWT TO THE IDENTITY OBJECT.
            jwt.verify(token, pem, function(error, decoded) {//not async
                if(error){
                    console.error(error);
                    throw new Error(401,error);
                }
                event.context.identity.user=decoded;
            });
        }
        return run(event)
    } catch (error) {//catch all errors and return them in an orderly manner
        console.error(error);
        throw new Error(error);
    }
};

//async/await keywords used for asynchronous calls to prevent lambda function from returning before mongodb interactions return
async function run(event) {
    // `conn` is in the global scope, Lambda may retain it between function calls thanks to `callbackWaitsForEmptyEventLoop`.
    if (conn == null) {
        //connect asyncoronously to mongodb
        conn = await mongoose.createConnection(process.env.MONGO_URL);
        //define the mongoose Schema
        let mySchema = new mongoose.Schema({ 
            ///my mongoose schem
        }); 
        mySchema('toJSON', { virtuals: true }); //will include both id and _id
        conn.model('mySchema', mySchema );  
    }
    //Get the mongoose Model from the Schema
    let mod = conn.model('mySchema');
    switch(event.field) {
        case "getOne": {
            return mod.findById(event.context.arguments.id);
        }   break;
        case "getAll": {
            return mod.find()
        }   break;
        default: {
            throw new Error ("Lambda handler error: Unknown field, unable to resolve " + event.field);
        }   break;
    }           
}

Это ПУТЬ лучше, чем мой другой "плохой" ответ, потому что вы не всегда запрашиваете БД для получения информации, которая у вас уже есть на стороне клиента. Примерно в 3 раза быстрее в моем опыте.

0 голосов
/ 01 мая 2018

Чтобы сделать имя пользователя, электронную почту, подписку и т. Д. Доступными через API AppSync, для этого есть ответ: https://stackoverflow.com/a/42405528/1207523

Подводя итог, вы хотите отправить токен ID пула пользователей в свой API (например, AppSync или API Gateway). Ваш запрос API аутентифицирован IAM. Затем вы проверяете токен идентификатора в лямбда-функции, и теперь у вас есть проверенный пользователь IAM и данные пользовательских пулов вместе.

Вы хотите использовать IAM identity.cognitoIdentityId в качестве первичного ключа для своей таблицы User. Добавьте данные, включенные в идентификационный токен (имя пользователя, адрес электронной почты и т. Д.), В качестве атрибутов.

Таким образом, вы можете сделать заявки пользователей доступными через ваш API. Теперь, например, вы можете установить $ctx.identity.cognitoIdentityId как владельца предмета. Тогда, возможно, другие пользователи смогут увидеть имя владельца через распознаватели GraphQL.

Если вам нужен доступ к утверждениям пользователя в вашем преобразователе, я боюсь, что в настоящий момент это невозможно. Я задал вопрос об этом, так как это было бы очень полезно для авторизации: Групповая авторизация в AppSync с использованием аутентификации IAM

В этом случае вместо использования резольвера вы можете использовать Lambda в качестве источника данных и получать утверждения пользователя из вышеупомянутой таблицы User.

Сейчас все немного сложно:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...