Habilitar SMS_MFA con Custom Authentication Challenge Lambda Triggers

Habilitar challenge name SMS_MFA con CUSTOM_CHALLENGE

Vamos a partir de la documentación que nos provee amazon sobre los triggers o disparadores que nos permiten cumstomizar nuestra app de autenticaciòn para que afronte diferentes retos a la hora de autenticar un usuario https://docs.aws.amazon.com/es_es/cognito/latest/developerguide/user-pool-lambda-challenge.html

Después de haber implementado nuestros triggers lambdas y conectados a nuestro cognito debemos habilitar el MFA a cognito y habilitar a un usuario de prueba.

  1. En la barra de navegación izquierda, elija MFA and verifications (MFA y verificaciones).
  2. Elija el estado de la MFA: Off (Desactivada), Optional (Opcional) o Required (Obligatoria).
  1. Elija Optional (Opcional) para habilitar MFA para cada usuario o si utiliza la autenticación flexible basada en riesgos. Para obtener más información sobre la autenticación flexible, consulte Adición de seguridad avanzada a un grupo de usuarios.
  2. Elija las opciones de segundo factor que desea admitir en la aplicación. Los usuarios pueden utilizar SMS text message (Mensaje de texto SMS) o Time-based One-time Password (Contraseña de un solo uso basada en el tiempo) como segundo factor. Recomendamos utilizar TOTP, que permite utilizar SMS como mecanismo de recuperación de contraseñas en lugar de como factor de autenticación.
  3. Si utiliza mensajes de texto SMS como segundo factor y no tiene un IAM rol de definido con este permiso, puede crear uno en la consola de . Elija Create role (Crear rol) para crear un IAM rol de que permita Amazon Cognito a enviar mensajes SMS a los usuarios en su nombre. Para obtener más información, consulte Roles de IAM.
  4. Elija Save changes.
  5. Habilitamos al usuario en cuestión MFA   autenticamos

Nos Autenticamos

aws cognito-idp admin-initiate-auth --user-pool-id us-west-2_aaaaaaaaa --client-id 3n4b5urk1ft4fl3mg5e62d9ado --auth-flow ADMIN_NO_SRP_AUTH --auth-parameters USERNAME=jane@example.com,PASSWORD=password

Después si todo sale bien amazon nos enviara el codigo de confirmación por SMS a nuestro teléfono y utilizamos la session anterior, y ejecutamos el siguiente código .


aws cognito-idp respond-to-auth-challenge \
   --client-id YOUR_COGNITO_APP_CLIENT_ID \
   --challenge-name SMS_MFA \
   --challenge-responses USERNAME=user@example.com,SMS_MFA_CODE=345678 \
   --session "LONG_SESSION_STRING"

Después de ejecutar nuestro codigo nos retorne que no esta autorizado o que error en usuario o contraseña .

Esto sucede porque en el cognito que crea los desafíos tu no tiene contemplado la session de MFA .


Este es nuestro código por defecto de nuestro triggre Define Auth Challenge

exports.handler = (event, context, callback) => {
    if (event.request.session.length == 1 && event.request.session[0].challengeName == 'SRP_A') {
        event.response.issueTokens = false;
        event.response.failAuthentication = false;
        event.response.challengeName = 'PASSWORD_VERIFIER';
    } else if (event.request.session.length == 2 && event.request.session[1].challengeName == 'PASSWORD_VERIFIER' && event.request.session[1].challengeResult == true) {
        event.response.issueTokens = false;
        event.response.failAuthentication = false;
        event.response.challengeName = 'CUSTOM_CHALLENGE';
    } else if (event.request.session.length == 3 && event.request.session[2].challengeName == 'CUSTOM_CHALLENGE' && event.request.session[2].challengeResult == true) {
        event.response.issueTokens = true;
        event.response.failAuthentication = false;
    } else {
        event.response.issueTokens = false;
        event.response.failAuthentication = true;
    }

    // Return to Amazon Cognito
    callback(null, event);
}

Lo vamos a modificar para que nos soporte MFA y nos dispare el siguiente reto


...
// validar que venga un challengeName de tipo SMS_MFA valido
 else if (event.request.session.length == 3 && 
 event.request.session[1].challengeName == 'PASSWORD_VERIFIER' && 
 event.request.session[2].challengeName == 'SMS_MFA' && event.request.session[2].challengeResult == true) {
        event.response.issueTokens = false;
        event.response.failAuthentication = false;
    } 
.....

y por ultimo agregar también la validación para que me soporte  CUSTOM_CHALLENGE cuando a pasado por MFA

...
// validar que venga un challengeName de tipo CUSTOM_CHALLENGE valido
 else if (event.request.session.length == 4 && 
 event.request.session[2].challengeName == 'SMS_MFA' && 
 event.request.session[3].challengeName == 'CUSTOM_CHALLENGE' && event.request.session[3].challengeResult == true) {
        event.response.issueTokens = true;
        event.response.failAuthentication = false;
    } 
.....

el código final  


exports.handler = (event, context, callback) => {
    if (event.request.session.length == 1 && event.request.session[0].challengeName == 'SRP_A') {
        event.response.issueTokens = false;
        event.response.failAuthentication = false;
        event.response.challengeName = 'PASSWORD_VERIFIER';
    } else if (event.request.session.length == 2 && event.request.session[1].challengeName == 'PASSWORD_VERIFIER' && event.request.session[1].challengeResult == true) {
        event.response.issueTokens = false;
        event.response.failAuthentication = false;
        event.response.challengeName = 'CUSTOM_CHALLENGE';
    } else if (event.request.session.length == 3 && event.request.session[2].challengeName == 'CUSTOM_CHALLENGE' && event.request.session[2].challengeResult == true) {
        event.response.issueTokens = true;
        event.response.failAuthentication = false;
       }
       // validar que venga un challengeName de tipo SMS_MFA valido
 else if (event.request.session.length == 3 && 
 event.request.session[1].challengeName == 'PASSWORD_VERIFIER' && 
 event.request.session[2].challengeName == 'SMS_MFA' && event.request.session[2].challengeResult == true) {
        event.response.issueTokens = false;
        event.response.failAuthentication = false;
    } 
    // validar que venga un challengeName de tipo CUSTOM_CHALLENGE valido
 else if (event.request.session.length == 4 && 
 event.request.session[2].challengeName == 'SMS_MFA' && 
 event.request.session[3].challengeName == 'CUSTOM_CHALLENGE' && event.request.session[3].challengeResult == true) {
        event.response.issueTokens = true;
        event.response.failAuthentication = false;
    } 
        
    } else {
        event.response.issueTokens = false;
        event.response.failAuthentication = true;
    }

    // Return to Amazon Cognito
    callback(null, event);
}

Actualizamos nuestro lambda y volveríamos a probar ejecutando con  respond-to-auth-challenge con challenge-name SMS_MFA y después con CUSTOM_CHALLENGE el cual es el único que habilitamos para que nos retorne los tokens.

Happy code .  

References:

Custom Authentication Challenge Lambda Triggers - Amazon Cognito
Challenge Lambda triggers.
Define Auth Challenge Lambda Trigger - Amazon Cognito
The Define Auth Challenge Lambda trigger is used to initiate the custom authentication flow.
Adición de la autenticación multifactor (MFA) a un grupo de usuarios - Amazon Cognito
Describe cómo añadir la MFA a un grupo de usuarios.