This repository has been archived on 2023-03-18. You can view files and clone it, but cannot push or open issues or pull requests.
osr-discourse-src/app/assets/javascripts/discourse/lib/webauthn.js.es6

79 lines
2.3 KiB
JavaScript

export function stringToBuffer(str) {
let buffer = new ArrayBuffer(str.length);
let byteView = new Uint8Array(buffer);
for (let i = 0; i < str.length; i++) {
byteView[i] = str.charCodeAt(i);
}
return buffer;
}
export function bufferToBase64(buffer) {
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
}
export function isWebauthnSupported() {
return typeof PublicKeyCredential !== "undefined";
}
export function getWebauthnCredential(
challenge,
allowedCredentialIds,
successCallback,
errorCallback
) {
if (!isWebauthnSupported()) {
return errorCallback(I18n.t("login.security_key_support_missing_error"));
}
let challengeBuffer = stringToBuffer(challenge);
let allowCredentials = allowedCredentialIds.map(credentialId => {
return {
id: stringToBuffer(atob(credentialId)),
type: "public-key"
};
});
navigator.credentials
.get({
publicKey: {
challenge: challengeBuffer,
allowCredentials: allowCredentials,
timeout: 60000,
// see https://chromium.googlesource.com/chromium/src/+/master/content/browser/webauth/uv_preferred.md for why
// default value of preferred is not necesarrily what we want, it limits webauthn to only devices that support
// user verification, which usually requires entering a PIN
userVerification: "discouraged"
}
})
.then(credential => {
// 1. if there is a credential, check if the raw ID base64 matches
// any of the allowed credential ids
if (
!allowedCredentialIds.some(
credentialId => bufferToBase64(credential.rawId) === credentialId
)
) {
return errorCallback(
I18n.t("login.security_key_no_matching_credential_error")
);
}
const credentialData = {
signature: bufferToBase64(credential.response.signature),
clientData: bufferToBase64(credential.response.clientDataJSON),
authenticatorData: bufferToBase64(
credential.response.authenticatorData
),
credentialId: bufferToBase64(credential.rawId)
};
successCallback(credentialData);
})
.catch(err => {
if (err.name === "NotAllowedError") {
return errorCallback(I18n.t("login.security_key_not_allowed_error"));
}
errorCallback(err);
});
}