将设备身份验证实现为自定义的无密码认知流程

时间:2019-04-30 23:01:07

标签: amazon-cognito custom-authentication

我想按照https://aws.amazon.com/blogs/mobile/implementing-passwordless-email-authentication-with-amazon-cognito/

的方式实施无密码身份验证解决方案

我具有基本的工作原理,但是一旦添加了设备跟踪功能,我的问题就会出现。我可以致电ConfirmDevice来确认并成功记住设备。

问题#1-后续对initialAuth的调用(包括DEVICE_KEY作为auth参数)似乎无济于事。在成功进行身份验证后,我仍然提供了newDeviceMetadata,表明它似乎无法识别我正在从记住的设备登录。

问题2-尽管我可以使用refreshToken来获取有效的令牌,但我发现这有点不安全,好像有人能够获取refreshToken一样,他们可以从任何地方访问它。我最想做的是实现DEVICE_SRP_AUTH和DEVICE_PASSWORD_VERIFIER,这样用户只需通过文本/电子邮件代码登录一次,然后该设备便可以根据需要使用为该设备生成的密码重新进行身份验证。

我能够将其实现为第二个应用程序客户端(这是我可以弄清楚如何告诉我想要不同的流程的唯一方法),并且可以将整个过程一直进行到DEVICE_PASSWORD_VERIFIER挑战。但是我无法克服错误:

com.amazonaws.services.cognitoidp.model.NotAuthorizedException:错误的用户名或密码。 (服务:AWSCognitoIdentityProvider;状态代码:400;错误代码:NotAuthorizedException;请求ID:fa48d3ce-6b90-11e9-a13a-0507dac88eff)

我的解决方案有很多部分,我有一个用于cognito sdk的java服务包装器,然后尽管这最终将用于移动应用程序,但我有一个基于javascript的Web客户端作为测试工具。为了(希望)减少SRP过程中出错的机会,我正在使用Amazon-cognito-identity-js库(版本3.0.11)进行SRP计算,相当接近地镜像了CognitoUser.js中的代码

我的流程:  1.用户注册并确认  2.用户通过输入电子邮件或电话号码登录-收到验证码,然后输入验证码作为质询响应。  3.收到令牌并确认设备被呼叫

用于调用confirmDevice的功能代码片段(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都存储在本地存储中。

现在,我想再次调用启动身份验证,但是不必使用应答,而是要使用设备密码进行身份验证。

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开始是一项要求吗?

非常感谢您的帮助。

0 个答案:

没有答案