mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-07-10 19:34:27 -04:00
Previously we would raise a 500 error in a variety of cases: - If a second key was being registered simultaneously (e.g. in a separate tab), which means the registration state could be missing after the first registration completes. That smells like an attack. - If the server-side verification failed e.g. origin verification, challenge verification, etc. The library seems to use 'ValueError' for all such errors [1] (after auditing its 'raise' statements, and excluding AttestationError [2], since we're not doing that). - If a key is used that attempts to sign with an unsupported algorithm. This would normally raise a NotImplemented error as part of verifying attestation [3], but we don't do that, so we need to verify the algorithm is supported by the library manually. This adds error handling to return a 400 response and error message in these cases, since the error is not unexpected (i.e. not a 500). A 400 seems more appropriate than a 403, since in many cases it's not clear if the request data is valid. I've used CBOR for the transport encoding, to match the successful request / response encoding. Note that the ordering of then/catch matters in JS - we don't want to catch our own throws! [1]:142587b3e6/fido2/server.py (L255)[2]:c42d9628a4/fido2/attestation/base.py (L39)[3]:c42d9628a4/fido2/cose.py (L92)
66 lines
2.1 KiB
JavaScript
66 lines
2.1 KiB
JavaScript
(function(window) {
|
|
"use strict";
|
|
|
|
window.GOVUK.Modules.RegisterSecurityKey = function() {
|
|
this.start = function(component) {
|
|
$(component)
|
|
.on('click', function(event) {
|
|
event.preventDefault();
|
|
|
|
fetch('/webauthn/register')
|
|
.then((response) => {
|
|
if (!response.ok) {
|
|
throw Error(response.statusText);
|
|
}
|
|
|
|
return response.arrayBuffer();
|
|
})
|
|
.then((data) => {
|
|
var options = window.CBOR.decode(data);
|
|
// triggers browser dialogue to select authenticator
|
|
return window.navigator.credentials.create(options);
|
|
})
|
|
.then((credential) => {
|
|
return postWebAuthnCreateResponse(
|
|
credential.response, component.data('csrfToken')
|
|
);
|
|
})
|
|
.then((response) => {
|
|
if (!response.ok) {
|
|
return response.arrayBuffer()
|
|
.then((cbor) => {
|
|
return Promise.resolve(window.CBOR.decode(cbor));
|
|
})
|
|
.catch(() => {
|
|
throw Error(response.statusText);
|
|
})
|
|
.then((text) => {
|
|
throw Error(text);
|
|
});
|
|
}
|
|
|
|
window.location.reload();
|
|
})
|
|
.catch((error) => {
|
|
console.error(error);
|
|
// some browsers will show an error dialogue for some
|
|
// errors; to be safe we always pop up an alert
|
|
var message = error.message || error;
|
|
alert('Error during registration.\n\n' + message);
|
|
});
|
|
});
|
|
};
|
|
};
|
|
|
|
function postWebAuthnCreateResponse(response, csrf_token) {
|
|
return fetch('/webauthn/register', {
|
|
method: 'POST',
|
|
headers: { 'X-CSRFToken': csrf_token },
|
|
body: window.CBOR.encode({
|
|
attestationObject: new Uint8Array(response.attestationObject),
|
|
clientDataJSON: new Uint8Array(response.clientDataJSON),
|
|
})
|
|
});
|
|
}
|
|
})(window);
|