Невозможно проверить секретный хэш для клиента в пулах пользователей Amazon Cognito

132

Я застрял в процессе «Пулы пользователей Amazon Cognito Identity».

Я перепробовал все возможные коды для аутентификации пользователя в пользовательских пулах когнито. Но я всегда получаю сообщение об ошибке: «Ошибка: невозможно проверить секретный хэш для клиента 4b ******* fd».

Вот код:

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
Принятый ответ больше НЕ действителен. Инструкции по созданию секретного хэша находятся здесь docs.aws.amazon.com/cognito/latest/developerguide/…
jasiustasiu
Да, и посмотрите на ответ @Simon Buchan ниже для реализации JavaScript. Работает отлично.
guzmonne

Ответы:

182

Похоже, что в настоящее время AWS Cognito плохо обрабатывает секреты клиента. Он будет работать в ближайшем будущем, но пока это все еще бета-версия.

Для меня он отлично работает для приложения без секрета клиента, но не работает для приложения с секретом клиента.

Итак, в вашем пользовательском пуле попробуйте создать новое приложение без создания секрета клиента. Затем используйте это приложение для регистрации нового пользователя или подтверждения регистрации.

thomas.g
источник
14
К вашему сведению: это случилось со мной только что. Это все еще работает в январе 2017 года. Когда я создал приложение без client_secret, я смог использовать JS SDK. Когда я создал приложение с client_secret, я получил ту же ошибку, что и в исходном вопросе.
Cheeso
5
По состоянию на 21 апреля 2017 г. он по-прежнему не работает с использованием интерфейса командной строки AWS, если для клиента приложения был включен секретный ключ. 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, PASSWORD = som3PassW0rd
Стэнли Йонг
26
По состоянию на январь 2018 года это все еще не поддерживается. В документации по "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."
репозиторию
5
19 мая 2018 года, такая же ошибка, нам нужно создать приложение без секрета клиента.
Дилип
4
12 сентября 2018 г. - та же проблема. Даже если не используется клиент, который генерирует секрет, я получаю 400 независимо от того, аутентифицирован пользователь или нет. Однако, несмотря на это, приложение работает нормально.
foxtrotuniform6969
70

Согласно Документам: http://docs.aws.amazon.com/cognito/latest/developerguide/setting-up-the-javascript-sdk.html

SDK Javascript не поддерживает приложения с секретным кодом клиента.

Теперь в инструкциях указано, что вам нужно снять флажок «Создать секрет клиента» при создании приложения для пула пользователей.

Д-р Дуглас GhD
источник
У меня это сработало, используя Node.js на стороне сервера. Спасибо, Док!
Рик
37

Это может произойти с опозданием на несколько лет, но просто снимите флажок «Создать секрет клиента», и он будет работать для ваших веб-клиентов.

создать вариант клиента приложения

Тийсецо Тьябане
источник
8
Обратите внимание, что вы не можете редактировать его после создания клиента, поэтому при необходимости создайте новый.
URL87
Если вы создаете новый клиент приложения и у вас есть пул удостоверений (в «Федеративных удостоверениях»), который использует поставщик проверки подлинности Cognito, не забудьте обновить поле идентификатора клиента приложения идентификатором нового клиента приложения.
AMS777,
21

Поскольку все остальные разместили свой язык, вот узел (и он работает в браузере browserify-crypto, автоматически используется, если вы используете webpack или browserify):

const crypto = require('crypto');

...

crypto.createHmac('SHA256', clientSecret)
  .update(username + clientId)
  .digest('base64')
Саймон Бьюкен
источник
4
это простое и лучшее встроенное решение Node.js, спасибо @simon
инженер
19

У меня была такая же проблема в SDK .net.

Вот как я решил, если это кому-то еще понадобится:

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

Тогда регистрация выглядит так:

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;
    }
}
Рон Сиджм
источник
Подтверждаем, что это все еще необходимо и все еще работает в пакете SDK AWS .NET v3.5 (предварительная версия).
pieSquared
13

Для всех, кто заинтересован в использовании AWS Lambda для регистрации пользователя с помощью AWS JS SDK, вот шаги, которые я сделал:

Создайте еще одну лямбда-функцию в Python для генерации ключа:

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

Вызовите функцию через функцию nodeJS в AWS. Подпись служила секретным хешем для Cognito.

Примечание. Ответ во многом основан на ответе Джорджа Кэмпбелла по следующей ссылке: Расчет хэша SHA со строкой + секретным ключом в python.

Molezz
источник
12

Решение для golang. Похоже, это нужно добавить в 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
источник
8

Решение для NodeJS с SecretHash

Кажется глупым, что AWS удалила секретный ключ из SDK, поскольку он не будет отображаться в NodeJS.

Я заставил его работать в NodeJS, перехватив выборку и добавив хешированный ключ, используя ответ @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 ( разветвленный и отредактированный для NodeJS из Fork 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
источник
Мне удалось зарегистрироваться после вашей процедуры, но я не могу войти в систему с помощью этой процедуры. Есть ли какие-либо изменения, которые необходимо сделать для входа в систему? Было бы очень полезно, если бы вы могли добавить его сюда. Заранее спасибо.
Винай Вадагави,
8

Amazon упоминает, как вычислять значения SecretHash для Amazon Cognito в своей документации с кодом Java-приложения. Здесь этот код работает с Boto 3 Python SDK .

сведения о клиенте приложения

Вы можете найти его App clientsв меню слева под General settings. Получите те App client idи App client secretсоздайте SECRET_HASH. Для вашего лучшего понимания я закомментировал все выходы каждой строки.

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=

В документации по бото 3 мы видим, что много времени спрашивают SECRET_HASH. Итак, приведенные выше строки кода помогут вам создать это SECRET_HASH.

Если вы не хотите использовать, SECRET_HASHпросто снимите флажок Generate client secretпри создании приложения.

создание нового приложения

Кушан Гунасекера
источник
1
Для меня это сработало, только если я переключил msg = bytes (app_client_id + username, 'latin-1') на msg = bytes (username + app_client_id, 'latin-1'). Для ясности, я поменял порядок clientId и имени пользователя так, чтобы имя пользователя появлялось первым.
Джош Вольф
1
Большое спасибо @JoshWolff, я по ошибке поменял местами app_client_idи username. Но я показываю правильный результат в виде комментария, который отображается в соответствии со знаком username+ app_client_id. Снова и снова большое спасибо.
Кушан Гунасекера
1
Совершенно никаких проблем! @Kushan Gunasekera
Джош Вольф
7

В Java вы можете использовать этот код:

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
источник
Где вы используете этот секретный хеш в SDK, кроме вывода его на экран?
Аарон
1
Может ли кто-нибудь указать на какие-либо документы AWS в Интернете, где объясняется аутентификация по секрету клиента? Кодировки подписи base64 / sha256 являются убедительными решениями, но бесполезны, если они явно не соответствуют документам AWS, в которых разъясняется, как аутентифицироваться по секрету клиента.
Kode Charlie
6

это образец кода php, который я использую для генерации секретного хэша

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

в этом случае результат:

DdSuILDJ2V84zfOChcn6TfgmlfnHsUYq0J6c01QV43I=
Тити Ванса бин Дамхор
источник
5

для JAVA и .NET вам необходимо передать секрет в параметрах аутентификации с именем 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
};

И это должно работать.

Шанмухи Голи
источник
3

C ++ с 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
источник
1

Может быть более компактная версия, но это работает для Ruby, особенно в Ruby on Rails, без каких-либо требований:

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

Cognito аутентификация

Ошибка: клиент приложения не настроен для секрета, но секретный хеш был получен

Предоставление secretKey as nil сработало для меня. Предоставляемые учетные данные включают: -

  • CognitoIdentityUserPoolRegion (регион)
  • 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)
    

Все вышеперечисленное работает с приведенным ниже примером кода.

Пример кода AWS: https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoYourUserPools-Sample/Swift

Сообщите мне, если это не сработает для вас.

Сиддхарт Кавтекар
источник
это мертвая ссылка
Jpnh
0

Вот моя 1 команда, и она работает (подтверждено :))

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}"
Туонг Ле
источник