0

我一直在试图弄清楚如何使用 webauthn 进行 2fa,并且我的注册部分正在工作。详细信息的文档记录确实很差,尤其是 javascript 中的所有编码有效负载。我可以向用户注册设备,但无法使用该设备进行身份验证。作为参考,我正在使用这些资源:

https://github.com/cedarcode/webauthn-ruby

https://www.passwordless.dev/js/mfa.register.js

特别是,对于身份验证,我试图模仿这个 js 功能:

https://www.passwordless.dev/js/mfa.register.js

在我的用户模型中,我有一个 webauthn_id 和几个 u2f 设备,每个设备都有一个 public_key 和一个 webauthn_id。

在我的 Rails 应用程序中,我这样做:

options = WebAuthn::Credential.options_for_get(allow: :webauthn_id)
session[:webauthn_options] = options

在我的 javascript 中,我尝试模仿上面的 js 文件,然后我做了(这是嵌入的 ruby​​):

options = <%= raw @options.as_json.to_json %>
options.challenge = WebAuthnHelpers.coerceToArrayBuffer(options.challenge);
options.allowCredentials = options.allowCredentials.map((c) => {
    c.id = WebAuthnHelpers.coerceToArrayBuffer(c.id);
    return c;
});

navigator.credentials.get({ "publicKey": options }).then(function (credentialInfoAssertion) 
{
            // send assertion response back to the server
            // to proceed with the control of the credential
alert('here');
}).catch(function (err) 
{
    debugger
    console.error(err);   /*  THIS IS WHERE THE ERROR IS THROWN */
});

问题是,我无法通过 navigator.credentials.get,我在 javascript 控制台中收到此错误:

TypeError: CredentialsContainer.get: Element of 'allowCredentials' member of PublicKeyCredentialRequestOptions can't be converted to a dictionary

调用 navigator.credentials.get 时的选项如下所示:

在此处输入图像描述

我已经尝试了各种方法将我的数据库存储的用户和设备变量转换为正确编码和解析的 javascript 变量,但似乎无法让它工作。有什么明显的关于我做错了什么吗?

感谢您的帮助,凯文

更新 -

添加服务器生成的选项json:

"{\"challenge\":\"SSDYi4I7kRWt5wc5KjuAvgJ3dsQhjy7IPOJ0hvR5tMg\",\"timeout\":120000,\"allowCredentials\":[{\"type\":\"public-key\",\"id\":\"OUckfxGNLGGASUfGiX-1_8FzehlXh3fKvJ98tm59mVukJkKb_CGk1avnorL4sQQASVO9aGqmgn01jf629Jt0Z0SmBpDKd9sL1T5Z9loDrkLTTCIzrIRqhwPC6yrkfBFi\"},{\"type\":\"public-key\",\"id\":\"Fj5T-WPmEMTz139mY-Vo0DTfsNmjwy_mUx6jn5rUEPx-LsY51mxNYidprJ39_cHeAOieg-W12X47iJm42K0Tsixj4_Fl6KjdgYoxQtEYsNF-LPhwtoKwYsy1hZgVojp3\"}]}"
4

1 回答 1

0

这是我们的实现返回的序列化 JSON 数据的示例:

{
    "challenge": "MQ1S8MBSU0M2kiJqJD8wnQ",
    "timeout": 60000,
    "rpId": "identity.acme.com",
    "allowCredentials": [
        {
            "type": "public-key",
            "id": "k5Ti8dLdko1GANsBT-_NZ5L_-8j_8TnoNOYe8mUcs4o",
            "transports": [
                "internal"
            ]
        },
        {
            "type": "public-key",
            "id": "LAqkKEO99XPCQ7fsUa3stz7K76A_mE5dQwX4S3QS6jdbI9ttSn9Hu37BA31JUGXqgyhTtskL5obe6uZxitbIfA",
            "transports": [
                "usb"
            ]
        },
        {
            "type": "public-key",
            "id": "nbN3S08Wv2GElRsW9AmK70J1INEpwIywQcOl6rp_DWLm4mcQiH96TmAXSrZRHciZBENVB9rJdE94HPHbeVjtZg",
            "transports": [
                "usb"
            ]
        }
    ],
    "userVerification": "discouraged",
    "extensions": {
        "txAuthSimple": "Sign in to your ACME account",
        "exts": true,
        "uvi": true,
        "loc": true,
        "uvm": true
    }
}

这被解析为一个对象,用于强制这些 base64url 编码值的代码是:

credentialRequestOptions.challenge = WebAuthnHelpers.coerceToArrayBuffer(credentialRequestOptions.challenge);

credentialRequestOptions.allowCredentials = credentialRequestOptions.allowCredentials.map((c) => {
    c.id = WebAuthnHelpers.coerceToArrayBuffer(c.id);
    return c;
});

希望有帮助。JSON 数据通过 fetch() 调用检索,并且 byte[] 字段在服务器端编码为 base64url。

于 2020-05-14T14:46:33.210 回答