Não foi possível verificar o hash secreto do cliente no Amazon Cognito Userpools

131

Estou preso no processo "Conjuntos de usuários do Amazon Cognito Identity".

Eu tentei todos os códigos possíveis para autenticar usuários em cognito userpools. Mas sempre recebo o erro "Erro: não é possível verificar o hash secreto do cliente 4b ******* fd".

Aqui está o código:

AWS.config.region = 'us-east-1'; // Region
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: 'us-east-1:b64bb629-ec73-4569-91eb-0d950f854f4f'
});

AWSCognito.config.region = 'us-east-1';
AWSCognito.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: 'us-east-1:b6b629-er73-9969-91eb-0dfffff445d'
});

AWSCognito.config.update({accessKeyId: 'AKIAJNYLRONAKTKBXGMWA', secretAccessKey: 'PITHVAS5/UBADLU/dHITesd7ilsBCm'})

var poolData = { 
    UserPoolId : 'us-east-1_l2arPB10',
    ClientId : '4bmsrr65ah3oas5d4sd54st11k'
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);

var userData = {
     Username : '[email protected]',
     Pool : userPool
};

var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);

cognitoUser.confirmRegistration('123456', true,function(err, result) {
if (err) {
    alert(err);
    return;
}
console.log('call result: ' + result);
});
Ronak Patel
fonte
9
A resposta aceita NÃO é mais válida. Instruções de como gerar hash de segredo está aqui docs.aws.amazon.com/cognito/latest/developerguide/...
jasiustasiu
Sim, e veja a resposta do @Simon Buchan abaixo para uma implementação do JavaScript. Funciona perfeitamente.
guzmonne

Respostas:

179

Parece que atualmente o AWS Cognito não manipula perfeitamente o segredo do cliente. Ele funcionará em um futuro próximo, mas, por enquanto, ainda é uma versão beta.

Para mim, está funcionando bem para um aplicativo sem um segredo do cliente, mas falha em um aplicativo com um segredo do cliente.

Portanto, no seu pool de usuários, tente criar um novo aplicativo sem gerar um segredo do cliente. Em seguida, use esse aplicativo para inscrever um novo usuário ou confirmar o registro.

thomas.g
fonte
14
FYI: Isso aconteceu comigo agora. Ainda está funcionando dessa maneira, janeiro de 2017. Quando criei um aplicativo sem client_secret, pude usar o JS SDK. Quando criei um aplicativo com um client_secret, obtive o mesmo problema que na pergunta original.
Cheeso
5
Em 21 de abril de 2017, ele ainda não funcionava usando a CLI da AWS quando a chave secreta foi ativada para o App Client. aws cognito-idp admin-initiate-auth \ - região ap-nordeste-1 \ - id do pool de usuários MY_POOL_ID \ - id do cliente MY_CLIENT_ID \ --auth-flow ADMIN_NO_SRP_AUTH \ --auth-parameters USERNAME = nome de usuário @ gmail.com, PASSWORD = som3PassW0rd
Stanley Yong
26
Em janeiro de 2018, isso ainda não é suportado. A documentação no repositório Github github.com/aws/amazon-cognito-identity-js menciona:"When creating the App, the generate client secret box must be unchecked because the JavaScript SDK doesn't support apps that have a client secret."
kakoma
5
May19 2018, mesmo erro, precisamos criar um aplicativo sem o segredo do cliente.
Dileep
4
12 de setembro de 2018 - mesma edição. Mesmo quando não estou usando um cliente que gera um segredo, recebo 400 se o usuário está ou não autenticado. O aplicativo funciona como esperado, apesar disso.
foxtrotuniform6969
70

De acordo com o Documentos: http://docs.aws.amazon.com/cognito/latest/developerguide/setting-up-the-javascript-sdk.html

O Javascript SDK não suporta aplicativos com um segredo do cliente.

As instruções agora indicam que você precisa desmarcar a opção "Gerar segredo do cliente" ao criar o aplicativo para o pool de usuários.

Dr Douglas GhD
fonte
Isso funcionou para mim usando o Node.js no lado do servidor. Obrigado Doc!
21318 Rick
37

Isso pode demorar alguns anos, mas desmarque a opção "Gerar segredo do cliente" e funcionará para seus clientes da Web.

opção de gerar aplicativo cliente

Tiisetso Tjabane
fonte
8
Observe que você não pode editá-lo após a criação do cliente; portanto, crie um novo, se necessário.
URL87
Se você criar um novo cliente de aplicativo e tiver um pool de identidades (em "Identidades Federadas") que use um provedor de autenticação Cognito, lembre-se de atualizar o campo de identificação do cliente de aplicativo com o ID do novo cliente de aplicativo.
AMS777 9/01
21

Como todos os outros postaram seu idioma, aqui está o nó (e ele funciona no navegador browserify-crypto, usado automaticamente se você usar o webpack ou o browserify):

const crypto = require('crypto');

...

crypto.createHmac('SHA256', clientSecret)
  .update(username + clientId)
  .digest('base64')
Simon Buchan
fonte
4
isso é simples e melhores Node.js construídas em solução, graças @simon
Engenheiro
19

Eu tive o mesmo problema no SDK .net.

Aqui está como eu resolvi, caso mais alguém precise:

public static class CognitoHashCalculator
{
    public static string GetSecretHash(string username, string appClientId, string appSecretKey)
    {
        var dataString = username + appClientId;

        var data = Encoding.UTF8.GetBytes(dataString);
        var key = Encoding.UTF8.GetBytes(appSecretKey);

        return Convert.ToBase64String(HmacSHA256(data, key));
    }

    public static byte[] HmacSHA256(byte[] data, byte[] key)
    {
        using (var shaAlgorithm = new System.Security.Cryptography.HMACSHA256(key))
        {
            var result = shaAlgorithm.ComputeHash(data);
            return result;
        }
    }
}

A inscrição fica assim:

public class CognitoSignUpController
{
    private readonly IAmazonCognitoIdentityProvider _amazonCognitoIdentityProvider;

    public CognitoSignUpController(IAmazonCognitoIdentityProvider amazonCognitoIdentityProvider)
    {
        _amazonCognitoIdentityProvider = amazonCognitoIdentityProvider;
    }

    public async Task<bool> SignUpAsync(string userName, string password, string email)
    {
        try
        {
            var request = CreateSignUpRequest(userName, password, email);
            var authResp = await _amazonCognitoIdentityProvider.SignUpAsync(request);

            return true;
        }
        catch
        {
            return false;
        }
    }

    private static SignUpRequest CreateSignUpRequest(string userName, string password, string email)
    {
        var clientId = ConfigurationManager.AppSettings["ClientId"];
        var clientSecretId = ConfigurationManager.AppSettings["ClientSecretId"];

        var request = new SignUpRequest
        {
            ClientId = clientId,
            SecretHash = CognitoHashCalculator.GetSecretHash(userName, clientId, clientSecretId),
            Username = userName,
            Password = password,
        };

        request.UserAttributes.Add("email", email);
        return request;
    }
}
Ron Sijm
fonte
Confirmando que isso ainda é necessário e ainda funciona no SDK do AWS .NET da v3.5 (visualização).
pieSquared
13

Para quem estiver interessado em usar o AWS Lambda para inscrever um usuário usando o AWS JS SDK, siga estas etapas:

Crie outra função lambda em python para gerar a chave:

import hashlib
import hmac
import base64

secretKey = "key"
clientId = "clientid"
digest = hmac.new(secretKey,
                  msg=username + clientId,
                  digestmod=hashlib.sha256
                 ).digest()
signature = base64.b64encode(digest).decode()

Chame a função por meio da função nodeJS na AWS. A assinatura agiu como o hash secreto do Cognito

Nota: A resposta é baseada fortemente na resposta de George Campbell no seguinte link: Calculando um hash SHA com uma string + chave secreta em python

Molezz
fonte
12

Solução para golang. Parece que isso deve ser adicionado ao SDK.

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
)

func SecretHash(username, clientID, clientSecret string) string {
    mac := hmac.New(sha256.New, []byte(clientSecret))
    mac.Write([]byte(username + ClientID))
    return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}
syvex
fonte
8

Solução para NodeJS com SecretHash

Parece bobagem que a AWS tenha removido a chave secreta do SDK, pois ela não será exposta no NodeJS.

Eu consegui trabalhar no NodeJS interceptando a busca e adicionando a chave de hash usando a resposta de @Simon Buchan .

cognito.js

import { CognitoUserPool, CognitoUserAttribute, CognitoUser } from 'amazon-cognito-identity-js'
import crypto from 'crypto'
import * as fetchIntercept from './fetch-intercept'

const COGNITO_SECRET_HASH_API = [
  'AWSCognitoIdentityProviderService.ConfirmForgotPassword',
  'AWSCognitoIdentityProviderService.ConfirmSignUp',
  'AWSCognitoIdentityProviderService.ForgotPassword',
  'AWSCognitoIdentityProviderService.ResendConfirmationCode',
  'AWSCognitoIdentityProviderService.SignUp',
]

const CLIENT_ID = 'xxx'
const CLIENT_SECRET = 'xxx'
const USER_POOL_ID = 'xxx'

const hashSecret = (clientSecret, username, clientId) => crypto.createHmac('SHA256', clientSecret)
  .update(username + clientId)
  .digest('base64')

fetchIntercept.register({
  request(url, config) {
    const { headers } = config
    if (headers && COGNITO_SECRET_HASH_API.includes(headers['X-Amz-Target'])) {
      const body = JSON.parse(config.body)
      const { ClientId: clientId, Username: username } = body
      // eslint-disable-next-line no-param-reassign
      config.body = JSON.stringify({
        ...body,
        SecretHash: hashSecret(CLIENT_SECRET, username, clientId),
      })
    }
    return [url, config]
  },
})

const userPool = new CognitoUserPool({
  UserPoolId: USER_POOL_ID,
  ClientId: CLIENT_ID,
})

const register = ({ email, password, mobileNumber }) => {
  const dataEmail = { Name: 'email', Value: email }
  const dataPhoneNumber = { Name: 'phone_number', Value: mobileNumber }

  const attributeList = [
    new CognitoUserAttribute(dataEmail),
    new CognitoUserAttribute(dataPhoneNumber),
  ]

  return userPool.signUp(email, password, attributeList, null, (err, result) => {
    if (err) {
      console.log((err.message || JSON.stringify(err)))
      return
    }
    const cognitoUser = result.user
    console.log(`user name is ${cognitoUser.getUsername()}`)
  })
}

export {
  register,
}

fetch-inceptor.js (bifurcado e editado para NodeJS a partir do Fork de https://github.com/werk85/fetch-intercept/blob/develop/src/index.js )

let interceptors = []

if (!global.fetch) {
  try {
    // eslint-disable-next-line global-require
    global.fetch = require('node-fetch')
  } catch (err) {
    throw Error('No fetch available. Unable to register fetch-intercept')
  }
}
global.fetch = (function (fetch) {
  return (...args) => interceptor(fetch, ...args)
}(global.fetch))

const interceptor = (fetch, ...args) => {
  const reversedInterceptors = interceptors.reduce((array, _interceptor) => [_interceptor].concat(array), [])
  let promise = Promise.resolve(args)

  // Register request interceptors
  reversedInterceptors.forEach(({ request, requestError }) => {
    if (request || requestError) {
      promise = promise.then(_args => request(..._args), requestError)
    }
  })

  // Register fetch call
  promise = promise.then(_args => fetch(..._args))

  // Register response interceptors
  reversedInterceptors.forEach(({ response, responseError }) => {
    if (response || responseError) {
      promise = promise.then(response, responseError)
    }
  })

  return promise
}

const register = (_interceptor) => {
  interceptors.push(_interceptor)
  return () => {
    const index = interceptors.indexOf(_interceptor)
    if (index >= 0) {
      interceptors.splice(index, 1)
    }
  }
}

const clear = () => {
  interceptors = []
}

export {
  register,
  clear,
}
ptimson
fonte
Consegui me inscrever seguindo seu procedimento, mas não consigo entrar usando este proc. Existe alguma modificação que precisa ser feita para entrar? Será muito útil se você puder adicioná-lo aqui. Desde já, obrigado.
Vinay Wadagavi 24/07
7

Em Java, você pode usar este código:

private String getSecretHash(String email, String appClientId, String appSecretKey) throws Exception {
    byte[] data = (email + appClientId).getBytes("UTF-8");
    byte[] key = appSecretKey.getBytes("UTF-8");

    return Base64.encodeAsString(HmacSHA256(data, key));
}

static byte[] HmacSHA256(byte[] data, byte[] key) throws Exception {
    String algorithm = "HmacSHA256";
    Mac mac = Mac.getInstance(algorithm);
    mac.init(new SecretKeySpec(key, algorithm));
    return mac.doFinal(data);
}
gandrademello
fonte
Onde você utiliza esse hash secreto no SDK, além de enviá-lo para a tela?
Aaron
1
Alguém pode apontar para qualquer documento da AWS on-line onde a autenticação no segredo do cliente é explicada? As codificações de assinatura base64 / sha256 são soluções atraentes - mas inúteis, a menos que sejam explicitamente compatíveis com os documentos da AWS, explicando como se autenticar no segredo do cliente.
precisa
7

A Amazon menciona como Computing SecretHash Values for Amazon Cognito em sua documentação com código de aplicativo Java. Aqui, esse código funciona com o boto 3 Python SDK .

detalhes do cliente do aplicativo

Você pode encontrar o seu App clientsno menu do lado esquerdo em General settings. Pegue esses App client ide App client secretcrie SECRET_HASH. Para sua melhor compreensão, comentei todas as saídas de cada linha.

import hashlib
import hmac
import base64

app_client_secret = 'u8f323eb3itbr3731014d25spqtv5r6pu01olpp5tm8ebicb8qa'
app_client_id = '396u9ekukfo77nhcfbmqnrec8p'
username = 'wasdkiller'

# convert str to bytes
key = bytes(app_client_secret, 'latin-1')  # b'u8f323eb3itbr3731014d25spqtv5r6pu01olpp5tm8ebicb8qa'
msg = bytes(username + app_client_id, 'latin-1')  # b'wasdkiller396u9ekukfo77nhcfbmqnrec8p'

new_digest = hmac.new(key, msg, hashlib.sha256).digest()  # b'P$#\xd6\xc1\xc0U\xce\xc1$\x17\xa1=\x18L\xc5\x1b\xa4\xc8\xea,\x92\xf5\xb9\xcdM\xe4\x084\xf5\x03~'
SECRET_HASH = base64.b64encode(new_digest).decode()  # UCQj1sHAVc7BJBehPRhMxRukyOoskvW5zU3kCDT1A34=

Na documentação do boto 3 , podemos ver muito tempo perguntando SECRET_HASH. Portanto, as linhas de código acima o ajudam a criar isso SECRET_HASH.

Se você não quiser usar, SECRET_HASHdesmarque a opção Generate client secretao criar um aplicativo.

novo aplicativo criado

Kushan Gunasekera
fonte
1
Para mim, isso só funcionou se eu alternasse msg = bytes (app_client_id + nome de usuário, 'latin-1') para msg = bytes (nome de usuário + app_client_id, 'latin-1'). Para esclarecer, mudei a ordem do clientId e o nome do usuário para que o nome do usuário apareça primeiro.
Josh Wolff
1
Muito obrigado @ JoshWolff, eu troco por engano app_client_ide username. Mas eu mostro a saída correta como um comentário que é exibido de acordo com o username+ app_client_id. Mais uma vez, muito obrigado.
Kushan Gunasekera
1
Não tem problema nenhum! @Kushan Gunasekera
Josh Wolff
6

este é um código php de exemplo que eu uso para gerar o hash secreto

<?php
    $userId = "aaa";
    $clientId = "bbb";
    $clientSecret = "ccc";
    $s = hash_hmac('sha256', $userId.$clientId, $clientSecret, true);
    echo base64_encode($s);
?>

neste caso, o resultado é:

DdSuILDJ2V84zfOChcn6TfgmlfnHsUYq0J6c01QV43I=
Titi Wangsa bin Damhore
fonte
5

para JAVA e .NET que você precisa para passar o segredo tem nos parâmetros de autenticação com o nome SECRET_HASH.

AdminInitiateAuthRequest request = new AdminInitiateAuthRequest
{
  ClientId = this.authorizationSettings.AppClientId,
  AuthFlow = AuthFlowType.ADMIN_NO_SRP_AUTH,
  AuthParameters = new Dictionary<string, string>
  {
    {"USERNAME", username},
    {"PASSWORD", password},
    {
      "SECRET_HASH", EncryptionHelper.GetSecretHash(username, AppClientId, AppClientSecret)
    }
  },
  UserPoolId = this.authorizationSettings.UserPoolId
};

E deve funcionar.

Shanmukhi Goli
fonte
3

C ++ com o Qt Framework

QByteArray MyObject::secretHash(
     const QByteArray& email,
     const QByteArray& appClientId, 
     const QByteArray& appSecretKey)
{
            QMessageAuthenticationCode code(QCryptographicHash::Sha256);
            code.setKey(appSecretKey);
            code.addData(email);
            code.addData(appClientId);
            return code.result().toBase64();
};
vpicaver
fonte
1

Pode haver uma versão mais compacta, mas isso funciona para o Ruby, especificamente no Ruby on Rails sem precisar exigir nada:

key = ENV['COGNITO_SECRET_HASH']
data = username + ENV['COGNITO_CLIENT_ID']
digest = OpenSSL::Digest.new('sha256')

hmac = Base64.strict_encode64(OpenSSL::HMAC.digest(digest, key, data))
Nikolay D
fonte
0

Autenticação do Cognito

Erro: o cliente do aplicativo não está configurado para segredo, mas o hash secreto foi recebido

Fornecer secretKey como nil funcionou para mim. As credenciais fornecidas incluem: -

  • CognitoIdentityUserPoolRegion (region)
  • CognitoIdentityUserPoolId (userPoolId)
  • CognitoIdentityUserPoolAppClientId (ClientId)
  • AWSCognitoUserPoolsSignInProviderKey (AccessKeyId)

    // setup service configuration
    let serviceConfiguration = AWSServiceConfiguration(region: CognitoIdentityUserPoolRegion, credentialsProvider: nil)
    
    // create pool configuration
    let poolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: CognitoIdentityUserPoolAppClientId,
                                                                    clientSecret: nil,
                                                                    poolId: CognitoIdentityUserPoolId)
    
    // initialize user pool client
    AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: poolConfiguration, forKey: AWSCognitoUserPoolsSignInProviderKey)
    

Todas as coisas acima funcionam com o exemplo de código vinculado abaixo.

Código de exemplo da AWS: https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoYourUserPools-Sample/Swift

Deixe-me saber se isso não funcionar para você.

Siddharth Kavthekar
fonte
este é um link morto
Jpnh 13/07
0

Aqui está o meu comando 1, e funciona (Confirmado :))

EMAIL="[email protected]" \
CLIENT_ID="[CLIENT_ID]" \
CLIENT_SECRET="[CLIENT_ID]" \
&& SECRET_HASH=$(echo -n "${EMAIL}${CLIENT_ID}" | openssl dgst -sha256 -hmac "${CLIENT_SECRET}" | xxd -r -p | openssl base64) \
&& aws cognito-idp ...  --secret-hash "${SECRET_HASH}"
Tuong Le
fonte