amazon web services - sha256sum - No se puede verificar el hash secreto para el cliente en Amazon Cognito Userpools
sha256sum que es (14)
Estoy atascado 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);
});
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();
};
Como todos los demás han publicado su idioma, aquí está el nodo (y funciona en el navegador con
browserify-crypto
, que se usa automáticamente si usa webpack o browserify):
const crypto = require(''crypto'');
...
crypto.createHmac(''SHA256'', clientSecret)
.update(username + clientId)
.digest(''base64'')
De acuerdo con los documentos: http://docs.aws.amazon.com/cognito/latest/developerguide/setting-up-the-javascript-sdk.html
El SDK de Javascript no admite aplicaciones con un secreto de cliente.
Las instrucciones ahora indican que debe desmarcar "Generar secreto de cliente" al crear la aplicación para el grupo de usuarios.
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);
}
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:
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;
}
}
}
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
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.
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))
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))
}
Tuve el mismo problema en el SDK de .net.
Así es como lo resolví, en caso de que alguien más lo necesite:
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;
}
}
Registrarse se ve así:
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()
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=
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.
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 .
Puede encontrar sus
App clients
en el menú del lado izquierdo en
General settings
.
Obtenga la
App client id
y el
App client secret
para 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 del
boto 3
, podemos ver mucho tiempo preguntando sobre
SECRET_HASH
.
Entonces, las líneas de código anteriores lo ayudan a crear este
SECRET_HASH
.
Si no desea utilizar
SECRET_HASH
simplemente desactive
Generate client secret
al crear una aplicación.
Solución para NodeJS con SecretHash
Parece una tontería 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,
}