Skip to content

Commit

Permalink
More useful error for missing email verification
Browse files Browse the repository at this point in the history
  • Loading branch information
scotttrinh committed Feb 12, 2024
1 parent ebda3e8 commit 64eaa1e
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 23 deletions.
32 changes: 32 additions & 0 deletions edb/server/protocol/auth_ext/_static/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,35 @@ export function encodeBase64Url(bytes) {
.replace(/\//g, "_")
.replace(/=/g, "");
}

/**
* Parse an HTTP Response object. Allows passing in custom handlers for
* different status codes and error.type values
*
* @param {Response} response
* @param {Function[]=} handlers
*/
export async function parseResponseAsJSON(response, handlers = []) {
const bodyText = await response.text();

if (!response.ok) {
let error;
try {
error = JSON.parse(bodyText);
} catch (e) {
throw new Error(
`Failed to parse body as JSON. Status: ${response.status} ${response.statusText}. Body: ${bodyText}`
);
}

for (const handler of handlers) {
handler(response, error);
}

throw new Error(
`Response was not OK. Status: ${response.status} ${response.statusText}. Body: ${bodyText}`
);
}

return response.json();
}
60 changes: 37 additions & 23 deletions edb/server/protocol/auth_ext/_static/webauthn-authenticate.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { decodeBase64Url, encodeBase64Url } from "./utils.js";
import {
decodeBase64Url,
encodeBase64Url,
parseResponseAsJSON,
} from "./utils.js";

document.addEventListener("DOMContentLoaded", () => {
const registerForm = document.getElementById("email-factor");
/** @type {HTMLFormElement | null} */
const authenticateForm = document.getElementById("email-factor");

if (registerForm === null) {
if (authenticateForm === null) {
return;
}

registerForm.addEventListener("submit", async (event) => {
authenticateForm.addEventListener("submit", async (event) => {
if (event.submitter?.id !== "webauthn-signin") {
return;
}
event.preventDefault();

const formData = new FormData(/** @type {HTMLFormElement} */ registerForm);
const formData = new FormData(
/** @type {HTMLFormElement} */ authenticateForm
);
const email = formData.get("email");
const provider = "builtin::local_webauthn";
const challenge = formData.get("challenge");
Expand Down Expand Up @@ -151,9 +158,9 @@ async function authenticateAssertion(props) {
signature: encodeBase64Url(
new Uint8Array(props.assertion.response.signature)
),
userHandle: props.assertion.response.userHandle ? encodeBase64Url(
new Uint8Array(props.assertion.response.userHandle)
) : null,
userHandle: props.assertion.response.userHandle
? encodeBase64Url(new Uint8Array(props.assertion.response.userHandle))
: null,
},
};

Expand All @@ -170,19 +177,26 @@ async function authenticateAssertion(props) {
}),
});

if (!authenticateResponse.ok) {
console.error(
"Failed to authenticate WebAuthn credentials:",
authenticateResponse.statusText
);
console.error(await authenticateResponse.text());
throw new Error("Failed to authenticate WebAuthn credentials");
}

try {
return await authenticateResponse.json();
} catch (e) {
console.error("Failed to parse WebAuthn registration result:", e);
throw new Error("Failed to parse WebAuthn registration result");
}
return await parseResponseAsJSON(authenticateResponse, [
(response, error) => {
if (response.status === 401 && error?.type === "VerificationRequired") {
console.error(
"User's email is not verified",
response.statusText,
error
);
throw new Error(
"Please verify your email before attempting to sign in."
);
}
},
(response, error) => {
console.error(
"Failed to authenticate WebAuthn credentials:",
response.statusText,
error
);
throw new Error("Failed to authenticate WebAuthn credentials");
},
]);
}

0 comments on commit 64eaa1e

Please sign in to comment.