1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
(async () => {
if (
SimpleWebAuthnBrowser.browserSupportsWebAuthn()
&& (await SimpleWebAuthnBrowser.platformAuthenticatorIsAvailable())
&& (await SimpleWebAuthnBrowser.browserSupportsWebAuthnAutofill())
) {
log("Passkeys are supported! ✅");
document.getElementById("passkeys_check").innerText = "Passkeys are supported! ✅";
document.getElementById("auth").disabled = false;
} else {
log("Passkeys are not supported! ❌");
document.getElementById("passkeys_check").innerText = "Passkeys are not supported! ❌";
throw new Error("Passkeys are not supported! ❌");
}
})();
const NAMES = ["Batman", "i miss her", "Bond, James Bond", "Skyler White Yo", "T800"];
const _nameEl = document.getElementById("name");
_nameEl.placeholder = NAMES[Math.floor(Math.random() * NAMES.length)];
const _logEl = document.getElementById("logs");
function log(message) {
console.log(message);
_logEl.innerHTML += `<pre>${JSON.stringify(message, null, 2)}</pre>`;
_logEl.hidden = false;
return message;
}
const _statusEl = document.getElementById("status");
function status(message) {
_statusEl.hidden = false;
_statusEl.innerText = message;
return message;
}
async function postJson(url, body?) {
return await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then((r) => (r.ok ? r.json() : r.text()));
}
async function handleRegister() {
const username = document.getElementById("name").value;
await postJson("/register/options", { username })
.then(log)
.then(async (options) => await SimpleWebAuthnBrowser.startRegistration(options))
.then(log)
.then(async (cred) => await postJson("/register/verify", { username, cred }))
.then(log)
.then((res) => {
status(res.verified ? "✅ Registered!" : "❌ Registration failed.");
})
.catch(log);
}
async function startAuth(options, conditional) {
await SimpleWebAuthnBrowser.startAuthentication(options, conditional)
.then(log)
.then(async (cred) => await postJson("/login/verify", { cred }))
.then(log)
.then((res) => {
status(res.verified ? "✅ Logged in!" : "❌ Login failed.");
});
}
const loginOptions = postJson("/login/options").then(log);
loginOptions.then(async (options) => {
log("WebAuthn Conditional UI started.");
return await startAuth(options, true);
});
async function handleLogin() {
const username = document.getElementById("name").value;
await loginOptions.then(async (options) => await startAuth(options, false));
}
async function handleLogout() {
await postJson("/logout");
status("✅ Logged out!");
}
👆 This is a val. Vals are TypeScript snippets of code, written in the browser and run on our servers. Create scheduled functions, email yourself, and persist small pieces of data — all from the browser.