Поток для аутентификации, когда MFA требуется для пользователя в AWS Cognito

Я пытаюсь добавить MFA для аутентификации пользователя в уже существующее решение (встроенное в Angular) для управления устройствами в AWS Cognito.

У меня возникают проблемы с выяснением того, как правильно обрабатывать этот конкретный ответ с точки зрения пользовательского опыта. На самом деле он чувствует себя разбитым, поэтому буду рад, если у кого-то еще есть болевые точки.

См. Пример использования 23. Пример реализации, мой ниже:

authenticate(username: string, password: string): Observable<any> {

    // init cognitoUser here

    return new Observable((observer) => {
        cognitoUser.authenticateUser(authenticationDetails, {
            onSuccess: (result: any) => {},
            onFailure: (err: Error) => {},
            mfaRequired: (codeDeliveryDetails: any) => {

                // SMS has just been sent automatically 
                // and it needs to be confirmed within this scope

                // The example linked requests the code via `confirm()`
                // which is awful UX...and since this is a service
                // probably non-compliant with best practice
                // However, without this `confirm` at this point in                     
                // time, we have no confirmationCode below

                cognitoUser.sendMFACode(confirmationCode, {
                    onSuccess: (result) => {
                        observer.next(result);
                        observer.complete();
                    }, onFailure: (err: Error) => {
                        observer.error(err);
                        observer.complete();
                    }
                });
            }
        });
    });
}

Ожидаемое:

  • Если пользователь успешно прошел аутентификацию, но не добавил это устройство через MFA, мы можем управлять перенаправлением на соответствующую страницу формы кода подтверждения и вызвать sendMFACode работать вручную (возможно, через какую-то ограниченную сессию?)

Вопросы:

  • у нас нет сеанса, поэтому у нас нет возможности запросить у пользователя код MFA, автоматически отправляемый за пределы этого экрана входа... поймать 22?
  • добавление еще одного поля "показать / скрыть" в форме входа не работает, так как sendMfaCode функционировать несколько раз, в результате чего отправлено несколько кодов SMS.

У кого-нибудь была удача выйти из этого потока?

2 ответа

Решение

Хотя я уверен, что очень талантливые люди работали над API - интерфейсом amazon-cognito-identity-js, он просто плохо спроектирован. Таким образом, почему это было исключено. Моим личным советом было бы перейти на Amplify, что сильно меня злит.

С Amplify вы можете сделать это.

import Amplify from 'aws-amplify'
import Auth from '@aws-amplify/auth'

let mfaRequired = false

Amplify.configure({
    Auth: {
        userPoolWebClientId: '',
        userPoolId: ''
    }
})

const logUserIn = (user) => {
  // Go forth and be happy
}

// Run me on your login form's submit event
const login = async (username, password) => {
  const user = await Auth.signIn(username, password)

  if (user.challengeName === 'SMS_MFA') {
    // Change UI to show MFA Code input
    mfaRequired = true
    return
  }
  return logUserIn(user)
}

// Run me when the user submits theire MFA code
const senfMfaCode = async (mfaCode) => {
  const user = await Auth.confirmSignIn(mfaCode)
  return logUserIn(user)
}

НО, если по какой-то грустной причине вам нужно продолжать использовать amazon-cognito-identity-js, не беспокойтесь. Понял тебя.

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

Просто помните, что документальное шоу это прохождение this в sendMFACode() для определения (что ужасно), но вы можете просто объявить ваши обратные вызовы как переменную и поделиться ею между authenticateUser() а также sendMFACode() функции (или столько функций, сколько вам нравится).

import { CognitoUserPool, AuthenticationDetails, CognitoUser } from 'amazon-cognito-identity-js'

export let mfaRequired = false
export let cognitoUser = null

export const cognitoCallbacks = {
  mfaRequired () {
    // Implement you functionality to show UI for MFA form
    mfaRequired = true
  },
  onSuccess (response) {
    // Dance for joy the code gods be glorious.
  },
  onFailure () {
    // Cry.
  }
}

export const logUserIn = payload => {
  cognitoUser = new CognitoUser({
    Username: 'Matt Damon',
    Pool: new CognitoUserPool({
      UserPoolId: '',
      ClientId: ''
    })
  })
  return cognitoUser.authenticateUser(new AuthenticationDetails(payload), cognitoCallbacks)
}

export const sendMfaCode = MFACode => {
  cognitoUser.sendMFACode(MFACode, cognitoCallbacks)
}

Это супер базовая реализация, и вдобавок ко всему, вы могли бы

  1. Просто переписать mfaRequired функция во внешнем модуле, чтобы делать все, что вы хотите.
  2. Оберните все это в паб /sub плагин и подпишитесь на события.

Надеюсь, это поможет!

Я знаю, что это старый вопрос, но я подумал, что этот ответ может быть полезен для всех, кто все еще использует API вместо Amplify. Ответ @stwilz отчасти работает, но есть несколько сложностей, которые возникают, когда вы слишком далеко отклоняетесь от вариантов использования документации (и могут возникнуть при использовании TOTP MFA вместо SMS MFA). Я создал обходной путь для решения ситуаций, когда вы можете получить такие ошибки, какInvalid Access Token,Missing parameter Session, илиInvalid session for the user.

Если вам нужно использовать что-то вродеsendMFACodeвне обратных вызовов, недостаточно просто сохранитьcognitoUserхранится вне обратного вызова. На самом деле вам нужно снова вызвать функцию , а затем вызвать sendMFACode в обратном вызове. Сложнее становитсяverifySoftwareTokenдля TOTP, где вам действительно нужно хранить пользовательский объект Cognito, а затем переназначать его при вызовеauthenticateUserснова.

Если все это не имеет смысла, я создал простой Github Gist, который использует React иamazon-cognito-identity-jsчтобы показать, как будет работать такой поток. Это здесь: https://gist.github.com/harve27/807597824720d0919476c0262e30f587

Другие вопросы по тегам