Impossibile verificare l'hash segreto per il client nei pool di utenti di Amazon Cognito


132

Sono bloccato nel processo "Pool di utenti di identità di Amazon Cognito".

Ho provato tutti i codici possibili per l'autenticazione dell'utente nei pool di utenti cognito. Ma ricevo sempre un errore che dice "Errore: impossibile verificare l'hash segreto per il client 4b ******* fd".

Ecco il codice:

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 : 'ronakpatel@gmail.com',
     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);
});

9
La risposta accettata NON è più valida. Le istruzioni su come generare hash segreto sono qui docs.aws.amazon.com/cognito/latest/developerguide/…
jasiustasiu

Sì, e guarda la risposta di @Simon Buchan di seguito per un'implementazione JavaScript. Funziona perfettamente.
guzmonne

Risposte:


182

Sembra che attualmente AWS Cognito non gestisca perfettamente il segreto del client. Funzionerà nel prossimo futuro, ma per ora è ancora una versione beta.

Per me funziona bene per un'app senza un segreto client ma non funziona per un'app con un segreto client.

Quindi nel tuo pool di utenti prova a creare una nuova app senza generare un segreto client. Quindi utilizza l'app per registrare un nuovo utente o per confermare la registrazione.


14
FYI: Questo è appena successo a me, proprio ora. Funziona ancora in questo modo, gennaio 2017. Quando ho creato un'app senza client_secret, sono stato in grado di utilizzare JS SDK. Quando ho creato un'app con client_secret, ho ottenuto lo stesso errore della domanda originale.
Cheeso

5
A partire dal 21 aprile 2017, non funziona ancora utilizzando AWS CLI quando la chiave segreta è stata abilitata per App Client. aws cognito-idp admin-initiate-auth \ --region ap-nordest-1 \ --user-pool-id MY_POOL_ID \ --client-id MY_CLIENT_ID \ --auth-flow ADMIN_NO_SRP_AUTH \ --auth-parameters USERNAME = nome utente @ gmail.com, PASSWORD = som3PassW0rd
Stanley Yong

26
A partire da gennaio 2018, questo non è ancora supportato. La documentazione sul repository Github github.com/aws/amazon-cognito-identity-js lo menziona:"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
19 maggio 2018, stesso errore necessario per creare un'app senza segreto client.
Dileep

4
12 settembre 2018 - Stessa questione. Anche quando non si utilizza un client che genera un segreto, ottengo un 400 indipendentemente dall'autenticazione dell'utente. Nonostante ciò, l'app funziona come previsto.
foxtrotuniform6969


37

Potrebbe essere in ritardo di alcuni anni, ma deseleziona semplicemente l'opzione "Genera segreto client" e funzionerà per i tuoi client web.

generare l'opzione client dell'app


8
Tieni presente che non puoi modificarlo dopo la creazione del client, quindi creane uno nuovo se necessario.
URL87

Se crei un nuovo client dell'app e disponi di un pool di identità (su "Federated Identities") che utilizza un provider di autenticazione Cognito, ricorda di aggiornare il campo dell'ID del client dell'app con l'ID del nuovo client dell'app.
AMS777

21

Poiché tutti gli altri hanno pubblicato la loro lingua, ecco node (e funziona nel browser con browserify-crypto, utilizzato automaticamente se usi webpack o browserify):

const crypto = require('crypto');

...

crypto.createHmac('SHA256', clientSecret)
  .update(username + clientId)
  .digest('base64')

4
questa è la soluzione semplice e migliore integrata di Node.js, grazie @simon
Ingegnere

19

Ho avuto lo stesso problema con .net SDK.

Ecco come ho risolto, nel caso in cui qualcun altro ne abbia bisogno:

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

La registrazione è quindi simile a questa:

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

Confermando che è ancora necessario e funziona ancora nella v3.5 AWS .NET SDK (anteprima).
pieSquared

13

Per chiunque sia interessato a utilizzare AWS Lambda per registrare un utente utilizzando AWS JS SDK, questi sono i passaggi che ho seguito:

Crea un'altra funzione lambda in Python per generare la chiave:

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()

Chiama la funzione tramite la funzione nodeJS in AWS. La firma fungeva da hash segreto per Cognito

Nota: la risposta è fortemente basata sulla risposta di George Campbell nel seguente collegamento: Calcolo di un hash SHA con una stringa + chiave segreta in python


12

Soluzione per golang. Sembra che questo dovrebbe essere aggiunto all'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))
}

8

Soluzione per NodeJS con SecretHash

Sembra sciocco che AWS abbia rimosso la chiave segreta dall'SDK poiché non verrà esposta in NodeJS.

L'ho fatto funzionare in NodeJS intercettando il recupero e aggiungendo la chiave con hash usando la risposta di @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 ( biforcato e modificato per NodeJS da Fork di 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,
}

Sono riuscito a registrarmi seguendo la tua procedura, ma non riesco ad accedere utilizzando questa procedura. È necessario apportare modifiche per accedere? Sarebbe molto utile se potessi aggiungerlo qui. Grazie in anticipo.
Vinay Wadagavi

8

Amazon menziona come Computing SecretHash Values per Amazon Cognito nella sua documentazione con il codice dell'applicazione Java. Qui questo codice funziona con boto 3 Python SDK .

dettagli del client dell'app

Puoi trovare il tuo App clientsnel menu a sinistra sotto General settings. Prendi quelli App client ide App client secretcrea SECRET_HASH. Per una tua migliore comprensione ho commentato tutte le uscite di ogni singola riga.

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=

Nella documentazione di Boto 3 , possiamo vedere molte domande su SECRET_HASH. Quindi le righe di codice sopra ti aiutano a crearloSECRET_HASH .

Se non vuoi usare, SECRET_HASHdeseleziona Generate client secretquando crei un'app.

creazione di una nuova app


1
Per me, questo ha funzionato solo se ho cambiato msg = bytes (app_client_id + username, 'latin-1') in msg = bytes (username + app_client_id, 'latin-1'). Per essere chiari, ho cambiato l'ordine del clientId e del nome utente in modo che il nome utente appaia per primo.
Josh Wolff

1
Grazie mille @JoshWolff, ho scambiato per errore app_client_ide username. Ma visualizzo l'output corretto come un commento che viene visualizzato in base al username+ app_client_id. Ancora e ancora grazie mille.
Kushan Gunasekera

1
Nessun problema! @Kushan Gunasekera
Josh Wolff

7

In Java potresti usare questo codice:

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

Dove utilizzi questo hash segreto nell'SDK oltre a inviarlo sullo schermo?
Aaron

1
Chiunque può indicare qualsiasi documento AWS online in cui viene spiegata l'autenticazione in base al segreto del client? Le codifiche della firma base64 / sha256 sono soluzioni convincenti, ma inutili a meno che non siano esplicitamente conformi ai documenti AWS che spiegano come autenticarsi in base al segreto del client.
Kode Charlie,

6

questo è un codice php di esempio che utilizzo per generare l'hash segreto

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

in questo caso il risultato è:

DdSuILDJ2V84zfOChcn6TfgmlfnHsUYq0J6c01QV43I=

5

per JAVA e .NET è necessario passare il segreto ha nei parametri di autenticazione con il 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 dovrebbe funzionare.


3

C ++ con 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();
};

1

Potrebbe esserci una versione più compatta, ma funziona per Ruby, in particolare in Ruby on Rails senza dover richiedere nulla:

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

0

Autenticazione Cognito

Errore: il client dell'app non è configurato per il segreto ma è stato ricevuto l'hash segreto

Fornire secretKey come zero ha funzionato per me. Le credenziali fornite includono: -

  • CognitoIdentityUserPoolRegion (regione)
  • 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)
    

Tutte le cose precedenti funzionano con l'esempio di codice collegato di seguito.

Codice di esempio AWS: https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoYourUserPools-Sample/Swift

Fammi sapere se non funziona per te.


questo è un collegamento morto
Jpnh

0

Ecco il mio comando 1 e funziona (Confermato :))

EMAIL="EMAIL@HERE.com" \
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}"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.