Impossible de vérifier le hachage secret pour le client dans les groupes d'utilisateurs Amazon Cognito


132

Je suis bloqué dans le processus "Groupes d'utilisateurs Amazon Cognito Identity".

J'ai essayé tous les codes possibles pour authentifier l'utilisateur dans les userpools cognito. Mais j'obtiens toujours une erreur disant "Erreur: Impossible de vérifier le hachage secret pour le client 4b ******* fd".

Voici le code:

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 réponse acceptée n'est plus valide. Les instructions pour générer un hachage secret sont ici docs.aws.amazon.com/cognito/latest/developerguide/…
jasiustasiu

Oui, et regardez la réponse @Simon Buchan ci-dessous pour une implémentation JavaScript. Cela fonctionne parfaitement.
guzmonne

Réponses:


182

Il semble qu'actuellement AWS Cognito ne gère pas parfaitement le secret client. Cela fonctionnera dans un proche avenir, mais pour l'instant, il s'agit toujours d'une version bêta.

Pour moi, cela fonctionne bien pour une application sans secret client, mais échoue pour une application avec un secret client.

Donc, dans votre pool d'utilisateurs, essayez de créer une nouvelle application sans générer de secret client. Utilisez ensuite cette application pour inscrire un nouvel utilisateur ou pour confirmer l'inscription.


14
FYI: Cela m'est arrivé juste maintenant. Cela fonctionne toujours de cette façon, janvier 2017. Lorsque j'ai créé une application sans client_secret, j'ai pu utiliser le SDK JS. Lorsque j'ai créé une application avec un client_secret, j'ai eu le même échec que dans la question d'origine.
Cheeso

5
Depuis le 21 avril 2017, cela ne fonctionnait toujours pas avec l'AWS CLI lorsque la clé secrète était activée pour App Client. aws cognito-idp admin-initate-auth \ --region ap-nord-est-1 \ --user-pool-id MY_POOL_ID \ --client-id MY_CLIENT_ID \ --auth-flow ADMIN_NO_SRP_AUTH \ --auth-parameters USERNAME = nom d'utilisateur @ gmail.com, PASSWORD = som3PassW0rd
Stanley Yong

26
Depuis janvier 2018, cela n'est toujours pas pris en charge. La documentation sur le repo Github github.com/aws/amazon-cognito-identity-js le mentionne:"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 mai 2018, même erreur dont nous avons besoin pour créer une application sans secret client.
Dileep

4
12 septembre 2018 - Même numéro. Même lorsque je n'utilise pas de client qui génère un secret, j'obtiens un 400 que l'utilisateur soit authentifié ou non. Cependant, l'application fonctionne comme prévu.
foxtrotuniform6969


37

Cela peut prendre quelques années de retard, mais décochez simplement l'option "Générer le secret client" et cela fonctionnera pour vos clients Web.

générer une option de client d'application


8
Notez que vous ne pouvez pas le modifier après la création du client, alors créez-en un nouveau si nécessaire.
URL87

Si vous créez un nouveau client d'application et que vous disposiez d'un pool d'identités (sur «Identités fédérées») qui utilise un fournisseur d'authentification Cognito, n'oubliez pas de mettre à jour le champ ID client d'application avec l'ID du nouveau client d'application.
AMS777 le

21

Puisque tout le monde a publié sa langue, voici le nœud (et cela fonctionne dans le navigateur avec browserify-crypto, utilisé automatiquement si vous utilisez webpack ou browserify):

const crypto = require('crypto');

...

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

4
c'est la meilleure solution intégrée Node.js simple et, merci @simon
Ingénieur

19

J'ai eu le même problème dans le SDK .net.

Voici comment j'ai résolu, au cas où quelqu'un d'autre en aurait besoin:

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

L'inscription ressemble alors à ceci:

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

Confirmer que cela est toujours nécessaire et fonctionne toujours dans le SDK AWS .NET v3.5 (préversion).
pieSquared le

13

Pour toute personne intéressée à utiliser AWS Lambda pour inscrire un utilisateur à l'aide du kit AWS JS SDK, voici les étapes que j'ai suivies:

Créez une autre fonction lambda en python pour générer la clé:

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

Appelez la fonction via la fonction nodeJS dans AWS. La signature a servi de hachage secret pour Cognito

Remarque: La réponse est largement basée sur la réponse de George Campbell dans le lien suivant: Calcul d'un hachage SHA avec une chaîne + clé secrète en python


12

Solution pour golang. Il semble que cela devrait être ajouté au 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

Solution pour NodeJS avec SecretHash

Il semble idiot qu'AWS ait supprimé la clé secrète du SDK car elle ne sera pas exposée dans NodeJS.

Je l'ai fait fonctionner dans NodeJS en interceptant fetch et en ajoutant la clé hachée en utilisant la réponse 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 (Forked et édité pour NodeJS depuis 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,
}

J'ai pu m'inscrire en suivant votre procédure, mais je ne parviens pas à me connecter à l'aide de cette procédure. Y a-t-il des modifications à apporter pour vous connecter? Ce sera très utile si vous pouviez l'ajouter ici. Merci d'avance.
Vinay Wadagavi le

8

Amazon mentionne comment calculer les valeurs SecretHash pour Amazon Cognito dans sa documentation avec le code d'application Java. Ici, ce code fonctionne avec le SDK Python boto 3 .

détails du client de l'application

Vous pouvez trouver votre App clientsmenu latéral gauche sous General settings. Obtenez-les App client idet App client secretcréez SECRET_HASH. Pour votre meilleure compréhension, j'ai commenté toutes les sorties de chaque ligne.

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=

Dans la documentation de boto 3 , nous pouvons voir beaucoup de temps poser des questions SECRET_HASH. Donc, les lignes de code ci-dessus vous aident à créer cela SECRET_HASH.

Si vous ne souhaitez pas utiliser, SECRET_HASHdécochez simplement Generate client secretlors de la création d'une application.

nouvelle application créer


1
Pour moi, cela ne fonctionnait que si je commutais msg = bytes (app_client_id + username, 'latin-1') à msg = bytes (username + app_client_id, 'latin-1'). Pour être clair, j'ai changé l'ordre du clientId et du nom d'utilisateur pour que le nom d'utilisateur apparaisse en premier.
Josh Wolff

1
Merci beaucoup @JoshWolff, j'ai par erreur échangé app_client_idet username. Mais j'affiche la sortie correcte sous forme de commentaire qui s'affiche en fonction du username+ app_client_id. Encore et encore merci beaucoup.
Kushan Gunasekera

1
Aucun problème du tout! @Kushan Gunasekera
Josh Wolff

7

En Java, vous pouvez utiliser ce code:

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

Où utilisez-vous ce hachage secret dans le SDK en plus de le sortir à l'écran?
Aaron

1
Quelqu'un peut-il pointer vers des documents AWS en ligne où l'authentification par rapport au secret client est expliquée? Les encodages de signature base64 / sha256 sont des solutions convaincantes - mais sans valeur à moins qu'ils ne soient explicitement conformes aux documents AWS expliquant comment s'authentifier par rapport au secret client.
Kode Charlie

6

ceci est un exemple de code php que j'utilise pour générer le hachage secret

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

dans ce cas, le résultat est:

DdSuILDJ2V84zfOChcn6TfgmlfnHsUYq0J6c01QV43I=

5

pour JAVA et .NET, vous devez transmettre le secret a dans les paramètres d'authentification avec le nom 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
};

Et ça devrait marcher.


3

C ++ avec le Framework Qt

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

Il existe peut-être une version plus compacte, mais cela fonctionne pour Ruby, en particulier dans Ruby on Rails sans avoir besoin de quoi que ce soit:

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

Authentification Cognito

Erreur: le client d'application n'est pas configuré pour le secret mais un hachage secret a été reçu

Fournir secretKey comme nul a fonctionné pour moi. Les informations d'identification fournies comprennent: -

  • CognitoIdentityUserPoolRegion (région)
  • 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)
    

Toutes les choses ci-dessus fonctionnent avec l'exemple de code lié ci-dessous.

Exemple de code AWS: https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoYourUserPools-Sample/Swift

Faites-moi savoir si cela ne fonctionne pas pour vous.


c'est un lien mort
Jpnh

0

Voici ma 1 commande, et cela fonctionne (confirmé :))

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}"
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.