Geheime Hashs für Clients in Amazon Cognito-Benutzerpools können nicht überprüft werden

131

Ich bin beim Prozess "Amazon Cognito Identity-Benutzerpools" festgefahren.

Ich habe alle möglichen Codes zur Authentifizierung des Benutzers in Cognito-Benutzerpools ausprobiert. Ich erhalte jedoch immer die Fehlermeldung "Fehler: Geheimer Hash für Client 4b ******* fd kann nicht überprüft werden".

Hier ist Code:

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);
});
Ronak Patel
quelle
9
Akzeptierte Antwort ist NICHT mehr gültig. Anweisungen zum Generieren von geheimem Hash finden Sie hier docs.aws.amazon.com/cognito/latest/developerguide/…
jasiustasiu
Ja, und sehen Sie sich die Antwort von @Simon Buchan unten für eine JavaScript-Implementierung an. Es funktioniert perfekt.
Guzmonne

Antworten:

179

Es scheint, dass AWS Cognito derzeit das Kundengeheimnis nicht perfekt handhabt. Es wird in naher Zukunft funktionieren, aber derzeit ist es noch eine Beta-Version.

Für mich funktioniert es gut für eine App ohne Client-Geheimnis, aber für eine App mit einem Client-Geheimnis schlägt dies fehl.

Versuchen Sie also in Ihrem Benutzerpool, eine neue App zu erstellen, ohne ein Client-Geheimnis zu generieren. Verwenden Sie dann diese App, um einen neuen Benutzer anzumelden oder die Registrierung zu bestätigen.

thomas.g
quelle
14
Zu Ihrer Information: Das ist mir gerade passiert. Es funktioniert immer noch so, Januar 2017. Als ich eine App ohne client_secret erstellt habe, konnte ich das JS SDK verwenden. Als ich eine App mit einem client_secret erstellt habe, habe ich die gleiche Funktion wie in der ursprünglichen Frage erhalten.
Cheeso
5
Ab dem 21. April 2017 funktioniert die Verwendung der AWS-CLI immer noch nicht, wenn der geheime Schlüssel für den App Client aktiviert wurde. 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 = Benutzername @ gmail.com, PASSWORD = som3PassW0rd
Stanley Yong
26
Ab Januar 2018 wird dies noch nicht unterstützt. Die Dokumentation zum Github-Repo github.com/aws/amazon-cognito-identity-js erwähnt es:"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. Mai 2018, der gleiche Fehler, den wir brauchen, um eine App ohne Client-Geheimnis zu erstellen.
Dileep
4
12. September 2018 - Gleiche Ausgabe. Selbst wenn ich keinen Client verwende, der ein Geheimnis generiert, erhalte ich eine 400, unabhängig davon, ob der Benutzer authentifiziert ist oder nicht. Trotzdem funktioniert die App wie erwartet.
foxtrotuniform6969
70

Laut den Dokumenten: http://docs.aws.amazon.com/cognito/latest/developerguide/setting-up-the-javascript-sdk.html

Das Javascript SDK unterstützt keine Apps mit einem Client Secret.

In den Anweisungen heißt es nun, dass Sie beim Erstellen der App für den Benutzerpool das Kontrollkästchen "Clientgeheimnis generieren" deaktivieren müssen.

Dr. Douglas GhD
quelle
Dies funktionierte bei mir mit Node.js auf der Serverseite. Danke Doc!
Rick
37

Dies kann einige Jahre zu spät sein, deaktivieren Sie jedoch einfach die Option "Client-Geheimnis generieren" und es funktioniert für Ihre Web-Clients.

App-Client-Option generieren

Tiisetso Tjabane
quelle
8
Beachten Sie, dass Sie es nach dem Erstellen des Clients nicht mehr bearbeiten können. Erstellen Sie daher bei Bedarf ein neues.
URL87
Wenn Sie einen neuen App-Client erstellen und über einen Identitätspool (unter "Verbundidentitäten") verfügen, der einen Cognito-Authentifizierungsanbieter verwendet, müssen Sie das Feld für die App-Client-ID mit der ID des neuen App-Clients aktualisieren.
AMS777
21

Da alle anderen ihre Sprache gepostet haben, ist hier der Knoten (und er funktioniert im Browser mit browserify-crypto, wird automatisch verwendet, wenn Sie Webpack oder browserify verwenden):

const crypto = require('crypto');

...

crypto.createHmac('SHA256', clientSecret)
  .update(username + clientId)
  .digest('base64')
Simon Buchan
quelle
4
Dies ist einfach und am besten Node.js eingebaute Lösung, danke @simon
Engineer
19

Ich hatte das gleiche Problem im .net SDK.

So habe ich es gelöst, falls es jemand anderes braucht:

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

Die Anmeldung sieht dann so aus:

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;
    }
}
Ron Sijm
quelle
Bestätigen, dass dies weiterhin benötigt wird und im v3.5 AWS .NET SDK (Vorschau) weiterhin funktioniert.
pieSquared
13

Für alle, die AWS Lambda verwenden möchten, um einen Benutzer mit dem AWS JS SDK anzumelden, habe ich folgende Schritte ausgeführt:

Erstellen Sie eine weitere Lambda-Funktion in Python, um den Schlüssel zu generieren:

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

Rufen Sie die Funktion über die Funktion nodeJS in AWS auf. Die Unterschrift fungierte als geheimer Hash für Cognito

Hinweis: Die Antwort basiert stark auf der Antwort von George Campbell unter folgendem Link: Berechnung eines SHA-Hashs mit einer Zeichenfolge + einem geheimen Schlüssel in Python

Molezz
quelle
12

Lösung für golang. Scheint so, als sollte dies dem SDK hinzugefügt werden.

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
quelle
8

Lösung für NodeJS mit SecretHash

Es scheint albern, dass AWS den geheimen Schlüssel aus dem SDK entfernt hat, da er in NodeJS nicht verfügbar gemacht wird.

Ich habe es in NodeJS zum Laufen gebracht, indem ich Fetch abgefangen und den Hash-Schlüssel mithilfe der Antwort von @Simon Buchan hinzugefügt habe .

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 und bearbeitet für NodeJS von Fork von 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
quelle
Ich konnte mich nach Ihrem Verfahren anmelden, aber ich kann mich mit diesem Verfahren nicht anmelden. Gibt es Änderungen, die vorgenommen werden müssen, um sich anzumelden? Es ist sehr hilfreich, wenn Sie es hier hinzufügen können. Danke im Voraus.
Vinay Wadagavi
7

In Java können Sie diesen Code verwenden:

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
quelle
Wo verwenden Sie diesen geheimen Hash im SDK neben der Ausgabe auf dem Bildschirm?
Aaron
1
Kann jemand auf AWS-Dokumente online verweisen, in denen die Authentifizierung gegen das Clientgeheimnis erklärt wird? Die Base64 / sha256-Signaturcodierungen sind überzeugende Lösungen - aber wertlos, es sei denn, sie entsprechen ausdrücklich den AWS-Dokumenten, in denen die Authentifizierung gegen das Clientgeheimnis beschrieben wird.
Kode Charlie
7

Amazon erwähnt in seiner Dokumentation mit Java-Anwendungscode, wie SecretHash-Werte für Amazon Cognito berechnet werden. Hier funktioniert dieser Code mit dem Boto 3 Python SDK .

App-Client-Details

Sie finden Ihr App clientsMenü auf der linken Seite unter General settings. Holen Sie sich diese App client idund App client secretzu erstellen SECRET_HASH. Zum besseren Verständnis habe ich alle Ausgaben jeder einzelnen Zeile auskommentiert.

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=

In der Boto 3- Dokumentation sehen wir viel Zeit zum Nachfragen SECRET_HASH. Die obigen Codezeilen helfen Ihnen also, dies zu erstellen SECRET_HASH.

Wenn Sie nicht verwenden möchten, SECRET_HASHdeaktivieren Sie einfach Generate client secretbeim Erstellen einer App.

neue App erstellen

Kushan Gunasekera
quelle
1
Für mich funktionierte dies nur, wenn ich msg = Bytes (app_client_id + Benutzername, 'latin-1') auf msg = Bytes (Benutzername + app_client_id, 'latin-1') umstellte. Um klar zu sein, habe ich die Reihenfolge der clientId und des Benutzernamens so geändert, dass der Benutzername zuerst angezeigt wird.
Josh Wolff
1
Vielen Dank @JoshWolff, ich tausche fälschlicherweise app_client_idund username. Aber ich zeige die korrekte Ausgabe als Kommentar an, der gemäß dem username+ angezeigt wird app_client_id. Immer wieder vielen Dank.
Kushan Gunasekera
1
Überhaupt kein Problem! @ Kushan Gunasekera
Josh Wolff
6

Dies ist ein Beispiel-PHP-Code, mit dem ich den geheimen Hash generiere

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

In diesem Fall ist das Ergebnis:

DdSuILDJ2V84zfOChcn6TfgmlfnHsUYq0J6c01QV43I=
Titi Wangsa bin Damhore
quelle
5

Für JAVA und .NET müssen Sie das Geheimnis in den Auth-Parametern mit dem Namen übergeben 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
};

Und es sollte funktionieren.

Shanmukhi Goli
quelle
3

C ++ mit dem 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
quelle
1

Möglicherweise gibt es eine kompaktere Version, aber dies funktioniert für Ruby, insbesondere in Ruby on Rails, ohne dass etwas erforderlich ist:

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))
Nikolay D.
quelle
0

Cognito-Authentifizierung

Fehler: Der App-Client ist nicht für geheim konfiguriert, aber es wurde ein geheimer Hash empfangen

Die Bereitstellung von secretKey als Null hat für mich funktioniert. Zu den bereitgestellten Anmeldeinformationen gehören: -

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

Alle oben genannten Dinge funktionieren mit dem unten verlinkten Codebeispiel.

AWS-Beispielcode: https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoYourUserPools-Sample/Swift

Lassen Sie mich wissen, wenn das bei Ihnen nicht funktioniert.

Siddharth Kavthekar
quelle
Dies ist eine tote Verbindung
Jpnh
0

Hier ist mein 1 Befehl und es funktioniert (Bestätigt :))

EMAIL="[email protected]" \
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}"
Tuong Le
quelle