Реализация аутентификации устройства как пользовательского потока Cognito без пароля - PullRequest
0 голосов
/ 01 мая 2019

Я хочу реализовать решение для аутентификации без пароля в соответствии с https://aws.amazon.com/blogs/mobile/implementing-passwordless-email-authentication-with-amazon-cognito/

У меня есть основы этой работы, но мои проблемы возникают, когда я добавляю отслеживание устройств.У меня есть возможность вызывать verifyDevice для успешного подтверждения и запоминания устройства.

Проблема № 1 - последующий вызов initiateAuth, включая DEVICE_KEY в качестве параметра auth, похоже, ничего не делает.Мне по-прежнему предоставляется newDeviceMetadata при успешной аутентификации, указывающее, что он, похоже, не распознает, что я вхожу в систему с запомненного устройства.

Проблема # 2 - хотя я могу использовать refreshToken для получения действительных токенов, я нахожуэто должно быть несколько небезопасно, как если бы кто-то смог получить refreshToken, он мог получить к нему доступ из любого места.Я хотел бы иметь возможность реализовать DEVICE_SRP_AUTH и DEVICE_PASSWORD_VERIFIER, чтобы пользователю приходилось входить в систему только один раз с помощью текстового кода / кода электронной почты, а затем устройство могло бы при необходимости повторно пройти аутентификацию, используя пароль, сгенерированный для этого устройства.

Я смог реализовать это как 2-й клиент приложения (единственный способ выяснить, как сказать, что мне нужен другой поток), и я могу пройти весь процесс до вызова DEVICE_PASSWORD_VERIFIER.Однако я не могу обойти ошибку:

com.amazonaws.services.cognitoidp.model.NotAuthorizedException: неверное имя пользователя или пароль.(Сервис: AWSCognitoIdentityProvider; Код состояния: 400; Код ошибки: NotAuthorizedException; Идентификатор запроса: fa48d3ce-6b90-11e9-a13a-0507dac88eff)

Мое решение состоит из множества частей, у меня есть Java-оболочка для сервиса cognito sdkи затем, хотя в конечном итоге это будет сделано для мобильного приложения, в качестве тестового набора у меня есть веб-клиент на основе JavaScript.Чтобы (надеюсь) уменьшить вероятность ошибки в процессе SRP, я использую библиотеку amazon-cognito-identity-js (версия 3.0.11) для вычислений SRP, достаточно близко отражая код в CognitoUser.js

Мой поток: 1. Пользователь зарегистрирован и подтвержден 2. Пользователь входит в систему, введя адрес электронной почты или номер телефона - получает код подтверждения и вводит код подтверждения в качестве ответа на запрос.3. Токены получены и подтверждают, что устройство называется

фрагмент кода жгута для вызова verifyDevice (доступ к sdk обернут в оболочку java, но это в значительной степени пропуск)

let challengeResponse = prompt("please enter the code");

            fetch('http://localhost:9887/api/v1/users/verifyCode?code=' + challengeResponse, {
                headers: {
                    "Accept": "application/json",
                    username: json.username,
                    session: json.session,
                    deviceName: navigator.userAgent
                }
            }).then(response => response.json())
                .then(challengeJson => {
                    console.log(challengeJson);

                    authenticationHelper.generateHashDevice(
                        challengeJson.deviceGroupKey,
                        challengeJson.deviceKey,
                        (errGenHash) => {
                            if (errGenHash) {
                                console.log(errGenHash)
                            }
                        });

                    let salt = Buffer.from(authenticationHelper.getSaltDevices(), 'hex').toString('base64');
                    let passwordVerifier = Buffer.from(authenticationHelper.getVerifierDevices(), 'hex').toString('base64');

                    //need to store this off so it can be used later
                    let randomPassword = authenticationHelper.getRandomPassword();
                    localStorage.setItem("randomPassword", randomPassword);
                    localStorage.setItem("deviceGroupKey", challengeJson.deviceGroupKey);
                    localStorage.setItem("deviceKey", challengeJson.deviceKey);

                    fetch('http://localhost:9887/api/v1/users/confirmDevice', {
                        headers: {
                            "Accept": "application/json",
                            accessToken: challengeJson.accessToken,
                            deviceKey: challengeJson.deviceKey,
                            deviceName: navigator.userAgent,
                            passwordVerifier: passwordVerifier,
                            salt: salt
                        }
                    });

                }).catch(function (challengeError) {
                console.log(challengeError);
            });

Теперь пользователь прошел проверку подлинности и устройство подтверждено, пароль, devicekey и deviceGroupKey все хранятся в локальном хранилище.

Теперь я хочу снова вызвать initgin auth, но вместо того, чтобы отвечать на вызов, я хочу использоватьпароль устройства для аутентификации.

if (json.challengeName === 'DEVICE_SRP_AUTH') {

            let deviceGroupKey = localStorage.getItem("deviceGroupKey");
            let randomPassword = localStorage.getItem("randomPassword");
            let session = json.session;

            authenticationHelper.getLargeAValue((errAValue, aValue) => {

                fetch('http://localhost:9887/api/v1/users/signindevice', {
                    headers: {
                        "Accept": "application/json",
                        username: json.username,
                        session: session,
                        "deviceKey": deviceKey,
                        "srpA": aValue.toString(16)
                    }
                }).then(response => response.json())
                    .then(json => {

                        const serverBValue = new BigInteger(json.srpB, 16);
                        const salt = new BigInteger(json.salt, 16);

                        authenticationHelper.getPasswordAuthenticationKey(
                            deviceKey,
                            randomPassword,
                            serverBValue,
                            salt,
                            (errHkdf, hkdf) => {
                                // getPasswordAuthenticationKey callback start
                                if (errHkdf) {
                                    return callback.onFailure(errHkdf);
                                }

                                const dateHelper = new DateHelper();
                                const dateNow = dateHelper.getNowString();

                                const message = CryptoJS.lib.WordArray.create(
                                    Buffer.concat([
                                        Buffer.from(deviceGroupKey, 'utf8'),
                                        Buffer.from(deviceKey, 'utf8'),
                                        Buffer.from(json.secretBlock, 'base64'),
                                        Buffer.from(dateNow, 'utf8'),
                                    ])
                                );
                                const key = CryptoJS.lib.WordArray.create(hkdf);
                                const signatureString = Base64.stringify(HmacSHA256(message, key));

                                fetch('http://localhost:9887/api/v1/users/signindeviceresponse', {
                                    headers: {
                                        "Accept": "application/json",
                                        username: json.username,
                                        session: session,
                                        "deviceKey": deviceKey,
                                        "secretBlock": json.secretBlock,
                                        "signature": signatureString,
                                        "timestamp": dateNow
                                    }
                                }).then(response => response.json())
                                    .then(json => {
                                        console.log(json);
                                    });
                    });
                });
            });

        }

И вот где я получаю ошибку 400.

Я попытался включить / отключить различные параметры в моем пуле пользователей, но пока нетпройти эту ошибку

Я не могу найти много документации по этому устройству, так что то, что я пытаюсь сделать, даже поддерживается?В документах говорится, что этот вызов возвращается только после других испытаний, поэтому стоит ли начинать с SRP?

Любая помощь очень ценится.

...