136 lines
3.5 KiB
JavaScript
136 lines
3.5 KiB
JavaScript
export function supported() {
|
|
return Boolean(
|
|
navigator.credentials &&
|
|
navigator.credentials.create &&
|
|
navigator.credentials.get &&
|
|
window.PublicKeyCredential,
|
|
);
|
|
}
|
|
|
|
export function isHTTPS() {
|
|
return window.location.protocol.startsWith('https');
|
|
}
|
|
|
|
export const FLOW_AUTHENTICATE = 'authenticate';
|
|
export const FLOW_REGISTER = 'register';
|
|
|
|
/**
|
|
* Converts a base64 string to an ArrayBuffer
|
|
*
|
|
* @param {String} str - A base64 encoded string
|
|
* @returns {ArrayBuffer}
|
|
*/
|
|
export const base64ToBuffer = (str) => {
|
|
const rawStr = atob(str);
|
|
const buffer = new ArrayBuffer(rawStr.length);
|
|
const arr = new Uint8Array(buffer);
|
|
for (let i = 0; i < rawStr.length; i += 1) {
|
|
arr[i] = rawStr.charCodeAt(i);
|
|
}
|
|
return arr.buffer;
|
|
};
|
|
|
|
/**
|
|
* Converts ArrayBuffer to a base64-encoded string
|
|
*
|
|
* @param {ArrayBuffer, String} str -
|
|
* @returns {String} - ArrayBuffer to a base64-encoded string.
|
|
* When input is a string, returns the input as-is.
|
|
*/
|
|
export const bufferToBase64 = (input) => {
|
|
if (typeof input === 'string') {
|
|
return input;
|
|
}
|
|
const arr = new Uint8Array(input);
|
|
return btoa(String.fromCharCode(...arr));
|
|
};
|
|
|
|
/**
|
|
* Return a URL-safe base64 string.
|
|
*
|
|
* RFC: https://datatracker.ietf.org/doc/html/rfc4648#section-5
|
|
* @param {String} base64Str
|
|
* @returns {String}
|
|
*/
|
|
export const base64ToBase64Url = (base64Str) => {
|
|
return base64Str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
};
|
|
|
|
/**
|
|
* Returns a copy of the given object with the id property converted to buffer
|
|
*
|
|
* @param {Object} param
|
|
*/
|
|
function convertIdToBuffer({ id, ...rest }) {
|
|
return {
|
|
...rest,
|
|
id: base64ToBuffer(id),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Returns a copy of the given array with all `id`s of the items converted to buffer
|
|
*
|
|
* @param {Array} items
|
|
*/
|
|
function convertIdsToBuffer(items) {
|
|
return items.map(convertIdToBuffer);
|
|
}
|
|
|
|
/**
|
|
* Returns an object with keys of the given props, and values from the given object converted to base64
|
|
*
|
|
* @param {String} obj
|
|
* @param {Array} props
|
|
*/
|
|
function convertPropertiesToBase64(obj, props) {
|
|
return props.reduce(
|
|
(acc, property) => Object.assign(acc, { [property]: bufferToBase64(obj[property]) }),
|
|
{},
|
|
);
|
|
}
|
|
|
|
export function convertGetParams({ allowCredentials, challenge, ...rest }) {
|
|
return {
|
|
...rest,
|
|
...(allowCredentials ? { allowCredentials: convertIdsToBuffer(allowCredentials) } : {}),
|
|
challenge: base64ToBuffer(challenge),
|
|
};
|
|
}
|
|
|
|
export function convertGetResponse(webauthnResponse) {
|
|
return {
|
|
type: webauthnResponse.type,
|
|
id: webauthnResponse.id,
|
|
rawId: bufferToBase64(webauthnResponse.rawId),
|
|
response: convertPropertiesToBase64(webauthnResponse.response, [
|
|
'clientDataJSON',
|
|
'authenticatorData',
|
|
'signature',
|
|
'userHandle',
|
|
]),
|
|
clientExtensionResults: webauthnResponse.getClientExtensionResults(),
|
|
};
|
|
}
|
|
|
|
export function convertCreateParams({ challenge, user, excludeCredentials, ...rest }) {
|
|
return {
|
|
...rest,
|
|
challenge: base64ToBuffer(challenge),
|
|
user: convertIdToBuffer(user),
|
|
...(excludeCredentials ? { excludeCredentials: convertIdsToBuffer(excludeCredentials) } : {}),
|
|
};
|
|
}
|
|
|
|
export function convertCreateResponse(webauthnResponse) {
|
|
return {
|
|
type: webauthnResponse.type,
|
|
id: webauthnResponse.id,
|
|
rawId: bufferToBase64(webauthnResponse.rawId),
|
|
clientExtensionResults: webauthnResponse.getClientExtensionResults(),
|
|
response: convertPropertiesToBase64(webauthnResponse.response, [
|
|
'clientDataJSON',
|
|
'attestationObject',
|
|
]),
|
|
};
|
|
}
|