I recently ran into a problem that seems to be present in a wide range of WebAuthn tutorials, but I don’t know where it originated.
WebAuthn is the web API for the FIDO authentication standards. Really nice and secure, highly recommended. There are a lot of tutorials on the web.
WebAuthn is mostly accessed via navigator.credentials in JavaScript in the browser. There is a need for some communication with the server, and usually this is done using JSON. The API uses ArrayBuffers which cannot be directly encoded as JSON. Usually these ArrayBuffers are encoded as Base64 strings which are no problem with JSON.
Someone decided to use URL-safe Base64 and that use has spread to many tutorials and example code snippets. But what caused me trouble was that the example everyone seems to be copying is asymmetrical. URL-safe Base64 is decoded to binary, but binary is encoded to plain Base64.
FusionAuth seems to have documented functions that can be used to round-trip the data:
function bufferToBase64URL(buffer) {
const bytes = new Uint8Array(buffer);
let string = '';
bytes.forEach(b => string += String.fromCharCode(b));
const base64 = btoa(string);
return base64
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
function base64URLToBuffer(base64URL) {
const base64 = base64URL.replace(/-/g, '+').replace(/_/g, '/');
const padLen = (4 - (base64.length % 4)) % 4;
return Uint8Array.from(atob(base64.padEnd(base64.length + padLen, '=')),
c => c.charCodeAt(0));
}