Статьи

Недопустимый токен доступа к пулу пользователей AWS Cognito

AWS Cognito является одним из наиболее комплексных сервисов управления пользователями и сеансами в облаке AWS. Как и другие сервисы, он имеет широкий спектр интеграции с другими сервисами AWS. Cognito разделен на две основные подуслуги:

  • Пулы пользователей: где вы управляете собственной базой пользователей (управление пользователями включает регистрацию пользователей, двухфакторную аутентификацию, забытый пароль и т. Д.)

  • Федеративная идентификация. Вы интегрируете сторонних поставщиков идентификационных данных в свой пул пользователей.

В этой статье рассматривается конкретная проблема в пулах пользователей на этапе выхода из системы. Во-первых, давайте смоделируем базовый поток аутентификации пользователя.

Название изображения

    Когда уже существующий пользователь хочет войти в систему, чтобы получить какие-то ключи, специфичные для сеанса, ему / ей необходимо выполнить   API-вызовы initiate-auth  или admin-initiate-auth, чтобы получить следующие токены:

{
    "AuthenticationResult": {
        "ExpiresIn": 3600, 
        "IdToken": "eyJraWQiOiJDTTZ2SGtqM3BHdnBNcnpac3o2ekxXXXXXXxXXC9MSGxwSnA3WT0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIwMDA3ZWU4Ny03ZjhiLTQ4YzktYTVlZS1kN2U3ZDNiNGVhNDIiLCJhdWQiOiIxZTBlb2M3bmI0bnIzdGNjaGcwN2dqM2ZqYyIsImV2ZW50X2lkIjoiZmUzZDFiYjAtMGM0ZS0xMWU4LWIxNDUtNDk0YzkyYTVkMzdkIiwidG9rZW5fdXNlIjoiaWQiLCJhdXRoX3RpbWUiOjE1MTgwMzk0MDQsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5ldS1jZW50cmFsLTEuYW1hem9uYXdzLmNvbVwvZXUtY2VudHJhbC0xX2JsN2tmTll4UyIsInBob25lX251bWJlcl92ZXJpZmllZCI6dHJ1ZSwiY29nbml0bzp1c2VybmFtZSI6IjAwMDdlZTg3LTdmOGItNDhjOS1hNWVlLWQ3ZTdkM2I0ZWE0MiIsInBob25lX251bWJlciI6IiszMTYxMTUzNDA0OCIsImV4cCI6MTUxODA0MzAwNCwiaWF0IjoxNTE4MDM5NDA0fQ.OTuZOOO0dBKiq-ubZYZGoUUK_x8Pcl6P90c9lQeL263b-JrDnS-jbx2yi6uhZnvNNDuGpkjTrWvggs9_UH5qm0oWxvl4VqDB94h1G027rdbg60S0vdG3pIil6W71a8s16qHJTmaSicE2-xc7YMw6kNUtBG4_sWR_UVvfGn7G2eFwEx0gxDjOCSVa1XIuONW4saBrOEw0AHEL67BSCbVEMUdIDQpOts_5_I4WA0n9sf0HbYU_brQ849JHAjrQuWDM9IPKv5NlB8Wmx4Ra4YdTrBoGrF_TPLmWrswutKj8BSt_mPLBEQ6ZqnZtYAMoJvyzT7KDRc3hnKIsLeWbFyP7Lg", 
        "RefreshToken": "eyJjdHkiOiJKV1QiLCJlXXXXXXXXwiYWxnIjoiUlNBLU9BRVAifQ.2U-2MfLRPHriV5QsCdCOzYWvMht4qXQ-gcaJ-FmLS7yE1xwSErEuWST08IjZj2f3CwIzTVHgvQho_n1aaIkqDbuM_w5kJ7V5gy-4I5WGTlYjliZsBjLy9aRujhG4YfdIxI0OSD8APa74pUrV9WtLDRjXS2eyGBg3mhaEABEbHLYpEkuogbrNF37cO9UyfTWRtirWpJEeLYqS5psOSAQxOWNpEO8DxO_nbPg8cwMuSAGDeWsR5rvPi56Kq0fuhK3NzZDtSfQNZVdChGJfpwEFuufpiPoTDIRXLnbfGAnzQIk_69GV_wlIihS50bcIrPKwTl-WNzdE9XcF_RuwK2G-TQ.wmqSm0nmWCvvq4CI.6C45JzWJ6Nc2WAlinDgWgF5MznLCyT7wjMrDbHnU1EpMQtwTmBpjrZCxyAc967LqzRasHFKqBESlh3EeXv561ALJNdjOzeYXG4k06BPIlE7Aev9qnaRY1iQkt5oKLkCBlRsfHZH6Q3PWE_6QDTyBgaC5JdecHpYWk9FZrWyVAHvz8ngu15iuZ9v8hS3ErSN0_Fyzglpv38e7Kf9sYKF2kNcqy8637wrBVxyFQbQ3fsGgZaTMlJRUEzFc9DA0-XrUV7ty0XsRQxg3sY-heJrt40dP8bQwKBPtyGQf9keZ-Mu4SOIuD0ggfPfddYpv3EBhRz4JuOn7KKi_w96aitJlDCOfa0D9CjtSSfBDJ3igFFC1qlQbW8ZnqkcHOoVJPewTGYiN1MZHKIR0kbca0q9rcyLiqpfZe6B47AXArKE9Ok6PPLmKewbO5jjcu6j0BifdUa0E9iFfv8u8ZU5Jo68CL0izbXEj5czMKiRxiUJjYZrPTO25wD9grenkS1HjPQ63XOVumFGYb1Jn9ESl1wAkZxFiV1Iw5PaBy1HbzxT_hFrFgUXzAiTQynfXh79CK0FEGSb7B-uAmaXjCHPyERPJrG23VmdTmibhfjpUwNBwl-b42hxmyZvAzjzynob4NsvovBGNsq1db0OB2Qx80CrORrUygOfj5Ui7lQy2nfHGi4ek2-yiktpFTnpTnhvDGVXYGrNnpSnxLdSYa_QSGbdO98-JluZe6_A8uDgpCLCKM054bOwDk1RCsxQ_sSpqUZzHmLIWlJ3VO8NR0abGsIppzE6cLO-NOdxhwrtghuYZArt8r71pduc-cMyWLmsKAuYARi3sfRT7fLFaPKaoLBLSF1AVBjehlIbRdVoXVKbGQXtl59v110uYcDn4WG0qk1Xgbzbe_y7xH-2HOAL_YmvlL1Zx3LgQ_HOgCe4jTkGhQXNmsK3vhgJPlCR6WeJMyVmI9rekaKofv1fWhmQ_-bFpMATcnCTWz7bShyZoKz1-WakVMjxJrpCBKylYTYu0Mt1y6xkpR_vBVX3VGy2sUVGQxD4_XR2GxDkAaGTkbr1NGmJIXPr4A0ipZhXmrm-WW5gSB3vojGZFhBRlsZa4mzqQ0xmfI2K5bzHQzgeeHoDDRoY6MApZmQ_tNl08Cj2HH3sEfi0GSleLXTpqRmk1DgvFfHY5rwNjFhq4kW-JgE7kjuE8q2H_MlJjtWPiUdwat6PuIylWyI0Lf_vPcT8vDOqprEBaQO1yGQbagbRsvG1QHBEz3Dhw3QkxI99kFuW0FZt2V3PhiXRJZJIK8FdEqdXMJQ4WnfA0.LkGP2cQd9l-7ZXL5MV6Oog", 
        "TokenType": "Bearer", 
        "AccessToken": "XXXXXG1jaEhMY00yQ3NkVT0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIwMDA3ZWU4Ny03ZjhiLTQ4YzktYTVlZS1kN2U3ZDNiNGVhNDIiLCJldmVudF9pZCI6ImZlM2QxYmIwLTBjNGUtMTFlOC1iMTQ1LTQ5NGM5MmE1ZDM3ZCIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAuZXUtY2VudHJhbC0xLmFtYXpvbmF3cy5jb21cL2V1LWNlbnRyYWwtMV9ibDdrZk5ZeFMiLCJleHAiOjE1MTgwNDMwMDQsImlhdCI6MTUxODAzOTQwNCwianRpIjoiNTE5Yjk4OGUtNjA1YS00OTc1LWIwOTQtZTFiMWEzYjU1ZWY0IiwiY2xpZW50X2lkIjoiMWUwZW9jN25iNG5yM3RjY2hnMDdnajNmamMiLCJ1c2VybmFtZSI6IjAwMDdlZTg3LTdmOGItNDhjOS1hNWVlLWQ3ZTdkM2I0ZWE0MiJ9.V-c7cwa1qUUp0VPYpiKGDWtlVyTf9VDavn8CToxxjIcVLcSsCgzYsBiVIes52UQ0Qt_AulNjhkNi-reYS0IyepcveTs-t5aYNNBVIrpWD3kDEyIbwZVSkjHUwNvMCSZIT4avBhVSCQlHRumJ-mR_ZBwIpVDMfCScFRnjfOa6awnDkGgTDBRkMrUUBiZUGzixrS8J1z4e4qDPAohgSp1UzDm1z_Zm3_0gqeEjLJPkAXc-Naw7RQdD9hwa1RGaTo0JjUNVH7i0aL4VEo0k4hzVz8fUXnYz_RIQKtyHylHNEtLg7UO_ZdFV3CprAIp_LHJWYfXN-4EO0BaAB0X4LO2A5A"
    }, 
    "ChallengeParameters": {}
}

Официальное значение этих токенов можно найти здесь . После того, как мы выпустим эти токены, мы можем реализовать соответствующие правильные действия для пользователя в наших реализациях.

    AWS API Gateway и AWS Cognito — это мощное средство в качестве входной двери для любых возможных API-интерфейсов. Вы можете привязать пул пользователей к определениям методов API-шлюза, чтобы прозрачно проверять сеансы пользователей , используя  только платформу .

    Проблема возникает, когда мы думаем о том, как сделать эти ключи сессии недействительными. Предполагается, что сеанс будет прерван, когда мы сделаем надлежащий глобальный выход администратора или пользователя  или глобальный выход . После этих вызовов API мы не должны использовать токены для каких-либо целей. Маркер доступа является JWT и определяет, что пользователь может делать в контексте пользовательского пула Cognito. Операции выхода из системы не делают ключи сессии недействительными (по крайней мере, функциональность еще не реализована). Здесь упоминается несколько платформ, таких как Github и StackOverflow в Интернете ( 123 ).

Поскольку мы не можем использовать эту прямую интеграцию, с Lambda Custom Authorizer можно использовать другое альтернативное решение. Просто ответственность за проверку сеанса принадлежит лямбда-функции. Существует хороший пример такой реализации уже. Однако этого решения также недостаточно, поскольку скрипт node.js не проверяет, вышел ли пользователь из системы или нет. Поэтому, когда вы только интегрируете эту лямбда-функцию и используете недействительный ключ, авторизатор все еще принимает запросы, предполагая, что сеанс все еще действителен. Вы должны ждать ровно 1 час для признания недействительным .

 Однако только добавление  проверки get-user в блок jwt-verify, похоже, решает всю проблему.

cognitoidentityserviceprovider.getUser(cognitoAuthTokenParams, function(err, data) {
          if (err) {
            console.log(err);
            context.fail("Unauthorized"); // an error occurred
            return;
          }
          else {
            console.log("Session not revoked"); // successful response
            console.log(data);
            context.succeed(policy.build());
          }
        });

Этим обновлением поток выглядит следующим образом:

  1. Пользователь обращается к шлюзу API с заголовком авторизации (токен доступа — JWT)

  2. Специальный авторизатор методов запускается и вызывается связанная лямбда-функция. API Gateway передает заголовок авторизации лямбда-функции авторизатора.

  3. Лямбда-функция проверяет токен доступа.

  4. Блок кода, расположенный выше, проверяет, отозван ли ключ доступа пользователя.

  5. Если все условия выполнены, политика генерируется и кэшируется на уровне шлюза API для определенного TTL (не 1 час, цель состоит в том, чтобы уменьшить этот интервал времени. Поэтому предпочтительное время составляет от 5 до 30 секунд.)

  6. В течение кэшированного времени лямбда не вызывается, вместо этого используется кэшированная политика.

  7. Если ключ сеанса недействителен, весь цикл начинается снова.

Конечное состояние блока jwt.verify выглядит следующим образом.

//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 {
        //Valid token. Generate the API Gateway policy for the user
        //Always generate the policy on value of 'sub' claim and not for 'username' because username is reassignable
        //sub is UUID for a user which is never reassigned to another user.
        var principalId = payload.sub;

        //Get AWS AccountId and API Options
        var apiOptions = {};
        var tmp = event.methodArn.split(':');
        var apiGatewayArnTmp = tmp[5].split('/');
        var awsAccountId = tmp[4];
        apiOptions.region = tmp[3];
        apiOptions.restApiId = apiGatewayArnTmp[0];
        apiOptions.stage = apiGatewayArnTmp[1];
        var method = apiGatewayArnTmp[2];
        var resource = '/'; // root resource
        if (apiGatewayArnTmp[3]) {
            resource += apiGatewayArnTmp[3];
        }

        var policy = new AuthPolicy(principalId, awsAccountId, apiOptions);
        policy.allowAllMethods();

        cognitoidentityserviceprovider.getUser(cognitoAuthTokenParams, function(err, data) {
          if (err) {
            console.log(err);
            context.fail("Unauthorized"); // an error occurred
            return;
          }
          else {
            console.log("Session not revoked"); // successful response
            console.log(data);
            context.succeed(policy.build());
          }
        });

      }
    });

(Ps. Этот обходной путь проверен командой технической поддержки AWS, пока они не сделают собственную реализацию.)