1import { base64url } from "rfc4648";
2
3export async function registerByWebAuthn(input) {
4 // Check if WebAuthn is supported by this browser
5 if (!window.PublicKeyCredential) {
6 returnFailure(input.errmsg);
7 return;
8 }
9
10 const publicKey = {
11 challenge: base64url.parse(input.challenge, { loose: true }),
12 rp: { id: input.rpId, name: input.rpEntityName },
13 user: {
14 id: base64url.parse(input.userid, { loose: true }),
15 name: input.username,
16 displayName: input.username,
17 },
18 pubKeyCredParams: getPubKeyCredParams(input.signatureAlgorithms),
19 };
20
21 if (input.attestationConveyancePreference !== "not specified") {
22 publicKey.attestation = input.attestationConveyancePreference;
23 }
24
25 const authenticatorSelection = {};
26 let isAuthenticatorSelectionSpecified = false;
27
28 if (input.authenticatorAttachment !== "not specified") {
29 authenticatorSelection.authenticatorAttachment =
30 input.authenticatorAttachment;
31 isAuthenticatorSelectionSpecified = true;
32 }
33
34 if (input.requireResidentKey !== "not specified") {
35 if (input.requireResidentKey === "Yes") {
36 authenticatorSelection.requireResidentKey = true;
37 } else {
38 authenticatorSelection.requireResidentKey = false;
39 }
40 isAuthenticatorSelectionSpecified = true;
41 }
42
43 if (input.userVerificationRequirement !== "not specified") {
44 authenticatorSelection.userVerification = input.userVerificationRequirement;
45 isAuthenticatorSelectionSpecified = true;
46 }
47
48 if (isAuthenticatorSelectionSpecified) {
49 publicKey.authenticatorSelection = authenticatorSelection;
50 }
51
52 if (input.createTimeout !== 0) {
53 publicKey.timeout = input.createTimeout * 1000;
54 }
55
56 const excludeCredentials = getExcludeCredentials(input.excludeCredentialIds);
57 if (excludeCredentials.length > 0) {
58 publicKey.excludeCredentials = excludeCredentials;
59 }
60
61 try {
62 const result = await doRegister(publicKey);
63 returnSuccess(result, input.initLabel, input.initLabelPrompt);
64 } catch (error) {
65 returnFailure(error);
66 }
67}
68
69function doRegister(publicKey) {
70 return navigator.credentials.create({ publicKey });
71}
72
73function getPubKeyCredParams(signatureAlgorithmsList) {
74 const pubKeyCredParams = [];
75 if (signatureAlgorithmsList.length === 0) {
76 pubKeyCredParams.push({ type: "public-key", alg: -7 });
77 return pubKeyCredParams;
78 }
79
80 for (const entry of signatureAlgorithmsList) {
81 pubKeyCredParams.push({
82 type: "public-key",
83 alg: entry,
84 });
85 }
86
87 return pubKeyCredParams;
88}
89
90function getExcludeCredentials(excludeCredentialIds) {
91 const excludeCredentials = [];
92 if (excludeCredentialIds === "") {
93 return excludeCredentials;
94 }
95
96 for (const entry of excludeCredentialIds.split(",")) {
97 excludeCredentials.push({
98 type: "public-key",
99 id: base64url.parse(entry, { loose: true }),
100 });
101 }
102
103 return excludeCredentials;
104}
105
106function getTransportsAsString(transportsList) {
107 if (!Array.isArray(transportsList)) {
108 return "";
109 }
110
111 return transportsList.join();
112}
113
114function returnSuccess(result, initLabel, initLabelPrompt) {
115 document.getElementById("clientDataJSON").value = base64url.stringify(
116 new Uint8Array(result.response.clientDataJSON),
117 { pad: false },
118 );
119 document.getElementById("attestationObject").value = base64url.stringify(
120 new Uint8Array(result.response.attestationObject),
121 { pad: false },
122 );
123 document.getElementById("publicKeyCredentialId").value = base64url.stringify(
124 new Uint8Array(result.rawId),
125 { pad: false },
126 );
127
128 if (typeof result.response.getTransports === "function") {
129 const transports = result.response.getTransports();
130 if (transports) {
131 document.getElementById("transports").value = getTransportsAsString(
132 transports,
133 );
134 }
135 } else {
136 console.log(
137 "Your browser is not able to recognize supported transport media for the authenticator.",
138 );
139 }
140
141 // let labelResult = window.prompt(initLabelPrompt, initLabel);
142 // if (labelResult === null) {
143 // labelResult = initLabel;
144 // }
145 document.getElementById("authenticatorLabel").value = "Passkey " +
146 new Date().toString();
147
148 document.getElementById("register").requestSubmit();
149}
150
151function returnFailure(err) {
152 if (err.name === "NotAllowedError") {
153 document.getElementById("error").value =
154 "the operation was cancelled / browser does not support passkey";
155 } else {
156 document.getElementById("error").value = err;
157 }
158 document.getElementById("register").requestSubmit();
159}