¿Cómo cambiar el estado de usuario FORCE_CHANGE_PASSWORD?


97

Con AWS Cognito, quiero crear usuarios ficticios con fines de prueba.

Luego utilizo la consola de AWS para crear dicho usuario, pero el usuario tiene su estado establecido en FORCE_CHANGE_PASSWORD. Con ese valor, este usuario no puede autenticarse.

¿Hay alguna forma de cambiar este estado?

ACTUALIZAR El mismo comportamiento al crear un usuario desde CLI


1
Respuesta del usuario proporcionada por @joe
donlys

Respuestas:


15

Lamento que tengas dificultades. No tenemos un proceso de un solo paso en el que puede crear usuarios y autenticarlos directamente. Podríamos cambiar esto en el futuro para permitir que los administradores establezcan contraseñas que los usuarios puedan utilizar directamente. Por ahora, cuando crea usuarios usando AdminCreateUsero registrando usuarios con la aplicación, se requieren pasos adicionales, ya sea forzando a los usuarios a cambiar la contraseña al iniciar sesión o haciendo que los usuarios verifiquen el correo electrónico o el número de teléfono para cambiar el estado del usuario CONFIRMED.


4
Por ahora, cuando crea usuarios usando AdminCreateUser o registrando usuarios con la aplicación, se requieren pasos adicionales, ya sea forzando a los usuarios a cambiar la contraseña al iniciar sesión o haciendo que los usuarios verifiquen el correo electrónico o el número de teléfono para cambiar el estado del usuario a CONFIRMADO. ¿Cuáles son exactamente estos esfuerzos adicionales y cómo puedo activarlos desde JS SDK?
Saurabh Tiwari

3
@joe señaló que ahora es posible, ya que se agregó. busque la --permanentbandera: stackoverflow.com/a/56948249/3165552
isaias-b

149

Sé que ha pasado un tiempo, pero pensé que esto podría ayudar a otras personas que se encuentren con esta publicación.

Puede utilizar la AWS CLI para cambiar la contraseña de los usuarios; sin embargo, es un proceso de varios pasos:


Paso 1: obtenga un token de sesión para el usuario deseado:

aws cognito-idp admin-initiate-auth --user-pool-id %USER POOL ID% --client-id %APP CLIENT ID% --auth-flow ADMIN_NO_SRP_AUTH --auth-parameters USERNAME=%USERS USERNAME%,PASSWORD=%USERS CURRENT PASSWORD%

Si esto devuelve un error sobre Unable to verify secret hash for client, cree otra aplicación cliente sin un secreto y use esa ID de cliente.

Paso 2: Si el paso 1 tiene éxito, responderá con el desafío NEW_PASSWORD_REQUIRED, otros parámetros del desafío y la clave de sesión del usuario. Luego, puede ejecutar el segundo comando para emitir la respuesta de desafío:

aws cognito-idp admin-respond-to-auth-challenge --user-pool-id %USER POOL ID% --client-id %CLIENT ID% --challenge-name NEW_PASSWORD_REQUIRED --challenge-responses NEW_PASSWORD=%DESIRED PASSWORD%,USERNAME=%USERS USERNAME% --session %SESSION KEY FROM PREVIOUS COMMAND with ""%

Si obtiene un error sobre Invalid attributes given, XXX is missingpasar los atributos que faltan usando el formatouserAttributes.$FIELD_NAME=$VALUE

El comando anterior debe devolver un resultado de autenticación válido y los tokens apropiados.


Importante: Para que esto funcione, el grupo de usuarios de Cognito DEBE tener un cliente de aplicación configurado con ADMIN_NO_SRP_AUTHfuncionalidad ( paso 5 en este documento ).


24
Increíblemente útil. Dos consejos más: si recibe un error sobre "No se puede verificar el hash secreto para el cliente", cree otra aplicación cliente sin un secreto y utilícela ( stackoverflow.com/questions/37438879/… ). Si obtiene un error sobre "Atributos no válidos indicados, falta XXX", pase los atributos que faltan utilizando el formato userAttributes.$FIELD_NAME=$VALUE( github.com/aws/aws-sdk-js/issues/1290 ).
Lane Rettig

Si no puede sacar a su usuario de FORCE_CHANGE_PASSWORD, con cualquiera de los comandos de la CLI, (incluida esta respuesta) intente 'admin-disable-user' y luego 'admin-enable-user' o use la consola. Luego, utilice este proceso o podría usar el flujo normal de restablecimiento de contraseña. En algún momento, un usuario "caducará" si no inició sesión en cognito dentro del límite predefinido. (predeterminado 7 días, creo)
comfytoday

Lo intenté con CLI y dentro de una lambda, obtuve este error: se proporcionaron atributos no válidos, falta el nombre
kolodi

1
@misher, lo está obteniendo debido a los atributos requeridos. puede incluirlos en la llamada, pero la sintaxis es un poco extraña:--challenge-responses NEW_PASSWORD=password,USERNAME=username,userAttributes.picture=picture,userAttributes.name=name
edzillion

88

Esto finalmente se ha agregado a AWSCLI: https://docs.aws.amazon.com/cli/latest/reference/cognito-idp/admin-set-user-password.html

Puede cambiar la contraseña de un usuario y actualizar el estado usando:

aws cognito-idp admin-set-user-password --user-pool-id <your user pool id> --username user1 --password password --permanent

Antes de usar esto, es posible que deba actualizar su AWS CLI mediante:

pip3 install awscli --upgrade


13
¡Esta es la última y más eficiente solución!
donlys

7
Esta debería ser la respuesta.
Mr. Young

4
La mejor y más fácil solución en 2020. ¡Gracias!
Tudor

23

Simplemente agregue este código después de su onSuccess: function (result) { ... },función de inicio de sesión. Su usuario tendrá entonces el estado CONFIRMADO .

newPasswordRequired: function(userAttributes, requiredAttributes) {
    // User was signed up by an admin and must provide new
    // password and required attributes, if any, to complete
    // authentication.

    // the api doesn't accept this field back
    delete userAttributes.email_verified;

    // unsure about this field, but I don't send this back
    delete userAttributes.phone_number_verified;

    // Get these details and call
    cognitoUser.completeNewPasswordChallenge(newPassword, userAttributes, this);
}

1
Esto funciona para mi. Incluso puede pasar la contraseña actual si no desea cambiarla.
mvandillen

Recursion FTW! ¡Gracias! (recursividad es el thisde la completa desafío nueva contraseña)
Paul S

22

Puede cambiar el estado de ese usuario FORCE_CHANGE_PASSWORDllamando respondToAuthChallenge()al usuario de esta manera:

var params = {
  ChallengeName: 'NEW_PASSWORD_REQUIRED', 
  ClientId: 'your_own3j6...0obh',
  ChallengeResponses: {
    USERNAME: 'user3',
    NEW_PASSWORD: 'changed12345'
  },
  Session: 'xxxxxxxxxxZDMcRu-5u...sCvrmZb6tHY'
};

cognitoidentityserviceprovider.respondToAuthChallenge(params, function(err, data) {
  if (err) console.log(err, err.stack); // an error occurred
  else     console.log(data);           // successful response
});

Después de esto, verá en la consola que el user3estado es CONFIRMED.


1
No entiendo cómo llegaste aquí. ¿A qué llamaste para obtener la sesión que hiciste XXX? Cuando llamo a adminInitiateAuth, aparece un error que dice UserNotFoundException.
Ryan Shillington

3
Lo siento si esa respuesta no fue clara como el cristal. Aquí hay más detalles: 1. El grupo de usuarios tiene un cliente llamado 'your_own3j63rs8j16bxxxsto25db00obh' que se crea SIN una clave secreta generada. El código anterior no funcionará si se asigna una clave al cliente. 2) La clave de sesión es el valor devuelto al llamarcognitoidentityserviceprovider.adminInitiateAuth({ AuthFlow: 'ADMIN_NO_SRP_AUTH', ClientId: 'your_own3j63rs8j16bxxxsto25db00obh', UserPoolId: 'us-east-1_DtNSUVT7n', AuthParameters: { USERNAME: 'user3', PASSWORD: 'original_password' } }, callback);
Ariel Araza

3) user3se creó en la consola e inicialmente se le dio la contraseña'original_password'
Ariel Araza

OKAY. Ahora entiendo por qué recibía una UserNotFoundException. Fue porque estaba usando un alias como nombre de usuario para iniciar sesión, que funciona bien en la API de JS pero aparentemente no funciona con adminInitiateAuth. Gracias Ariel Araza, agradezco tu ayuda.
Ryan Shillington

¡Hurra! Finalmente conseguí que esto funcionara. ¡Gracias! ¡Gracias Ariel!
Ryan Shillington

11

No estoy seguro de si todavía estás luchando con esto, pero solo para crear un grupo de usuarios de prueba, usé el awsclicomo tal:

  1. Utilice el subcomando de registro de cognito-idp para crear el usuario
aws cognito-idp sign-up \
   --region %aws_project_region% \
   --client-id %aws_user_pools_web_client_id% \
   --username %email_address% \
   --password %password% \
   --user-attributes Name=email,Value=%email_address%
  1. Confirme el usuario usando admin-confirm-sign-up
aws cognito-idp admin-confirm-sign-up \
--user-pool-id %aws_user_pools_web_client_id% \
--username %email_address%

5

ACTUALIZAR:

Ahora estoy usando esto, traducido para amplificar, dentro de un NodeJS Lambda:

// enable node-fetch polyfill for Node.js
global.fetch = require("node-fetch").default;
global.navigator = {};

const AWS = require("aws-sdk");
const cisp = new AWS.CognitoIdentityServiceProvider();

const Amplify = require("@aws-amplify/core").default;
const Auth = require("@aws-amplify/auth").default;

...


/*
  this_user: {
    given_name: string,
    password: string,
    email: string,
    cell: string
  }
*/
const create_cognito = (this_user) => {
  let this_defaults = {
    password_temp: Math.random().toString(36).slice(-8),
    password: this_user.password,
    region: global._env === "prod" ? production_region : development_region,
    UserPoolId:
      global._env === "prod"
        ? production_user_pool
        : development_user_pool,
    ClientId:
      global._env === "prod"
        ? production_client_id
        : development_client_id,
    given_name: this_user.given_name,
    email: this_user.email,
    cell: this_user.cell,
  };

  // configure Amplify
  Amplify.configure({
    Auth: {
      region: this_defaults.region,
      userPoolId: this_defaults.UserPoolId,
      userPoolWebClientId: this_defaults.ClientId,
    },
  });
  if (!Auth.configure())
    return Promise.reject("could not configure amplify");

  return new Promise((resolve, reject) => {
    let _result = {};

    let this_account = undefined;
    let this_account_details = undefined;

    // create cognito account
    cisp
      .adminCreateUser({
        UserPoolId: this_defaults.UserPoolId,
        Username: this_defaults.given_name,
        DesiredDeliveryMediums: ["EMAIL"],
        ForceAliasCreation: false,
        MessageAction: "SUPPRESS",
        TemporaryPassword: this_defaults.password_temp,
        UserAttributes: [
          { Name: "given_name", Value: this_defaults.given_name },
          { Name: "email", Value: this_defaults.email },
          { Name: "phone_number", Value: this_defaults.cell },
          { Name: "email_verified", Value: "true" },
        ],
      })
      .promise()
      .then((user) => {
        console.warn(".. create_cognito: create..");
        _result.username = user.User.Username;
        _result.temporaryPassword = this_defaults.password_temp;
        _result.password = this_defaults.password;

        // sign into cognito account
        return Auth.signIn(_result.username, _result.temporaryPassword);
      })
      .then((user) => {
        console.warn(".. create_cognito: signin..");

        // complete challenge
        return Auth.completeNewPassword(user, _result.password, {
          email: this_defaults.email,
          phone_number: this_defaults.cell,
        });
      })
      .then((user) => {
        console.warn(".. create_cognito: confirmed..");
        this_account = user;
        // get details
        return Auth.currentAuthenticatedUser();
      })
      .then((this_details) => {
        if (!(this_details && this_details.attributes))
          throw "account creation failes";

        this_account_details = Object.assign({}, this_details.attributes);

        // signout
        return this_account.signOut();
      })
      .then(() => {
        console.warn(".. create_cognito: complete");
        resolve(this_account_details);
      })
      .catch((err) => {
        console.error(".. create_cognito: error");
        console.error(err);
        reject(err);
      });
  });
};

Estoy configurando una contraseña temporal y luego la restablezco a la contraseña solicitada por el usuario.

PUESTO ANTIGUO:

Puede resolver esto usando el SDK de amazon-cognito-identity-js autenticándose con la contraseña temporal después de la creación de la cuenta cognitoidentityserviceprovider.adminCreateUser()y ejecutándose cognitoUser.completeNewPasswordChallenge()dentro cognitoUser.authenticateUser( ,{newPasswordRequired}), todo dentro de la función que crea su usuario.

Estoy usando el siguiente código dentro de AWS lambda para crear cuentas de usuario de Cognito habilitadas. Estoy seguro de que se puede optimizar, ten paciencia conmigo. Esta es mi primera publicación y todavía soy bastante nuevo en JavaScript.

var AWS = require("aws-sdk");
var AWSCognito = require("amazon-cognito-identity-js");

var params = {
    UserPoolId: your_poolId,
    Username: your_username,
    DesiredDeliveryMediums: ["EMAIL"],
    ForceAliasCreation: false,
    MessageAction: "SUPPRESS",
    TemporaryPassword: your_temporaryPassword,
    UserAttributes: [
        { Name: "given_name", Value: your_given_name },
        { Name: "email", Value: your_email },
        { Name: "phone_number", Value: your_phone_number },
        { Name: "email_verified", Value: "true" }
    ]
};

var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
let promise = new Promise((resolve, reject) => {
    cognitoidentityserviceprovider.adminCreateUser(params, function(err, data) {
        if (err) {
            reject(err);
        } else {
            resolve(data);
        }
    });
});

promise
    .then(data => {
        // login as new user and completeNewPasswordChallenge
        var anotherPromise = new Promise((resolve, reject) => {
            var authenticationDetails = new AWSCognito.AuthenticationDetails({
                Username: your_username,
                Password: your_temporaryPassword
            });
            var poolData = {
                UserPoolId: your_poolId,
                ClientId: your_clientId
            };
            var userPool = new AWSCognito.CognitoUserPool(poolData);
            var userData = {
                Username: your_username,
                Pool: userPool
            };

            var cognitoUser = new AWSCognito.CognitoUser(userData);
            let finalPromise = new Promise((resolve, reject) => {
                cognitoUser.authenticateUser(authenticationDetails, {
                    onSuccess: function(authResult) {
                        cognitoUser.getSession(function(err) {
                            if (err) {
                            } else {
                                cognitoUser.getUserAttributes(function(
                                    err,
                                    attResult
                                ) {
                                    if (err) {
                                    } else {
                                        resolve(authResult);
                                    }
                                });
                            }
                        });
                    },
                    onFailure: function(err) {
                        reject(err);
                    },
                    newPasswordRequired(userAttributes, []) {
                        delete userAttributes.email_verified;
                        cognitoUser.completeNewPasswordChallenge(
                            your_newPoassword,
                            userAttributes,
                            this
                        );
                    }
                });
            });

            finalPromise
                .then(finalResult => {
                    // signout
                    cognitoUser.signOut();
                    // further action, e.g. email to new user
                    resolve(finalResult);
                })
                .catch(err => {
                    reject(err);
                });
        });
        return anotherPromise;
    })
    .then(() => {
        resolve(finalResult);
    })
    .catch(err => {
        reject({ statusCode: 406, error: err });
    });

@Tom - ¿te funciona? algo que pueda aclarar?
qqan.ny

Mucho mejor ahora.
Tom Aranda

@ qqan.ny usa promesas y devoluciones de llamada al mismo tiempo? ¿Por qué?
Iurii Golskyi

@Iurii Golskyi: en ese entonces no sabía nada mejor, acababa de comenzar a aprender tanto AWS como JS desde cero.
qqan.ny

4

Para Java SDK, asumiendo que su cliente Cognito está configurado y tiene su usuario en el estado FORCE_CHANGE_PASSWORD, puede hacer lo siguiente para que su usuario sea CONFIRMADO ... y luego autenticado como de costumbre.

AdminCreateUserResult createUserResult = COGNITO_CLIENT.adminCreateUser(createUserRequest());

AdminInitiateAuthResult authResult = COGNITO_CLIENT.adminInitiateAuth(authUserRequest());


Map<String,String> challengeResponses = new HashMap<>();
challengeResponses.put("USERNAME", USERNAME);
challengeResponses.put("NEW_PASSWORD", PASSWORD);
RespondToAuthChallengeRequest respondToAuthChallengeRequest = new RespondToAuthChallengeRequest()
      .withChallengeName("NEW_PASSWORD_REQUIRED")
      .withClientId(CLIENT_ID)
      .withChallengeResponses(challengeResponses)
      .withSession(authResult.getSession());

COGNITO_CLIENT.respondToAuthChallenge(respondToAuthChallengeRequest);

Espero que ayude con esas pruebas de integración (lo siento por el formato)


4

Básicamente, esta es la misma respuesta pero para .Net C # SDK:

Lo siguiente hará una creación de usuario administrador completa con el nombre de usuario y la contraseña deseados. Tener el siguiente modelo de usuario:

public class User
{
    public string Username { get; set; }
    public string Password { get; set; }
}

Puede crear un usuario y dejarlo listo para usar usando:

   public void AddUser(User user)
    {
        var tempPassword = "ANY";
        var request = new AdminCreateUserRequest()
        {
            Username = user.Username,
            UserPoolId = "MyuserPoolId",
            TemporaryPassword = tempPassword
        };
        var result = _cognitoClient.AdminCreateUserAsync(request).Result;
        var authResponse = _cognitoClient.AdminInitiateAuthAsync(new AdminInitiateAuthRequest()
        {
            UserPoolId = "MyuserPoolId",
            ClientId = "MyClientId",
            AuthFlow = AuthFlowType.ADMIN_NO_SRP_AUTH,
            AuthParameters = new Dictionary<string, string>()
            {
                {"USERNAME",user.Username },
                {"PASSWORD", tempPassword}
            }
        }).Result;
        _cognitoClient.RespondToAuthChallengeAsync(new RespondToAuthChallengeRequest()
        {
         ClientId = "MyClientId",
            ChallengeName = ChallengeNameType.NEW_PASSWORD_REQUIRED,
            ChallengeResponses = new Dictionary<string, string>()
            {
                {"USERNAME",user.Username },
                {"NEW_PASSWORD",user.Password }
            },
            Session = authResponse.Session
        });
    }

3

Si está intentando cambiar el estado como administrador desde la consola. Luego, siga los pasos a continuación después de crear el usuario.

  1. En Cognito goto -> "administrar grupo de usuarios" ->
  2. Vaya a "Configuración del cliente de la aplicación" en la sección Integración de la aplicación.
  3. Verifique los siguientes elementos i) Grupo de usuarios de Cognito ii) Concesión del código de autorización iii) Concesión implícita iv) teléfono v) correo electrónico vi) openid vii) aws.cognito.signin.user.admin viii) perfil
  4. Ingrese la URL de devolución de llamada de su aplicación. Si no está seguro, ingrese, por ejemplo: https://google.com y luego puede cambiarlo a su URL de devolución de llamada real
  5. haga clic en guardar cambios.
  6. Una vez guardados los cambios, haga clic en el enlace "Iniciar interfaz de usuario alojada"
  7. Ingrese las credenciales del nuevo usuario creado
  8. Restablezca la contraseña con nuevas credenciales y comparta la misma con el usuario

paso 2

paso 3 4 5 6

paso 7

paso 8


2

Sé que es la misma respuesta, pero pensé que podría ayudar a la Gocomunidad de desarrolladores. básicamente es iniciar una solicitud de autenticación, obtener la sesión y responder al desafíoNEW_PASSWORD_REQUIRED

func sessionWithDefaultRegion(region string) *session.Session {
    sess := Session.Copy()
    if v := aws.StringValue(sess.Config.Region); len(v) == 0 {
        sess.Config.Region = aws.String(region)
    }

    return sess
}



func (c *CognitoAppClient) ChangePassword(userName, currentPassword, newPassword string)   error {

    sess := sessionWithDefaultRegion(c.Region)
    svc := cognitoidentityprovider.New(sess)

    auth, err := svc.AdminInitiateAuth(&cognitoidentityprovider.AdminInitiateAuthInput{
        UserPoolId:aws.String(c.UserPoolID),
        ClientId:aws.String(c.ClientID),
        AuthFlow:aws.String("ADMIN_NO_SRP_AUTH"),
        AuthParameters: map[string]*string{
            "USERNAME": aws.String(userName),
            "PASSWORD": aws.String(currentPassword),
        },

    })



    if err != nil {
        return err
    }

    request := &cognitoidentityprovider.AdminRespondToAuthChallengeInput{
        ChallengeName: aws.String("NEW_PASSWORD_REQUIRED"),
        ClientId:aws.String(c.ClientID),
        UserPoolId: aws.String(c.UserPoolID),
        ChallengeResponses:map[string]*string{
            "USERNAME":aws.String(userName),
            "NEW_PASSWORD": aws.String(newPassword),
        },
        Session:auth.Session,
    }


    _, err = svc.AdminRespondToAuthChallenge(request)

    return err 
}

Aquí hay una prueba unitaria:

import (
    "fmt"
    "github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
    . "github.com/smartystreets/goconvey/convey"
    "testing"
)


func TestCognitoAppClient_ChangePassword(t *testing.T) {


    Convey("Testing ChangePassword!", t, func() {
        err := client.ChangePassword("user_name_here", "current_pass", "new_pass")



        Convey("Testing ChangePassword Results!", func() {
            So(err, ShouldBeNil)

        })

    })
}

1

OKAY. Finalmente tengo un código donde un administrador puede crear un nuevo usuario. El proceso es así:

  1. El administrador crea el usuario
  2. El usuario recibe un correo electrónico con su contraseña temporal
  3. El usuario inicia sesión y se le solicita que cambie su contraseña

El paso 1 es la parte difícil. Aquí está mi código para crear un usuario en Node JS:

let params = {
  UserPoolId: "@cognito_pool_id@",
  Username: username,
  DesiredDeliveryMediums: ["EMAIL"],
  ForceAliasCreation: false,
  UserAttributes: [
    { Name: "given_name", Value: firstName },
    { Name: "family_name", Value: lastName},
    { Name: "name", Value: firstName + " " + lastName},
    { Name: "email", Value: email},
    { Name: "custom:title", Value: title},
    { Name: "custom:company", Value: company + ""}
  ],
};
let cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider();
cognitoIdentityServiceProvider.adminCreateUser(params, function(error, data) {
  if (error) {
    console.log("Error adding user to cognito: " + error, error.stack);
    reject(error);
  } else {
    // Uncomment for interesting but verbose logging...
    //console.log("Received back from cognito: " + CommonUtils.stringify(data));
    cognitoIdentityServiceProvider.adminUpdateUserAttributes({
      UserAttributes: [{
        Name: "email_verified",
        Value: "true"
      }],
      UserPoolId: "@cognito_pool_id@",
      Username: username
    }, function(err) {
      if (err) {
        console.log(err, err.stack);
      } else {
        console.log("Success!");
        resolve(data);
      }
    });
  }
});

Básicamente, debe enviar un segundo comando para forzar que el correo electrónico se considere verificado. El usuario aún necesita ir a su correo electrónico para obtener la contraseña temporal (que también verifica el correo electrónico). Pero sin esa segunda llamada que configura el correo electrónico como verificado, no recibirá la llamada correcta para restablecer su contraseña.


Sigo sin recibir el correo electrónico, ¿alguna sugerencia?
Vinicius

Extraño, ¿ha configurado correctamente la dirección de correo electrónico en Cognito, con acceso a SES, etc.? Cogntio no solo enviará correos electrónicos a las personas hasta que haya verificado la dirección de correo electrónico a la que está tratando de enviar o hasta que obtenga la aprobación para enviar a alguien.
Ryan Shillington

Recibo correos electrónicos con códigos de confirmación, por lo que la configuración es correcta. Solo el correo electrónico con la contraseña temporal nunca llega ... Terminé creando una lambda adjunta al disparador de registro previo para enviar el correo electrónico.
Vinicius
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.