userinfo: add webauthn buttons to user info page (#3075)

* userinfo: add webauthn buttons to user info page

* use new buttons on original page

* fix test
This commit is contained in:
Caleb Doxsey 2022-02-23 10:08:24 -07:00 committed by GitHub
parent 38c7089642
commit 35f697e491
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 423 additions and 288 deletions

View file

@ -0,0 +1,70 @@
import React, { FC, useRef, useState } from "react";
import { WebAuthnRequestOptions } from "../types";
import { decode, encodeUrl } from "../util/base64";
import WebAuthnButton, { WebAuthnButtonProps } from "./WebAuthnButton";
type CredentialForAuthenticate = {
id: string;
type: string;
rawId: ArrayBuffer;
response: {
authenticatorData: ArrayBuffer;
clientDataJSON: ArrayBuffer;
signature: ArrayBuffer;
userHandle: ArrayBuffer;
};
};
async function authenticateCredential(
requestOptions: WebAuthnRequestOptions
): Promise<CredentialForAuthenticate> {
const credential = await navigator.credentials.get({
publicKey: {
allowCredentials: requestOptions?.allowCredentials?.map((c) => ({
type: c.type,
id: decode(c.id),
})),
challenge: decode(requestOptions?.challenge),
timeout: requestOptions?.timeout,
userVerification: requestOptions?.userVerification,
},
});
return credential as CredentialForAuthenticate;
}
export type WebAuthnAuthenticateButtonProps = Omit<
WebAuthnButtonProps,
"action" | "enable" | "onClick" | "text"
> & {
requestOptions: WebAuthnRequestOptions;
};
export const WebAuthnAuthenticateButton: FC<
WebAuthnAuthenticateButtonProps
> = ({ requestOptions, ...props }) => {
async function authenticate(): Promise<unknown> {
const credential = await authenticateCredential(requestOptions);
return {
id: credential.id,
type: credential.type,
rawId: encodeUrl(credential.rawId),
response: {
authenticatorData: encodeUrl(credential.response.authenticatorData),
clientDataJSON: encodeUrl(credential.response.clientDataJSON),
signature: encodeUrl(credential.response.signature),
userHandle: encodeUrl(credential.response.userHandle),
},
};
}
return (
<WebAuthnButton
action="authenticate"
enable={requestOptions?.allowCredentials?.length > 0}
onClick={authenticate}
text={"Authenticate Existing Device"}
{...props}
/>
);
};
export default WebAuthnAuthenticateButton;