помощь в форматировании javascript аутентификации webauthn

Я пытался понять, как сделать 2fa с помощью webauthn, и у меня работает регистрационная часть. Детали действительно плохо документированы, особенно все полезные данные кодирования в 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 выше, и я делаю (это встроенный рубин):

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 выглядят следующим образом:

введите здесь описание изображения

Я пробовал всеми способами преобразовать мои сохраненные в db переменные пользователя и устройства в правильно закодированные и проанализированные переменные 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\"}]}"

person user1130176    schedule 14.05.2020    source источник


Ответы (1)


Это пример сериализованных данных 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 на стороне сервера.

person mackie    schedule 14.05.2020
comment
спасибо @mackie, единственная разница между вашей полезной нагрузкой и моей в том, что у меня нет ключей rpId, userVerificaiton или extension, а в allowedCredentials у меня нет транспортов. Я делаю успехи, но теперь в консоли я получаю Запрос не разрешен пользовательским агентом или платформой в текущем контексте, возможно, из-за того, что пользователь отказал в разрешении - какие-либо идеи? Я добавлю свои варианты к своему вопросу, если это поможет. Спасибо за вашу помощь. - person user1130176; 14.05.2020
comment
Все это необязательно, так что это не должно быть проблемой. Это должно быть связано с тем, как эти значения base64url преобразуются в ArrayBuffers. Эта ошибка возвращается непосредственно из navigator.credentials.get? - person mackie; 14.05.2020
comment
да, это на самом деле в блоке catch в этой строке navigator.credentails.get, я действительно обновил свой код, чтобы он соответствовал вашему, позвольте мне обновить свой вопрос новым кодом, может быть, что-то очевидно - person user1130176; 14.05.2020
comment
хорошо, я обновил код с комментарием, чтобы показать вам в блоке catch, где возникает ошибка. Супер благодарен за вашу помощь с этим. - person user1130176; 14.05.2020
comment
Мне все кажется нормальным, очень странно! - person mackie; 15.05.2020