No se puede verificar el hash secreto para el cliente en Amazon Cognito Userpools

131

Estoy atrapado en el proceso "Grupos de usuarios de Amazon Cognito Identity".

Intenté todos los códigos posibles para autenticar usuarios en grupos de usuarios cognito. Pero siempre recibo un error que dice "Error: no se puede verificar el hash secreto para el cliente 4b ******* fd".

Aquí está el 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
fuente
9
La respuesta aceptada ya no es válida. Las instrucciones para generar hash secreto están aquí docs.aws.amazon.com/cognito/latest/developerguide/…
jasiustasiu el
Sí, y mira la respuesta de @Simon Buchan a continuación para una implementación de JavaScript. Funciona perfectamente
guzmonne

Respuestas:

180

Parece que actualmente AWS Cognito no maneja el secreto del cliente a la perfección. Funcionará en el futuro cercano, pero por ahora sigue siendo una versión beta.

Para mí está funcionando bien para una aplicación sin un secreto de cliente, pero falla para una aplicación con un secreto de cliente.

Entonces, en su grupo de usuarios, intente crear una nueva aplicación sin generar un secreto de cliente. Luego use esa aplicación para registrar un nuevo usuario o para confirmar el registro.

thomas.g
fuente
14
FYI: Esto me acaba de pasar, justo ahora. Todavía funciona de esta manera, enero de 2017. Cuando creé una aplicación sin client_secret, pude usar el SDK de JS. Cuando creé una aplicación con client_secret, obtuve el mismo error que en la pregunta original.
Cheeso
55
A partir del 21 de abril de 2017, todavía no funciona con la AWS CLI cuando se habilitó la clave secreta para App Client. aws cognito-idp admin-initiate-auth \ --region ap-northeast-1 \ --user-pool-id MY_POOL_ID \ --client-id MY_CLIENT_ID \ --auth-flow ADMIN_NO_SRP_AUTH \ --auth-parameters USERNAME = username @ gmail.com, CONTRASEÑA = som3PassW0rd
Stanley Yong
26
A partir de enero de 2018, esto todavía no es compatible. La documentación en el repositorio de Github github.com/aws/amazon-cognito-identity-js lo 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
55
19 de mayo de 2018, mismo error que necesitamos para crear una aplicación sin secreto de cliente.
Dileep
44
12 de septiembre de 2018 - Mismo problema. Incluso cuando no estoy usando un cliente que genera un secreto, obtengo un 400 independientemente de si el usuario está autenticado o no. Sin embargo, la aplicación funciona como se esperaba a pesar de esto.
foxtrotuniform6969
37

Esto podría demorar algunos años, pero simplemente desmarque la opción "Generar secreto de cliente" y funcionará para sus clientes web.

opción de generar cliente de aplicación

Tiisetso Tjabane
fuente
8
Tenga en cuenta que no puede editarlo después de la creación del cliente, por lo tanto, cree uno nuevo si es necesario.
URL87
Si crea un nuevo cliente de aplicación y tenía un grupo de identidades (en "Identidades federadas") que usa un proveedor de autenticación Cognito, recuerde actualizar el campo de identificación del cliente de la aplicación con la identificación del nuevo cliente de la aplicación.
AMS777
21

Como todos los demás han publicado su idioma, aquí está el nodo (y funciona en el navegador con browserify-crypto, usado automáticamente si usa webpack o browserify):

const crypto = require('crypto');

...

crypto.createHmac('SHA256', clientSecret)
  .update(username + clientId)
  .digest('base64')
Simon Buchan
fuente
44
esta es la mejor solución integrada de Node.js, gracias @simon
Ingeniero el
19

Tuve el mismo problema en el SDK de .net.

Así es como lo resolví, en caso de que alguien más lo necesite:

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

Registrarse se ve así:

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
fuente
Confirmando que esto todavía es necesario y aún funciona en el v3.5 AWS .NET SDK (versión preliminar).
PieSquared
13

Para cualquiera que esté interesado en usar AWS Lambda para inscribir a un usuario con AWS JS SDK, estos son los pasos que hice:

Cree otra función lambda en python para generar la clave:

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

Llame a la función a través de la función nodeJS en AWS. La firma actuó como el hash secreto para Cognito

Nota: La respuesta se basa en gran medida en la respuesta de George Campbell en el siguiente enlace: Cálculo de un hash SHA con una cadena + clave secreta en python

Molezz
fuente
12

Solución para golang. Parece que esto debería agregarse al 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
fuente
8

Solución para NodeJS con SecretHash

Parece tonto que AWS haya eliminado la clave secreta del SDK, ya que no estará expuesta en NodeJS.

Lo hice funcionar en NodeJS interceptando fetch y agregando la clave hash usando la respuesta 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 y editado para NodeJS de 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
fuente
Pude registrarme siguiendo su procedimiento, pero no puedo iniciar sesión con este proceso. ¿Hay alguna modificación que deba hacerse para iniciar sesión? Será muy útil si pudiera agregarlo aquí. Gracias por adelantado.
Vinay Wadagavi
7

En Java podrías 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
fuente
¿Dónde utiliza este hash secreto en el SDK además de enviarlo a la pantalla?
Aaron
1
¿Alguien puede señalar cualquier documento de AWS en línea donde se explica la autenticación contra el secreto del cliente? Las codificaciones de firma base64 / sha256 son soluciones convincentes, pero no valen nada a menos que sean explícitamente compatibles con los documentos de AWS que explican cómo autenticarse contra el secreto del cliente.
Kode Charlie
7

Amazon menciona cómo Computing SecretHash Values para Amazon Cognito en su documentación con el código de la aplicación Java. Aquí este código funciona con boto 3 Python SDK .

detalles del cliente de la aplicación

Puede encontrar su App clientsen el menú del lado izquierdo debajo General settings. Consigue esos App client idy App client secretpara crear SECRET_HASH. Para su mejor comprensión, comenté todos los resultados de cada línea.

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=

En la documentación de boto 3 , podemos ver mucho tiempo para preguntar SECRET_HASH. Entonces, las líneas de código anteriores lo ayudan a crear esto SECRET_HASH.

Si no desea usar, SECRET_HASHsimplemente desmarque Generate client secretal crear una aplicación.

nueva aplicación crear

Kushan Gunasekera
fuente
1
Para mí, esto solo funcionó si cambié msg = bytes (app_client_id + nombre de usuario, 'latin-1') a msg = bytes (nombre de usuario + app_client_id, 'latin-1'). Para que quede claro, cambié el orden del clientId y el nombre de usuario de modo que el nombre de usuario aparezca primero.
Josh Wolff
1
Muchas gracias @JoshWolff, por error intercambié app_client_idy username. Pero muestro la salida correcta como un comentario que se muestra de acuerdo con el username+ app_client_id. Una y otra vez muchas gracias.
Kushan Gunasekera
1
¡No hay problema! @Kushan Gunasekera
Josh Wolff
6

este es un código php de muestra que uso para generar el hash secreto

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

en este caso el resultado es:

DdSuILDJ2V84zfOChcn6TfgmlfnHsUYq0J6c01QV43I=
Titi Wangsa bin Damhore
fuente
5

para JAVA y .NET necesita pasar el secreto que tiene en los parámetros de autenticación con el nombre 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
};

Y debería funcionar.

Shanmukhi Goli
fuente
3

C ++ con el marco 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();
};
vpicaver
fuente
1

Puede haber una versión más compacta, pero esto funciona para Ruby, específicamente en Ruby on Rails sin tener que requerir 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
fuente
0

Autenticación Cognito

Error: el cliente de la aplicación no está configurado para secreto pero se recibió hash secreto

Proporcionar secretKey como nil funcionó para mí. Las credenciales proporcionadas incluyen: -

  • CognitoIdentityUserPoolRegion (región)
  • 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 las cosas anteriores funcionan con el ejemplo de código vinculado a continuación.

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

Avísame si eso no te funciona.

Siddharth Kavthekar
fuente
este es un enlace muerto
Jpnh
0

Aquí está mi 1 comando, y 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
fuente