ไม่สามารถตรวจสอบแฮชลับสำหรับไคลเอนต์ใน Amazon Cognito Userpools


133

ฉันติดอยู่ที่กระบวนการ "กลุ่มผู้ใช้ Amazon Cognito Identity"

ฉันลองใช้รหัสที่เป็นไปได้ทั้งหมดสำหรับการพิสูจน์ตัวตนผู้ใช้ใน userpools cognito แต่ฉันมักจะได้รับข้อผิดพลาดว่า"ข้อผิดพลาด: ไม่สามารถตรวจสอบแฮชลับสำหรับไคลเอนต์ 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

คำตอบ:


183

ดูเหมือนว่าปัจจุบัน 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-parameters USERNAME = ชื่อผู้ใช้ @ gmail.com, PASSWORD = som3PassW0rd
Stanley Yong

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 เกิดข้อผิดพลาดเดียวกันกับที่เราต้องสร้างแอปโดยไม่มีความลับของลูกค้า
Dileep

4
12 กันยายน 2018 - ฉบับเดียวกัน แม้ว่าจะไม่ได้ใช้ไคลเอนต์ที่สร้างความลับฉันจะได้รับ 400 ไม่ว่าผู้ใช้จะได้รับการตรวจสอบสิทธิ์หรือไม่ก็ตาม แอพทำงานตามที่คาดไว้อย่างไรก็ตามสิ่งนี้
foxtrotuniform6969

70

ตามเอกสาร: http://docs.aws.amazon.com/cognito/latest/developerguide/setting-up-the-javascript-sdk.html

Javascript SDK ไม่รองรับ Apps ที่มี Client Secret

ตอนนี้คำแนะนำระบุว่าคุณต้องยกเลิกการเลือก "สร้างข้อมูลลับของไคลเอ็นต์" เมื่อสร้างแอปสำหรับกลุ่มผู้ใช้


สิ่งนี้ใช้ได้ผลสำหรับฉันโดยใช้ Node.js ที่ฝั่งเซิร์ฟเวอร์ ขอบคุณหมอ!
Rick

38

อาจช้าไปสองสามปี แต่เพียงแค่ยกเลิกการเลือกตัวเลือก "สร้างข้อมูลลับของไคลเอ็นต์" "และจะใช้ได้กับเว็บไคลเอ็นต์ของคุณ

สร้างตัวเลือกไคลเอนต์แอป


8
โปรดทราบว่าคุณไม่สามารถแก้ไขได้หลังจากสร้างไคลเอนต์แล้วดังนั้นให้สร้างใหม่หากจำเป็น
URL87

หากคุณสร้างไคลเอนต์แอปใหม่และคุณมีกลุ่มข้อมูลประจำตัว (บน "Federated Identities") ที่ใช้ผู้ให้บริการการพิสูจน์ตัวตน Cognito อย่าลืมอัปเดตฟิลด์รหัสไคลเอ็นต์ของแอปด้วยรหัสของไคลเอ็นต์แอปใหม่
AMS777

ขอบคุณ @Tiisetso Tjabane 🙏
kartonnade

21

เนื่องจากคนอื่น ๆ ได้โพสต์ภาษาของตนโหนดนี่ (และทำงานในเบราว์เซอร์browserify-cryptoโดยใช้โดยอัตโนมัติหากคุณใช้ webpack หรือ 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

14

สำหรับใครก็ตามที่สนใจใช้ AWS Lambda เพื่อลงทะเบียนผู้ใช้โดยใช้ AWS JS SDK นี่คือขั้นตอนที่ฉันทำ:

สร้างฟังก์ชัน lambda อื่นใน 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

หมายเหตุ: คำตอบนั้นอ้างอิงจากคำตอบของ George Campbell ในลิงค์ต่อไปนี้: การคำนวณแฮช 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 (Forked และแก้ไขสำหรับ 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,
}

ฉันสามารถลงทะเบียนได้ตามขั้นตอนของคุณ แต่ฉันไม่สามารถลงชื่อเข้าใช้ด้วย proc นี้ได้ มีการแก้ไขใด ๆ ที่ต้องทำเพื่อลงชื่อเข้าใช้? จะเป็นประโยชน์มากถ้าคุณสามารถเพิ่มได้ที่นี่ ขอบคุณล่วงหน้า.
Vinay Wadagavi

8

Amazonกล่าวถึงวิธีการคำนวณค่า SecretHashสำหรับAmazon Cognitoในเอกสารประกอบด้วยโค้ดแอปพลิเคชัน Java นี่คือรหัสนี้ทำงานร่วมกับBoto 3 หลาม 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 = bytes (app_client_id + username, 'latin-1') เป็น msg = bytes (username + app_client_id, 'latin-1') เพื่อความชัดเจนฉันเปลี่ยนลำดับของไคลเอนต์ไอดีและชื่อผู้ใช้ให้ชื่อผู้ใช้ปรากฏก่อน
Josh Wolff

1
ขอบคุณมาก @JoshWolff ผมเข้าใจผิดแลกเปลี่ยนและapp_client_id usernameแต่ผมแสดงผลลัพธ์ที่ถูกต้องเป็นความคิดเห็นที่แสดงตาม+username app_client_idขอบคุณอีกครั้งและอีกครั้ง
Kushan Gunasekera

1
ไม่มีปัญหาเลย! @Kushan Gunasekera
Josh Wolff

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

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

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 เป็นศูนย์สำหรับฉัน ข้อมูลรับรองที่ให้ไว้ ได้แก่ : -

  • 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.