Como alterar o status do usuário FORCE_CHANGE_PASSWORD?

103

Usando o AWS Cognito, desejo criar usuários fictícios para fins de teste.

Em seguida, uso o console AWS para criar esse usuário, mas o usuário tem seu status definido como FORCE_CHANGE_PASSWORD. Com esse valor, este usuário não pode ser autenticado.

Existe uma maneira de alterar esse status?

ATUALIZAÇÃO Mesmo comportamento ao criar usuário a partir de CLI

Dominique Vial
fonte
1
Resposta do usuário fornecida por
@joe

Respostas:

15

Desculpe, você está tendo dificuldades. Não temos um processo de uma etapa em que você pode apenas criar usuários e autenticá-los diretamente. Podemos mudar isso no futuro, para permitir que os administradores definam senhas que podem ser usadas diretamente pelos usuários. Por enquanto, quando você cria usuários usando AdminCreateUserou inscrevendo usuários no aplicativo, etapas extras são necessárias, forçando os usuários a alterar a senha no login ou fazendo com que os usuários verifiquem o e-mail ou número de telefone para alterar o status do usuário para CONFIRMED.

Ionut Trestian
fonte
4
Por enquanto, quando você cria usuários usando AdminCreateUser ou inscrevendo usuários no aplicativo, etapas extras são necessárias, forçando os usuários a alterar a senha no login ou fazendo com que os usuários verifiquem o e-mail ou número de telefone para alterar o status do usuário para CONFIRMADO. O que exatamente são esses esforços extras e como posso acioná-los a partir do JS SDK.
Saurabh Tiwari
3
@joe apontou que agora é possível, uma vez que foi adicionado. procure a --permanentsinalização: stackoverflow.com/a/56948249/3165552
isaias-b
152

Eu sei que já faz um tempo, mas achei que isso poderia ajudar outras pessoas que encontraram este post.

Você pode usar o AWS CLI para alterar a senha dos usuários, no entanto, é um processo de várias etapas:


Etapa 1: obtenha um token de sessão para o usuário desejado:

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%

Se isso retornar um erro sobre Unable to verify secret hash for client, crie outro cliente de aplicativo sem um segredo e use esse ID de cliente.

Etapa 2: Se a etapa 1 for bem-sucedida, ele responderá com o desafio NEW_PASSWORD_REQUIRED, outros parâmetros de desafio e a chave de sessão do usuário. Em seguida, você pode executar o segundo comando para emitir a resposta de desafio:

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 ""%

Se você receber um erro sobre como Invalid attributes given, XXX is missingpassar os atributos ausentes usando o formatouserAttributes.$FIELD_NAME=$VALUE

O comando acima deve retornar um Resultado de Autenticação válido e Tokens apropriados.


Importante: Para que isso funcione, o Cognito User Pool DEVE ter um aplicativo cliente configurado com ADMIN_NO_SRP_AUTHfuncionalidade ( Etapa 5 neste documento ).

Pinguim Neutro
fonte
26
Incrivelmente útil. Mais duas dicas: se você receber um erro sobre "Não foi possível verificar o hash secreto do cliente", crie outro aplicativo cliente sem um segredo e use-o ( stackoverflow.com/questions/37438879/… ). Se você receber um erro sobre "Atributos inválidos fornecidos, XXX está faltando", passe os atributos ausentes usando o formato userAttributes.$FIELD_NAME=$VALUE( github.com/aws/aws-sdk-js/issues/1290 ).
Lane Rettig
Se você não conseguir tirar seu usuário de FORCE_CHANGE_PASSWORD, com qualquer um dos comandos CLI, (incluindo esta resposta) tente 'admin-disable-user' e depois 'admin-enable-user' ou use o console. Em seguida, use este processo ou use o fluxo normal de redefinição de senha. Às vezes, um usuário irá 'expirar' se não efetuar login no cognito dentro do limite predefinido. (padrão de 7 dias, eu acho)
confortável hoje,
Tentei com CLI e dentro de um lambda, recebi este erro: Atributos inválidos fornecidos, nome ausente
kolodi
1
@misher você está obtendo isso devido aos atributos obrigatórios. você pode incluí-los na chamada, mas a sintaxe é um pouco estranha:--challenge-responses NEW_PASSWORD=password,USERNAME=username,userAttributes.picture=picture,userAttributes.name=name
edzillion
102

Finalmente foi adicionado ao AWSCLI: https://docs.aws.amazon.com/cli/latest/reference/cognito-idp/admin-set-user-password.html

Você pode alterar a senha de um usuário e atualizar o status usando:

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

Antes de usar isso, você pode precisar atualizar seu AWS CLI usando:

pip3 install awscli --upgrade

joe
fonte
15
Esta é a solução mais recente e eficiente!
donlys
9
Esta deve ser a resposta.
Sr. Young
6
Melhor e mais fácil solução em 2020. Obrigado!
tudor
23

Basta adicionar este código depois de onSuccess: function (result) { ... },dentro da função de login. Seu usuário terá então o status 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);
}
Assado em Meio
fonte
1
Isso funciona para mim. Você pode até passar a senha atual se não quiser alterá-la.
mvandillen
Recursion FTW! obrigado! (a recursão é thisno desafio de senha completo)
Paul S
22

Você pode alterar o status do usuário FORCE_CHANGE_PASSWORDchamando respondToAuthChallenge()o usuário desta forma:

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
});

Depois disso, você verá no console que o user3status é CONFIRMED.

Ariel Araza
fonte
1
Não entendo como você chegou aqui. O que você ligou para conseguir a Sessão que você XXX deu? Quando eu chamo adminInitiateAuth, recebo um erro dizendo UserNotFoundException.
Ryan Shillington
3
Desculpe se essa resposta não foi muito clara. Aqui estão mais detalhes: 1. O Pool de usuários tem um cliente chamado 'your_own3j63rs8j16bxxxsto25db00obh' que é criado SEM uma chave secreta gerada. O código acima não funcionará se uma chave for atribuída ao cliente. 2) A chave da sessão é o valor retornado pela chamadacognitoidentityserviceprovider.adminInitiateAuth({ AuthFlow: 'ADMIN_NO_SRP_AUTH', ClientId: 'your_own3j63rs8j16bxxxsto25db00obh', UserPoolId: 'us-east-1_DtNSUVT7n', AuthParameters: { USERNAME: 'user3', PASSWORD: 'original_password' } }, callback);
Ariel Araza
3) user3foi criado no console e inicialmente recebeu a senha'original_password'
Ariel Araza
ESTÁ BEM. Eu entendo agora porque eu estava recebendo uma UserNotFoundException. Era porque eu estava usando um alias como nome de usuário para fazer o login, que funciona bem na API JS, mas aparentemente não funciona com adminInitiateAuth. Obrigado Ariel Araza, agradeço sua ajuda.
Ryan Shillington
Yay! Eu finalmente consegui fazer isso funcionar. Obrigado! Obrigada Ariel!
Ryan Shillington
11

não tenho certeza se você ainda está lutando com isso, mas para criar um grupo de usuários de teste apenas, usei o awsclicomo:

  1. Use o subcomando de inscrição do cognito-idp para criar o usuário
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 o usuário usando admin-confirm-sign-up
aws cognito-idp admin-confirm-sign-up \
--user-pool-id %aws_user_pools_web_client_id% \
--username %email_address%
Björn W
fonte
5

Se você estiver tentando alterar o status de administrador no console. Em seguida, siga as etapas abaixo após criar o usuário.

  1. No Cognito, goto -> "gerenciar pool de usuários" ->
  2. Vá para "Configurações do cliente do aplicativo" na seção de integração do aplicativo.
  3. Verifique os itens abaixo i) Grupo de usuários Cognito ii) Concessão do código de autorização iii) Concessão implícita iv) telefone v) e-mail vi) openid vii) aws.cognito.signin.user.admin viii) perfil
  4. Insira o url de retorno de chamada do seu aplicativo. Se você não tiver certeza, digite, por exemplo: https://google.com e posteriormente, você pode alterá-lo para seu url de retorno de chamada
  5. clique em salvar alterações.
  6. Assim que as alterações forem salvas, clique no link "Launch Hosted UI"
  7. Insira as credenciais do novo usuário criado
  8. Redefina a senha com novas credenciais e compartilhe-as com o usuário

passo 2

passo 3 4 5 6

passo 7

passo 8

Rahil BR
fonte
5

ATUALIZAR:

Houve algumas atualizações e o cliente Amplify não é mais necessário. Depois de adminCreateUser (), você pode apenas usar

cisp.adminSetUserPassword({
  UserPoolId: pool_id,
  Username: login,
  Password: password,
  Permanent: true
}) 

[https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminSetUserPassword.html]

isso definirá o usuário como "confirmado".

ATUALIZAR:

Agora estou usando isso, traduzido para amplificar, dentro de um Lambda NodeJS:

// 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);
      });
  });
};

Estou definindo uma senha temporária e, em seguida, redefinindo-a com a senha solicitada pelo usuário.

ANTIGA POSTAGEM:

Você pode resolver isso usando o SDK amazon-cognito-identity-js autenticando com a senha temporária após a criação da conta com cognitoidentityserviceprovider.adminCreateUser()e executando cognitoUser.completeNewPasswordChallenge()dentro cognitoUser.authenticateUser( ,{newPasswordRequired})- tudo dentro da função que cria seu usuário.

Estou usando o código abaixo dentro do AWS lambda para criar contas de usuário do Cognito habilitadas. Tenho certeza de que pode ser otimizado, seja paciente comigo. Este é meu primeiro post, e ainda sou muito novo em 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 });
    });
qqan.ny
fonte
@Tom - funciona para você? algo que eu possa esclarecer?
qqan.ny
Muito melhor agora.
Tom Aranda
@ qqan.ny usa promessas e retornos de chamada ao mesmo tempo? Por quê?
Iurii Golskyi
@Iurii Golskyi - naquela época eu não sabia bem, tinha apenas começado a aprender AWS e JS do zero.
qqan.ny
4

Para Java SDK, supondo que seu cliente Cognito esteja configurado e você tenha seu usuário no estado FORCE_CHANGE_PASSWORD, você pode fazer o seguinte para que seu usuário seja CONFIRMADO ... e então autenticado normalmente.

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 ajude com os testes de integração (desculpe a formatação)

HKalsi
fonte
4

Basicamente, esta é a mesma resposta, mas para .Net C # SDK:

O seguinte fará uma criação completa do usuário admin com o nome de usuário e senha desejados. Tendo o seguinte modelo de usuário:

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

Você pode criar um usuário e torná-lo pronto para uso 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
        });
    }
Yahya Hussein
fonte
2

Eu sei que é a mesma resposta, mas pensei que poderia ajudar a Gocomunidade de desenvolvedores. basicamente, é iniciar a solicitação de autenticação, obter a sessão e responder ao desafioNEW_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 
}

Aqui está um teste de unidade:

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)

        })

    })
}
Muhammad Soliman
fonte
1

ESTÁ BEM. Finalmente tenho um código em que um administrador pode criar um novo usuário. O processo é assim:

  1. O administrador cria o usuário
  2. O usuário recebe um e-mail com sua senha temporária
  3. O usuário faz login e é solicitado a alterar sua senha

O passo 1 é a parte difícil. Este é meu código para criar um usuário no 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);
      }
    });
  }
});

Basicamente, você precisa enviar um segundo comando para forçar o e-mail a ser considerado verificado. O usuário ainda precisa acessar seu e-mail para obter a senha temporária (que também verifica o e-mail). Mas sem a segunda chamada que define o e-mail como verificado, você não receberá a chamada certa para redefinir a senha.

Ryan Shillington
fonte
Ainda não recebo o e-mail, alguma sugestão?
Vinicius
Estranho, você configurou o endereço de email corretamente no Cognito, com acesso ao SES, etc? A Cogntio não enviará e-mails apenas para as pessoas até que você verifique o endereço de e-mail para o qual está tentando enviar ou seja aprovado para enviar para qualquer pessoa.
Ryan Shillington
Recebo e-mails com código de confirmação, então as configurações estão corretas. Só o e-mail com a senha temporária nunca chega ... Acabei criando um lambda anexado na trigger de pré-cadastro para enviar o e-mail.
Vinicius