Amazon Cognito 사용자 풀에서 클라이언트에 대한 비밀 해시를 확인할 수 없음


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

예, JavaScript 구현에 대해서는 아래 @Simon Buchan 답변을 참조하십시오. 완벽하게 작동합니다.
guzmonne

답변:


182

현재 AWS Cognito는 클라이언트 암호를 완벽하게 처리하지 못하는 것 같습니다. 가까운 장래에 작동하지만 현재로서는 아직 베타 버전입니다.

나에게는 클라이언트 비밀이없는 앱에서는 잘 작동하지만 클라이언트 비밀이있는 앱에서는 실패합니다.

따라서 사용자 풀에서 클라이언트 암호를 생성하지 않고 새 앱을 만들어보십시오. 그런 다음 해당 앱을 사용하여 새 사용자를 등록하거나 등록을 확인합니다.


14
참고 : 방금 나에게 이런 일이 일어났습니다. 2017 년 1 월에도 여전히 이런 방식으로 작동합니다. client_secret없이 앱을 만들었을 때 JS SDK를 사용할 수있었습니다. client_secret으로 앱을 만들었을 때 원래 질문과 동일한 오류가 발생했습니다.
Cheeso

5
2017 년 4 월 21 일부터 App Client에 대해 비밀 키가 활성화 된 경우 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-parameters USERNAME = username @ gmail.com, PASSWORD = som3PassW0rd
Stanley Yong

26
2018 년 1 월부터는 아직 지원되지 않습니다. Github 리포지토리 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
2018 년 5 월 19 일, 클라이언트 시크릿없이 앱을 생성해야하는 동일한 오류입니다.
Dileep

4
2018 년 9 월 12 일-같은 호. 비밀을 생성하는 클라이언트를 사용하지 않아도 사용자 인증 여부에 관계없이 400 점을받습니다. 그러나 앱이 예상대로 작동합니다.
foxtrotuniform6969


37

이것은 몇 년 늦을 수 있지만 "클라이언트 비밀 생성"옵션을 선택 취소하면 웹 클라이언트에서 작동합니다.

앱 클라이언트 옵션 생성


8
클라이언트가 만든 후에는 수정할 수 없으므로 필요한 경우 새로 만드세요.
URL87

새 앱 클라이언트를 생성하고 Cognito 인증 공급자를 사용하는 자격 증명 풀 ( "Federated Identities")이있는 경우 새 앱 클라이언트의 ID로 앱 클라이언트 ID 필드를 업데이트해야합니다.
AMS777

21

다른 모든 사람들이 자신의 언어를 게시했기 때문에 여기 노드가 있습니다 (그리고 브라우저에서 작동하며 browserify-cryptowebpack 또는 browserify를 사용하는 경우 자동으로 사용됨).

const crypto = require('crypto');

...

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

4
이것은 간단하고 최고의 Node.js 내장 솔루션입니다. 감사합니다. @simon
Engineer

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를 사용하여 사용자를 등록하는 데 관심이있는 사람을 위해 다음 단계를 수행했습니다.

파이썬에서 다른 람다 함수를 만들어 키를 생성합니다.

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

AWS에서 nodeJS 함수를 통해 함수를 호출합니다. 서명은 Cognito의 비밀 해시 역할을했습니다.

참고 : 대답은 다음 링크에있는 George Campbell의 대답을 기반으로합니다. 파이썬에서 문자열 + 비밀 키로 SHA 해시 계산


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

SecretHash를 사용한 NodeJS 솔루션

AWS가 NodeJS에 노출되지 않기 때문에 SDK에서 비밀 키를 제거한 것은 어리석은 것 같습니다.

가져 오기를 가로 채서 @Simon Buchan 의 답변을 사용하여 해시 키를 추가하여 NodeJS에서 작동하도록했습니다 .

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 ( https://github.com/werk85/fetch-intercept/blob/develop/src/index.js 포크에서 NodeJS에 대해 포크 및 편집 됨 )

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에서이 비밀 해시를 어디에 활용합니까?
Aaron

1
누구나 클라이언트 암호에 대한 인증이 설명 된 온라인 AWS 문서를 가리킬 수 있습니까? base64 / sha256 서명 인코딩은 매력적인 솔루션이지만 클라이언트 암호에 대해 인증하는 방법을 설명하는 AWS 문서를 명시 적으로 준수하지 않는 한 가치가 없습니다.
Kode Charlie

7

아마존은 어떻게 언급 컴퓨팅 SecretHash 값 에 대한 아마존 Cognito을 자바 애플리케이션 코드와의 문서. 여기서이 코드는 boto 3 Python SDK 와 함께 작동합니다 .

앱 클라이언트 세부 정보

App clients에서 왼쪽 메뉴에서 찾을 수 있습니다 General settings. 그 가져 오기 App client idApp 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 = bytes (app_client_id + username, 'latin-1')을 msg = bytes (username + app_client_id, 'latin-1')로 전환 한 경우에만 작동했습니다. 명확히하기 위해 사용자 이름이 먼저 표시되도록 clientId와 사용자 이름의 순서를 변경했습니다.
Josh Wolff 19

1
감사합니다 많이 @JoshWolff, 내가 실수로 스왑 app_client_idusername. 그러나 username+ 에 따라 표시되는 주석으로 올바른 출력을 표시합니다 app_client_id. 몇 번이고 감사합니다.
Kushan Gunasekera

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

Qt 프레임 워크를 사용한 C ++

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 인증

오류 : 앱 클라이언트가 비밀에 대해 구성되지 않았지만 비밀 해시를 받았습니다.

nil로 secretKey를 제공하면 나를 위해 일했습니다. 제공되는 자격 증명은 다음과 같습니다.

  • 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}"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.