Неможливо перевірити секретний хеш для клієнта в Amazon Cognito Userpools


131

Я застряг у процесі "басейни користувачів ідентичності 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. Це прекрасно працює.
гузмонне

Відповіді:


182

Схоже, що в даний час AWS Cognito не справляється із секретом клієнта ідеально. Він буде працювати найближчим часом, але поки це бета-версія.

Для мене це працює чудово для програми без секрету клієнта, але не працює для програми із секретом клієнта.

Тож у вашому пулі користувачів спробуйте створити новий додаток, не створюючи клієнтську таємницю. Потім використовуйте цей додаток для реєстрації нового користувача або для підтвердження реєстрації.


14
FYI: Це зі мною щойно трапилося. Це все ще працює таким чином, січень 2017. Коли я створив додаток без client_secret, я зміг використовувати JS SDK. Коли я створив додаток з client_secret, у мене виникла така ж помилка, як і в оригінальному запитанні.
Cheeso

5
Станом на 21 квітня 2017 року, він все ще не працює з використанням AWS CLI, коли секретний ключ був включений для клієнта додатка. 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-settings USERNAME = ім'я користувача @ gmail.com, PASSWORD = som3PassW0rd
Стенлі Йон

26
Станом на січень 2018 року це все ще не підтримується. У документації на Github repo github.com/aws/amazon-cognito-identity-js згадується це:"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 2018, та сама помилка, яку нам потрібно створити додаток без секрету клієнта.
Занурений

4
12 вересня 2018 року - той самий випуск. Навіть коли не використовується клієнт, який генерує секрет, я отримую 400, незалежно від того, чи користувач має автентифікацію. Незважаючи на це, додатки функціонують як очікувалося.
foxtrotuniform6969

70

Відповідно до Документів: http://docs.aws.amazon.com/cognito/latest/developerguide/setting-up-the-javascript-sdk.html

SDK Javascript не підтримує програми з секретом клієнта.

В інструкціях тепер зазначено, що потрібно знімати прапорець "Створити секрет клієнта" під час створення програми для Басейну користувачів.


Це працювало для мене за допомогою Node.js на стороні сервера. Дякую Док!
Рік

37

Це може запізнитися на кілька років, але просто зніміть прапорець "Створити таємницю клієнта" та це буде працювати для ваших веб-клієнтів.

згенерувати клієнтський варіант програми


8
Зауважте, що ви не можете редагувати його після створення клієнта, тому створіть новий, якщо потрібно.
URL87

Якщо ви створили нового клієнта додатка, і у вас був пул ідентифікації (у розділі "Об'єднані ідентичності"), який використовує постачальника автентифікації Cognito, не забудьте оновити поле ідентифікатора клієнта додатка з ідентифікатором нового клієнта програми.
AMS777

21

Оскільки всі інші опублікували свою мову, ось вузол (і він працює у веб-переглядачі browserify-crypto, автоматично використовується, якщо ви використовуєте веб-пакет або переглядаєте):

const crypto = require('crypto');

...

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

4
це простий і найкращий Node.js, вбудований у рішення, дякую @simon
інженер

19

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

Ось як я вирішив, на випадок, якщо комусь це потрібно:

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

Підтвердження того, що це все ще потрібно і все ще працює в v3.5 AWS .NET SDK (попередній перегляд).
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


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

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

Мені вдалося зареєструватися після вашої процедури, але я не можу ввійти, використовуючи цю процедуру. Чи є якісь зміни, які потрібно зробити для входу? Буде дуже корисно, якщо ви можете додати їх тут. Заздалегідь спасибі.
Vinay Wadagavi

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

Де ви використовуєте цей секретний хеш у SDK, окрім виведення його на екран?
Аарон

1
Чи може хтось вказати на будь-які документи AWS в Інтернеті, де пояснюється автентифікація проти клієнтської таємниці? Кодування підписів base64 / sha256 є переконливими рішеннями, але нікчемними, якщо вони явно не відповідають документам AWS, що визначають автентифікацію проти клієнтської таємниці.
Коде Чарлі

7

Amazon згадують, як Computing 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=

У документації boto 3 ми можемо побачити багато часу, про який потрібно запитати SECRET_HASH. Отже, вище кодові рядки допоможуть вам створити це SECRET_HASH.

Якщо ви не хочете використовувати, SECRET_HASHзніміть прапорець Generate client secretпід час створення програми.

створення нового додатка


1
Для мене це спрацювало лише в тому випадку, якщо я перейшов msg = байти (app_client_id + ім'я користувача, 'latin-1') на msg = байти (ім'я користувача + app_client_id, 'latin-1'). Щоб було зрозуміло, я змінив порядок clientId та ім’я користувача таким чином, щоб ім’я користувача з’явилося першим.
Джош

1
Велике спасибі @JoshWolff, я помилково обміняю app_client_idі username. Але я показую правильний вихід як коментар, який відображається відповідно до username+ app_client_id. Знову і знову дуже дякую.
Кушань Гунасекера

1
Не проблема взагалі! @Kushan Gunasekera
Josh Wolff

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 вам потрібно передати секрет має в параметрах auth з назвою 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();
};

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 Authentication

Помилка: Клієнт додатка не налаштований для секретного, але секретний хеш отримано

Надання секретного ключа, як нуль працювало на мене. Надані повноваження включають:

  • CognitoIdentityUserPoolRegion (region)
  • 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}"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.