Cognito User Pool: обновить токен доступа с помощью токена обновления

Я использую пул пользователей Cognito для аутентификации пользователей в моей системе. Успешная аутентификация дает идентификационный токен (JWT), токен доступа (JWT) и токен обновления. В документации здесь четко упоминается, что токен обновления может использоваться для обновления токена доступа, но не упоминается как. У меня вопрос, как только истекает срок действия моего токена доступа, как мне использовать сохраненный токен обновления, чтобы снова обновить мой токен доступа?

Я искал через SDK JavaScript и не мог найти какой-либо метод, чтобы сделать то же самое. Я определенно что-то пропустил.

Также я думал сделать это с помощью функции Lambda, которая получает токен доступа и обновляет токен и отвечает обновленным токеном доступа. Было бы здорово, если бы кто-нибудь смог пролить свет на это.

Ответы

Ответ 1

Если вы находитесь в ситуации, когда Cognito Javascript SDK не будет работать для ваших целей, вы все равно сможете увидеть, как он обрабатывает процесс обновления в источнике SDK:

В refreshSession вы можете видеть, что конечная точка Cognito InitiateAuth вызывается с REFRESH_TOKEN_AUTH, установленным для значения AuthFlow, и объектом, переданным как значение AuthParameters.

Этот объект необходимо будет настроить в соответствии с потребностями вашего пула пользователей. В частности, вам может потребоваться передать ваш SECRET_HASH, если у вашего целевого идентификатора клиента приложения есть связанный секретный ключ клиента приложения. Клиентские приложения пула пользователей, созданные для использования с Javascript SDK, в настоящее время не могут содержать секрет клиента, поэтому для их подключения SECRET_HASH не требуется.

Еще одно предостережение, которое может привести вас к циклу, если ваш пул пользователей настроен на запоминание устройств, и вы не передаете DEVICE_KEY вместе со своим REFRESH_TOKEN. В настоящее время Cognito API возвращает ошибку "Недопустимый токен обновления", если вы передаете RefreshToken, не передавая также DeviceKey. Эта ошибка возвращается, даже если вы передаете действительный RefreshToken. Связанная выше нить освещает это, хотя я надеюсь, что AWS обновит свою обработку ошибок, чтобы в будущем она стала менее загадочной.

Как обсуждалось в этой теме, если вы используете AdminInitiateAuth вместе с ADMIN_NO_SRP_AUTH, полезная нагрузка успешного ответа аутентификации в настоящее время не содержит NewDeviceMetadata; это означает, что вам не нужно будет передавать DeviceKey при попытке обновить свои токены.

Мое приложение требует реализации на Python, поэтому вот пример, который работал для меня:

def refresh_token(self, username, refresh_token):
    try:
        return client.initiate_auth(
            ClientId=self.client_id,
            AuthFlow='REFRESH_TOKEN_AUTH',
            AuthParameters={
                'REFRESH_TOKEN': refresh_token,
                'SECRET_HASH': self.get_secret_hash(username)
                # Note that SECRET_HASH is missing from JSDK
                # Note also that DEVICE_KEY is missing from my example
            }
        )
    except botocore.exceptions.ClientError as e:
        return e.response

Ответ 2

Javascript sdk обрабатывает обновление токенов внутри. Когда вы вызываете "getSession" для получения токенов, в отсутствие каких-либо действительных кэшированных токенов доступа и идентификаторов SDK использует токен обновления, чтобы получить новый доступ и токены id. Он вызывает аутентификацию пользователя, требуя от пользователя указать имя пользователя и пароль, только когда токен обновления также истек.

С уважением, Махеш

Ответ 3

Обновление сеанса с SDK браузера amazon-cognito-identity-js; в основном это делается для вас, и если вы не делаете что-то необычное, вам не нужно напрямую обрабатывать токен обновления. Вот что вам нужно знать:

Предположим, что вы создали экземпляр пула пользователей следующим образом:

const userPool = new AmazonCognitoIdentity.CognitoUserPool({
  UserPoolId: USER_POOL_ID,
  ClientId: USER_POOL_CLIENT_ID
});

Чтобы найти последнее имя пользователя аутентифицированным, вы должны сделать это:

const cognitoUser = cognitoUserPool.getCurrentUser();

Если он найдет его, cognitoUser будет не нулевым, и вы можете сделать это, что при необходимости обновит ваши токены за кулисами:

cognitoUser.getSession(function(err, data) {
  if (err) {
    // Prompt the user to reauthenticate by hand...
  } else {
    const cognitoUserSession = data;
    const yourIdToken = cognitoUserSession.getIdToken().jwtToken;
    const yourAccessToken = cognitoUserSession.getAccessToken().jwtToken;
  }
});

Если вы не хотите, чтобы эти токены сохранялись в локальном хранилище, вы можете:

cognitoUser.signOut();

Как это работает, после успешной аутентификации браузер будет хранить ваши токены JWT, включая этот токен обновления. Он хранит их в локальном хранилище в вашем браузере по умолчанию, хотя вы можете предоставить свой собственный объект хранения, если хотите. По умолчанию токен обновления действителен в течение 30 дней, но это свойство (RefreshTokenValidity) вашего UserPoolClient, которое вы можете изменить. Когда вы сделаете вышеописанное, getSession() сначала увидит, существуют ли токены в вашем хранилище и все еще действительны; в противном случае он попытается использовать любой найденный там refreshToken для аутентификации в новом сеансе.

В документации http://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html указано, что SDK для iOS и Android будут делать это для Вы, хотя я не использовал их, поэтому не могу за это поручиться.

Ответ 4

Я боролся с этим, а также в Javascript. Здесь мое решение основано на https://github.com/aws/amazon-cognito-identity-js, но оно не полагается на хранилище, поэтому вы можете использовать его в лямбда-функции, если вы пожелает. Изменить: Исправлен код, благодаря Crayons

const userPool = new AWSCognito.CognitoUserPool({
  UserPoolId: <COGNITO_USER_POOL>,
  ClientId: <COGNITO_APP_ID>
})

userPool.client.makeUnauthenticatedRequest('initiateAuth', {
  ClientId: <COGNITO_APP_ID>,
  AuthFlow: 'REFRESH_TOKEN_AUTH',
  AuthParameters: {
    'REFRESH_TOKEN': <REFRESH_TOKEN> // client refresh JWT
  }
}, (err, authResult) => {
  if (err) {
     throw err
  }
  console.log(authResult) // contains new session
})

Ответ 5

Вот пример того, как это сделать с JavaScript на стороне сервера с помощью Node.js.

const AccessToken = new CognitoAccessToken({ AccessToken: tokens.accessToken });
const IdToken = new CognitoIdToken({ IdToken: tokens.idToken });
const RefreshToken = new CognitoRefreshToken({ RefreshToken: tokens.refreshToken });

const sessionData = {
  IdToken: IdToken,
  AccessToken: AccessToken,
  RefreshToken: RefreshToken
};
const userSession = new CognitoUserSession(sessionData);

const userData = {
  Username: email,
  Pool: this.userPool
};

const cognitoUser = new CognitoUser(userData);
cognitoUser.setSignInUserSession(userSession);

cognitoUser.getSession(function (err, session) { // You must run this to verify that session (internally)
  if (session.isValid()) {
    // Update attributes or whatever else you want to do
  } else {
    // TODO: What to do if session is invalid?
  }
});

Вы можете увидеть полный рабочий пример в своем сообщении в блоге Как аутентифицировать пользователей с помощью токенов с помощью Cognito.

Ответ 6

Если у вас есть токен обновления, вы можете получить новый токен доступа, идентификатор и обновить, просто сделав этот простой запрос POST к cognito:

POST https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/token >
Content-Type='application/x-www-form-urlencoded'
Authorization=Basic aSdxd892iujendek328uedj

grant_type=refresh_token&
client_id=djc98u3jiedmi283eu928&
refresh_token=REFRESH_TOKEN

Вы получите следующий ответ:

HTTP/1.1 200 OK
Content-Type: application/json

{
   "access_token":"eyJz9sdfsdfsdfsd", 
   "refresh_token":"dn43ud8uj32nk2je",
   "id_token":"dmcxd329ujdmkemkd349r",
   "token_type":"Bearer", 
   "expires_in":3600
}

Ответ 7

Используя NodeJS aws-sdk и немного Promise, вы можете await аутентифицировать, используя маркер обновления с initiateAuth как следующим образом:

const {CognitoIdentityServiceProvider} = require('aws-sdk');

const initiateAuth = (ClientId, REFRESH_TOKEN, DEVICE_KEY) =>
    new Promise((resolve, reject) => {
        const CISP = new CognitoIdentityServiceProvider();

        CISP.initiateAuth(
            {
                ClientId, // Cognito App Client Id
                AuthFlow: 'REFRESH_TOKEN_AUTH',
                AuthParameters: {
                    REFRESH_TOKEN,
                    DEVICE_KEY
                }
            },
            (err, data) => {
                if (err) {
                    return reject(err);
                }

                resolve(data);
            }
        );
    });

// ------ Usage ------ //

(async () => {
    const tokens = await initiateAuth('mY4pps3cR3T', '<R3FR3SHT0K3N>');

    console.log('Tokens', tokens);

    const {AuthenticationResult: {AccessToken, IdToken, ExpiresIn, TokenType}} = tokens;
})()

Помните, что если отслеживание устройства включено, вы должны передать ключ устройства, в противном случае вы можете получить ошибку Invalid update token.