WebauthnAuthenticate: refactor authentication flow

Lots of weird syntax here, and we were using `useCallback` rather than
`useConstCallback`.
This commit is contained in:
Mary Strodl
2022-10-06 14:25:04 -04:00
parent 4b3ae58ea7
commit 9e63183f4b

View File

@ -1,10 +1,11 @@
import React, { useCallback, useRef, useState, memo } from "react"; import React, { useRef, useState, memo } from "react";
import Template from "./Template"; import Template from "./Template";
import type { KcProps } from "./KcProps"; import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase"; import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx"; import { useCssAndCx } from "../tools/useCssAndCx";
import type { I18n, MessageKeyBase } from "../i18n"; import type { I18n, MessageKeyBase } from "../i18n";
import { base64url } from "rfc4648"; import { base64url } from "rfc4648";
import { useConstCallback } from "powerhooks/useConstCallback";
const WebauthnAuthenticate = memo( const WebauthnAuthenticate = memo(
({ ({
@ -23,76 +24,67 @@ const WebauthnAuthenticate = memo(
const { cx } = useCssAndCx(); const { cx } = useCssAndCx();
const webAuthnAuthenticate = useCallback(() => { const webAuthnAuthenticate = useConstCallback(async () => {
if (!isUserIdentified) { if (!isUserIdentified) {
return; return;
} }
checkAllowCredentials(); const allowCredentials = authenticators.authenticators.map(
authenticator =>
function checkAllowCredentials() { ({
const allowCredentials = authenticators.authenticators.map( id: base64url.parse(authenticator.credentialId, { loose: true }),
authenticator => type: "public-key"
({ } as PublicKeyCredentialDescriptor)
id: base64url.parse(authenticator.credentialId, { loose: true }), );
type: "public-key" // Check if WebAuthn is supported by this browser
} as PublicKeyCredentialDescriptor) if (!window.PublicKeyCredential) {
); setError(msgStr("webauthn-unsupported-browser-text"));
doAuthenticate(allowCredentials); submitForm();
return;
} }
function doAuthenticate(allowCredentials: PublicKeyCredentialDescriptor[]) {
// Check if WebAuthn is supported by this browser
if (!window.PublicKeyCredential) {
setError(msgStr("webauthn-unsupported-browser-text"));
submitForm();
return;
}
const publicKey: PublicKeyCredentialRequestOptions = { const publicKey: PublicKeyCredentialRequestOptions = {
rpId, rpId,
challenge: base64url.parse(challenge, { loose: true }) challenge: base64url.parse(challenge, { loose: true })
}; };
if (createTimeout !== 0) { if (createTimeout !== 0) {
publicKey.timeout = createTimeout * 1000; publicKey.timeout = createTimeout * 1000;
}
if (allowCredentials.length) {
publicKey.allowCredentials = allowCredentials;
}
if (userVerification !== "not specified") {
publicKey.userVerification = userVerification;
}
navigator.credentials
.get({ publicKey })
.then(resultRaw => {
if (!resultRaw || resultRaw.type != "public-key") return;
const result = resultRaw as PublicKeyCredential;
if (!("authenticatorData" in result.response)) return;
const response = result.response as AuthenticatorAssertionResponse;
let clientDataJSON = response.clientDataJSON;
let authenticatorData = response.authenticatorData;
let signature = response.signature;
setClientDataJSON(base64url.stringify(new Uint8Array(clientDataJSON), { pad: false }));
setAuthenticatorData(base64url.stringify(new Uint8Array(authenticatorData), { pad: false }));
setSignature(base64url.stringify(new Uint8Array(signature), { pad: false }));
setCredentialId(result.id);
setUserHandle(base64url.stringify(new Uint8Array(response.userHandle!), { pad: false }));
submitForm();
})
.catch(err => {
setError(err);
submitForm();
});
} }
}, [kcContext]);
if (allowCredentials.length) {
publicKey.allowCredentials = allowCredentials;
}
if (userVerification !== "not specified") {
publicKey.userVerification = userVerification;
}
try {
const resultRaw = await navigator.credentials.get({ publicKey });
if (!resultRaw || resultRaw.type != "public-key") return;
const result = resultRaw as PublicKeyCredential;
if (!("authenticatorData" in result.response)) return;
const response = result.response as AuthenticatorAssertionResponse;
const clientDataJSON = response.clientDataJSON;
const authenticatorData = response.authenticatorData;
const signature = response.signature;
setClientDataJSON(base64url.stringify(new Uint8Array(clientDataJSON), { pad: false }));
setAuthenticatorData(base64url.stringify(new Uint8Array(authenticatorData), { pad: false }));
setSignature(base64url.stringify(new Uint8Array(signature), { pad: false }));
setCredentialId(result.id);
setUserHandle(base64url.stringify(new Uint8Array(response.userHandle!), { pad: false }));
submitForm();
} catch (err) {
setError(String(err));
submitForm();
}
});
const webAuthForm = useRef<HTMLFormElement>(null); const webAuthForm = useRef<HTMLFormElement>(null);
const submitForm = useCallback(() => { const submitForm = useConstCallback(() => {
webAuthForm.current!.submit(); webAuthForm.current!.submit();
}, [webAuthForm.current]); });
const [clientDataJSON, setClientDataJSON] = useState(""); const [clientDataJSON, setClientDataJSON] = useState("");
const [authenticatorData, setAuthenticatorData] = useState(""); const [authenticatorData, setAuthenticatorData] = useState("");