feat: finish up frontend and add qr code verification

This commit is contained in:
eva
2026-02-11 21:54:53 +00:00
parent 3ce5c4abcb
commit f11a200559
5 changed files with 204 additions and 68 deletions
+5 -25
View File
@@ -464,33 +464,13 @@ async function verify(qrCodeUrlStr: string) {
distancing: {
screenDetectionConfidence: Array.from({ length: 2 }, () => randomFloat(0.01, 0.03)),
screenFaceOverlap: [0, 0],
screenBoundingBoxes: [
[],
[
{
x: 1,
y: 60,
width: 158,
height: 335
}
]
],
screenBoundingBoxes: [Array.from({ length: 2 }, generateBoundingBox)],
alternativeScore: Array.from({ length: 2 }, () => randomFloat(0.2, 0.9))
},
closing: {
screenDetectionConfidence: Array.from({ length: 2 }, () => randomFloat(0.01, 0.03)),
screenFaceOverlap: [0, 0],
screenBoundingBoxes: [
[
{
x: 370,
y: 233,
width: 58,
height: 85
}
],
[]
],
screenBoundingBoxes: [Array.from({ length: 2 }, generateBoundingBox)],
alternativeScore: Array.from({ length: 2 }, () => randomFloat(0.2, 0.9))
},
postChallenge: {
@@ -579,9 +559,9 @@ async function verify(qrCodeUrlStr: string) {
txFinishedInLandscapeMode: false
},
initializationCharacteristics: {
cropperInitTime: 2718,
coreInitTime: 6746,
pageLoadTime: 602.0999999996275,
cropperInitTime: randomInt(150, 250),
coreInitTime: randomInt(800, 1000),
pageLoadTime: randomInt(250, 350),
from_qr_scan: false,
blendShapesAvailable: true
},
+4
View File
@@ -4,4 +4,8 @@
let { children } = $props();
</script>
<svelte:head>
<title>discord/twitch/kick/snapchat age verifier</title>
</svelte:head>
{@render children()}
+77 -9
View File
@@ -1,17 +1,24 @@
<svelte:head>
<title>discord age verifier</title>
</svelte:head>
<script lang="ts">
let qrCodeUrl = $state<string | null>(null);
let qrCodeError = $state<string | null>(null);
let qrCodeSuccess = $state(false);
</script>
<div class="mx-auto w-screen max-w-6xl items-center p-5 pb-16">
<h1 class="mt-16 text-center text-3xl font-extrabold">discord age verifier</h1>
<p class="text-center">age verifies your discord account automatically as an adult</p>
<h1 class="mt-16 text-center text-3xl font-extrabold">
discord/twitch/kick/snapchat age verifier
</h1>
<p class="text-center">
age verifies your account automatically as an adult on any website using k-id
</p>
<p class="mt-4 text-center text-white/50">
made by <a class="underline" href="https://eva.ac" target="_blank">xyzeva</a> and
made by <a class="underline" href="https://kibty.town" target="_blank">xyzeva</a> and
<a class="underline" href="https://github.com/Dziurwa14" target="_blank">Dziurwa</a>, greetz to
<a class="underline" href="https://amplitudes.me/" target="_blank">amplitudes</a> (for previous work)
</p>
<p class="mt-8 text-left">
<h2 class="mt-8 text-2xl font-bold">how to verify on discord</h2>
<p class="mt-4 text-left">
it <span class="font-bold">doesn't matter</span> if you are in the UK or similar region that
currently has access to this, this will verify your account for the future global rollout in
march aswell as current. to use, simply paste this script into your discord console by going to
@@ -48,7 +55,7 @@ findByProps = (...props) => &#123;
api = findByProps('Bo','oh').Bo
// send a api request to discord /age-verification/verify and then redirect the page to our website
window.location.href = `https://discord-verifier.eva.ac/webview?url=$&#123;encodeURIComponent((await api.post(&#123; url: '/age-verification/verify', body: &#123; method: 3 &#125;&#125;)).body.verification_webview_url)&#125;`</pre>
window.location.href = `https://age-verifier.kibty.town/webview?url=$&#123;encodeURIComponent((await api.post(&#123; url: '/age-verification/verify', body: &#123; method: 3 &#125;&#125;)).body.verification_webview_url)&#125;`</pre>
<p class="text-center text-white/50">
(feel free to read the code, we made it readable and we have nothing to hide)
</p>
@@ -59,7 +66,68 @@ window.location.href = `https://discord-verifier.eva.ac/webview?url=$&#123;encod
</p>
<p class="mt-2 text-left">congrats! your discord account is now age verified.</p>
<h2 class="mt-12 text-2xl font-bold">how does this work</h2>
<h2 class="mt-4 text-2xl font-bold">
how to verify on other platforms (twitch, kick, snapchat, ...others)
</h2>
<p>
navigate to the age verification page and choose selfie, from there, get the url of the qr code
and put it in this input box, and press verify
</p>
<div class="mt-4 flex gap-4">
<input
class="w-full border-2 border-white/50 p-2"
bind:value={qrCodeUrl}
placeholder="https://..."
/>
<button
class="w-24 border-2 border-white/50 p-2 hover:cursor-pointer"
onclick={(e) => {
e.preventDefault();
qrCodeError = null;
qrCodeSuccess = false;
if (!qrCodeUrl) {
qrCodeError = "you didn't enter a qr code url";
return;
}
fetch('/api/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'qr_link',
identifier: qrCodeUrl
})
})
.then(async (r) => {
if (!r.ok) {
qrCodeError = await r.text().catch(() => 'unexpected error');
return;
}
qrCodeSuccess = true;
})
.catch((e) => {
console.error('error sending verify request', e);
qrCodeError = 'unexpected error, please check your browser console.';
});
}}>verify</button
>
</div>
{#if qrCodeSuccess}
<p class="mt-4 text-green-500">
your account has successfully been verified. go back to the site tab to continue
</p>
{:else if qrCodeError}
<p class="mt-4 text-red-500">
{qrCodeError}
</p>
{/if}
<h2 class="mt-8 text-2xl font-bold">how does this work</h2>
<p>
k-id, the age verification provider discord uses doesn't store or send your face to the server.
instead, it sends a bunch of metadata about your face and general process details. while this is
+106 -32
View File
@@ -6,7 +6,7 @@ const K_ID_DEPLOYMENT_ID = '20260210222654-016f063-production';
const K_ID_PRIVATELY_ACTION_ID = '40dc500368168e3130ea4625c535d5a9bbbf0243f1';
const K_ID_NEXT_ROUTER_TREE =
'%5B%22%22%2C%7B%22children%22%3A%5B%22verify%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%2Ctrue%5D';
const PRIVATELY_URL_REGEX = /(https:\/\/[a-z0-9]+\.cloudfront\.net\/.*)(?=:\{)/
const PRIVATELY_URL_REGEX = /(https:\/\/[a-z0-9]+\.cloudfront\.net\/.*)(?=:\{)/;
const jsonResponse = (body: unknown, status: number = 200, extraHeaders: object = {}) =>
new Response(JSON.stringify(body), {
@@ -748,7 +748,13 @@ export const POST = async (event: RequestEvent) => {
const { type, identifier }: { type: string; identifier: string } = await event.request.json();
if (type === 'webview') {
const webviewUrl = new URL(identifier);
let webviewUrl: URL;
try {
webviewUrl = new URL(identifier);
} catch (e) {
return jsonResponse({ error: 'error parsing webview url' }, 400);
}
if (webviewUrl.host !== 'family.k-id.com' || webviewUrl.pathname !== '/verify') {
return jsonResponse({ error: 'unexpected webview url' }, 400);
}
@@ -764,42 +770,110 @@ export const POST = async (event: RequestEvent) => {
}
const payload = JSON.parse(atob(parts[1]));
// fetch the webview first
await fetch(webviewUrl)
const privately = await fetch(webviewUrl, {
await fetch(webviewUrl, {
headers: {
'User-Agent': userAgent,
accept: '*/*',
'accept-language': location.lang,
priority: 'u=1, i',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
Origin: 'https://family.k-id.com',
Referer: identifier
}
});
const privatelyActionRes = await fetch(webviewUrl, {
method: 'POST',
headers: {
'User-Agent': userAgent,
accept: '*/*',
'accept-language': location.lang,
priority: 'u=1, i',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Next-Action': K_ID_PRIVATELY_ACTION_ID,
'Next-Router-State-Tree': K_ID_NEXT_ROUTER_TREE,
'X-Deployment-Id': K_ID_DEPLOYMENT_ID,
'Content-Type': 'application/json',
Origin: 'https://family.k-id.com',
Referer: identifier
},
body: JSON.stringify([{"verificationId":payload.jti,"useBranding":true,"attemptId":crypto.randomUUID()}])
}).then(res => res.text());
'User-Agent': userAgent,
accept: '*/*',
'accept-language': location.lang,
priority: 'u=1, i',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Next-Action': K_ID_PRIVATELY_ACTION_ID,
'Next-Router-State-Tree': K_ID_NEXT_ROUTER_TREE,
'X-Deployment-Id': K_ID_DEPLOYMENT_ID,
'Content-Type': 'application/json',
Origin: 'https://family.k-id.com',
Referer: identifier
},
body: JSON.stringify([
{ verificationId: payload.jti, useBranding: true, attemptId: crypto.randomUUID() }
])
});
const match = privately.match(PRIVATELY_URL_REGEX);
if (match) {
const privatelyUrl = new URL(match[1]);
const privatelyToken = privatelyUrl.searchParams.get('token');
if (!privatelyToken) {
return jsonResponse({ error: 'no privately token' }, 400);
}
await verify(userAgent, location, privatelyToken);
} else {
return jsonResponse({ error: 'no privately url found in response' }, 400);
if (!privatelyActionRes.ok) {
return jsonResponse(
{
error: `failed to execute k-id privately action (status=${privatelyActionRes.status})`
},
500
);
}
return jsonResponse({ success: true }, 200);
const privatelyActionBody = await privatelyActionRes.text();
const match = privatelyActionBody.match(PRIVATELY_URL_REGEX);
if (!match) {
return jsonResponse({ error: 'no privately url found in response' }, 500);
}
const privatelyUrl = new URL(match[1]);
const privatelyToken = privatelyUrl.searchParams.get('token');
if (!privatelyToken) {
return jsonResponse({ error: 'no privately token' }, 500);
}
await verify(userAgent, location, privatelyToken);
return jsonResponse({ success: true });
}
if (type === 'qr_link') {
let qrCodeUrl: URL;
try {
qrCodeUrl = new URL(identifier);
} catch (e) {
return jsonResponse({ error: 'error parsing qr code url' }, 400);
}
const shortlinkId = qrCodeUrl.searchParams.get('sl');
if (!shortlinkId) {
return jsonResponse({ error: 'failed to get shortlink id from qr code url' }, 400);
}
const res = await fetch(`${BASE_URL}/shortlinks/${encodeURIComponent(shortlinkId)}`, {
headers: {
'User-Agent': userAgent,
accept: '*/*',
'accept-language': location.lang,
priority: 'u=1, i',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site'
}
});
if (!res.ok) {
return jsonResponse(`failed to get shortlink (status=${res.status})`, 400);
}
const data = await res.json();
const originalUrl = new URL(data.Item.original_url.S.replace('#', ''));
const token = originalUrl.searchParams.get('token');
if (!token) {
return jsonResponse({ error: 'token not found in original url' }, 500);
}
await verify(userAgent, location, token);
return jsonResponse({ success: true });
}
return jsonResponse({ error: 'unexpected type' }, 400);
+12 -2
View File
@@ -31,7 +31,7 @@
});
</script>
<div class="flex min-h-screen w-screen items-center justify-center text-3xl">
<div class="flex min-h-screen w-screen flex-col items-center justify-center text-center">
{#if webviewUrl}
{#if success}
<p class="text-green-500">your account is now age verified. enjoy</p>
@@ -41,6 +41,16 @@
<p>automatically verifying your age</p>
{/if}
{:else}
<p>invalid url</p>
<p>
<span class="text-red-500">invalid url</span> <br /> did you mean to go to
<a class="underline" href="/">the instructions page?</a>
</p>
{/if}
</div>
<div class="absolute bottom-4 flex w-screen justify-center">
<p class="text-center text-white/50">
made by <a class="underline" href="https://kibty.town" target="_blank">xyzeva</a> and
<a class="underline" href="https://github.com/Dziurwa14" target="_blank">Dziurwa</a>, greetz to
<a class="underline" href="https://amplitudes.me/" target="_blank">amplitudes</a> (for previous work)
</p>
</div>