diff --git a/scripts/standalone-verify.ts b/scripts/standalone-verify.ts index 7c02881..55eef70 100644 --- a/scripts/standalone-verify.ts +++ b/scripts/standalone-verify.ts @@ -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 }, diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 65e8182..206d446 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -4,4 +4,8 @@ let { children } = $props(); + + discord/twitch/kick/snapchat age verifier + + {@render children()} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 2d9a02c..cca1a59 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,17 +1,24 @@ - - discord age verifier - +
-

discord age verifier

-

age verifies your discord account automatically as an adult

+

+ discord/twitch/kick/snapchat age verifier +

+

+ age verifies your account automatically as an adult on any website using k-id +

- made by xyzeva and + made by xyzeva and Dziurwa, greetz to amplitudes (for previous work)

-

+

how to verify on discord

+

it doesn't matter 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) => { 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=${encodeURIComponent((await api.post({ url: '/age-verification/verify', body: { method: 3 }})).body.verification_webview_url)}` +window.location.href = `https://age-verifier.kibty.town/webview?url=${encodeURIComponent((await api.post({ url: '/age-verification/verify', body: { method: 3 }})).body.verification_webview_url)}`

(feel free to read the code, we made it readable and we have nothing to hide)

@@ -59,7 +66,68 @@ window.location.href = `https://discord-verifier.eva.ac/webview?url=${encod

congrats! your discord account is now age verified.

-

how does this work

+

+ how to verify on other platforms (twitch, kick, snapchat, ...others) +

+

+ 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 +

+ +
+ + +
+ {#if qrCodeSuccess} +

+ your account has successfully been verified. go back to the site tab to continue +

+ {:else if qrCodeError} +

+ {qrCodeError} +

+ {/if} + +

how does this work

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 diff --git a/src/routes/api/verify/+server.ts b/src/routes/api/verify/+server.ts index 681c163..5aa4ee6 100644 --- a/src/routes/api/verify/+server.ts +++ b/src/routes/api/verify/+server.ts @@ -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); diff --git a/src/routes/webview/+page.svelte b/src/routes/webview/+page.svelte index 1053e2f..2a1d15c 100644 --- a/src/routes/webview/+page.svelte +++ b/src/routes/webview/+page.svelte @@ -31,7 +31,7 @@ }); -

+
{#if webviewUrl} {#if success}

your account is now age verified. enjoy

@@ -41,6 +41,16 @@

automatically verifying your age

{/if} {:else} -

invalid url

+

+ invalid url
did you mean to go to + the instructions page? +

{/if}
+
+

+ made by xyzeva and + Dziurwa, greetz to + amplitudes (for previous work) +

+