From 4f030a51c09c76e3a6e9bd2aee6d693d3e0f7fd6 Mon Sep 17 00:00:00 2001 From: DecDuck Date: Thu, 31 Jul 2025 09:39:38 +1000 Subject: [PATCH 1/6] feat: code-based authorization --- assets/tailwindcss.css | 9 ++ .../{[id]/callback.vue => authorize/[id].vue} | 75 ++++++++---- pages/client/code/index.vue | 112 ++++++++++++++++++ .../api/v1/client/auth/callback/index.post.ts | 10 +- server/api/v1/client/auth/code/index.get.ts | 17 +++ server/api/v1/client/auth/code/index.post.ts | 35 ++++++ server/api/v1/client/auth/code/ws.get.ts | 14 +++ .../client/auth/{callback => }/index.get.ts | 12 +- server/api/v1/client/auth/initiate.post.ts | 33 +++--- server/internal/clients/handler.ts | 46 ++++++- 10 files changed, 315 insertions(+), 48 deletions(-) rename pages/client/{[id]/callback.vue => authorize/[id].vue} (79%) create mode 100644 pages/client/code/index.vue create mode 100644 server/api/v1/client/auth/code/index.get.ts create mode 100644 server/api/v1/client/auth/code/index.post.ts create mode 100644 server/api/v1/client/auth/code/ws.get.ts rename server/api/v1/client/auth/{callback => }/index.get.ts (71%) diff --git a/assets/tailwindcss.css b/assets/tailwindcss.css index 5c2596be..97cf5d42 100644 --- a/assets/tailwindcss.css +++ b/assets/tailwindcss.css @@ -2,3 +2,12 @@ @plugin "@tailwindcss/typography"; @plugin "@tailwindcss/forms"; @config "../tailwind.config.js"; + +@layer base { + input[type="number"]::-webkit-outer-spin-button, + input[type="number"]::-webkit-inner-spin-button, + input[type="number"] { + -webkit-appearance: none; + -moz-appearance: textfield !important; + } +} diff --git a/pages/client/[id]/callback.vue b/pages/client/authorize/[id].vue similarity index 79% rename from pages/client/[id]/callback.vue rename to pages/client/authorize/[id].vue index 71277b83..f8d05769 100644 --- a/pages/client/[id]/callback.vue +++ b/pages/client/authorize/[id].vue @@ -155,38 +155,58 @@ import { XCircleIcon, } from "@heroicons/vue/16/solid"; import { LockClosedIcon } from "@heroicons/vue/20/solid"; -import { CheckCircleIcon } from "@heroicons/vue/24/outline"; +import { CheckCircleIcon, CloudIcon } from "@heroicons/vue/24/outline"; import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/vue/24/solid"; +import type { FetchError } from "ofetch"; +import { InternalClientCapability } from "~/server/internal/clients/capabilities"; const route = useRoute(); const clientId = route.params.id; -const clientData = await $dropFetch( - `/api/v1/client/auth/callback?id=${clientId}`, -); +const clientData = await $dropFetch(`/api/v1/client/auth?id=${clientId}`); const completed = ref(false); const error = ref(); const authToken = ref(); async function authorize() { - const { redirect, token } = await $dropFetch("/api/v1/client/auth/callback", { - method: "POST", - body: { id: clientId }, + switch (clientData.mode) { + case "callback": { + const { redirect, token } = await $dropFetch( + `/api/v1/client/auth/callback`, + { + method: "POST", + body: { id: clientId }, + }, + ); + authToken.value = token; + window.location.replace(redirect); + return; + } + case "code": { + await $dropFetch("/api/v1/client/auth/code", { + method: "POST", + body: { id: clientId }, + }); + return; + } + } + throw createError({ + statusCode: 500, + statusMessage: "Unknown client auth mode: " + clientData.mode, + fatal: true, }); - authToken.value = token; - window.location.replace(redirect); } -function authorize_wrapper() { - authorize() - .catch((e) => { - const errorMessage = e.statusMessage || "An unknown error occurred."; - error.value = errorMessage; - }) - .then(() => { - completed.value = true; - }); +async function authorize_wrapper() { + try { + await authorize(); + } catch (e) { + const errorMessage = (e as FetchError)?.statusMessage || "An unknown error occurred."; + error.value = errorMessage; + } finally { + completed.value = true; + } } const scopes = [ @@ -198,20 +218,27 @@ const scopes = [ icon: ArrowDownTrayIcon, }, { + name: "Update your status", + description: + "The client will be able to update your status, and affect your playtime.", + href: "/docs/access/status", + icon: UserGroupIcon, + }, + clientData.capabilities[InternalClientCapability.PeerAPI] && { name: "Access the Drop network", description: "The client will be able to establish P2P connections with other users to enable features like download aggregation, Remote LAN play and P2P multiplayer.", href: "/docs/access/network", icon: LockClosedIcon, }, - { - name: "Manage your account", + clientData.capabilities[InternalClientCapability.CloudSaves] && { + name: "Upload and sync cloud saves", description: - "The client will be able to change your account details, and friend statuses on your behalf.", - href: "/docs/access/account", - icon: UserGroupIcon, + "The client will be able to upload new cloud saves, and edit your existing ones.", + href: "/docs/access/cloud", + icon: CloudIcon, }, -]; +].filter((e) => e !== undefined); useHead({ title: "Authorize", diff --git a/pages/client/code/index.vue b/pages/client/code/index.vue new file mode 100644 index 00000000..dff95766 --- /dev/null +++ b/pages/client/code/index.vue @@ -0,0 +1,112 @@ + + + diff --git a/server/api/v1/client/auth/callback/index.post.ts b/server/api/v1/client/auth/callback/index.post.ts index b08a57bf..bc63be70 100644 --- a/server/api/v1/client/auth/callback/index.post.ts +++ b/server/api/v1/client/auth/callback/index.post.ts @@ -8,13 +8,19 @@ export default defineEventHandler(async (h3) => { const body = await readBody(h3); const clientId = await body.id; - const data = await clientHandler.fetchClientMetadata(clientId); - if (!data) + const client = await clientHandler.fetchClient(clientId); + if (!client) throw createError({ statusCode: 400, statusMessage: "Invalid or expired client ID.", }); + if (client.userId != user.userId) + throw createError({ + statusCode: 403, + statusMessage: "Not allowed to authorize this client.", + }); + const token = await clientHandler.generateAuthToken(clientId); return { diff --git a/server/api/v1/client/auth/code/index.get.ts b/server/api/v1/client/auth/code/index.get.ts new file mode 100644 index 00000000..03300aa9 --- /dev/null +++ b/server/api/v1/client/auth/code/index.get.ts @@ -0,0 +1,17 @@ +import clientHandler from "~/server/internal/clients/handler"; + +export default defineEventHandler(async (h3) => { + const query = getQuery(h3); + const code = query.code?.toString(); + if (!code) + throw createError({ + statusCode: 400, + statusMessage: "Code required in query params.", + }); + + const clientId = await clientHandler.fetchClientIdByCode(code); + if (!clientId) + throw createError({ statusCode: 400, statusMessage: "Invalid code." }); + + return clientId; +}); diff --git a/server/api/v1/client/auth/code/index.post.ts b/server/api/v1/client/auth/code/index.post.ts new file mode 100644 index 00000000..998dbef0 --- /dev/null +++ b/server/api/v1/client/auth/code/index.post.ts @@ -0,0 +1,35 @@ +import clientHandler from "~/server/internal/clients/handler"; +import sessionHandler from "~/server/internal/session"; + +export default defineEventHandler(async (h3) => { + const user = await sessionHandler.getSession(h3); + if (!user) throw createError({ statusCode: 403 }); + + const body = await readBody(h3); + const clientId = await body.id; + + const client = await clientHandler.fetchClient(clientId); + if (!client) + throw createError({ + statusCode: 400, + statusMessage: "Invalid or expired client ID.", + }); + + if (client.userId != user.userId) + throw createError({ + statusCode: 403, + statusMessage: "Not allowed to authorize this client.", + }); + + if (!client.data.peer) + throw createError({ + statusCode: 500, + statusMessage: "No client listening for authorization.", + }); + + const token = await clientHandler.generateAuthToken(clientId); + + await client.data.peer.send(token); + + return; +}); diff --git a/server/api/v1/client/auth/code/ws.get.ts b/server/api/v1/client/auth/code/ws.get.ts new file mode 100644 index 00000000..43968284 --- /dev/null +++ b/server/api/v1/client/auth/code/ws.get.ts @@ -0,0 +1,14 @@ +import clientHandler from "~/server/internal/clients/handler"; + +export default defineWebSocketHandler({ + async open(peer) { + const h3 = { headers: peer.request?.headers ?? new Headers() }; + const code = h3.headers.get("Authorization"); + if (!code) + throw createError({ + statusCode: 400, + statusMessage: "Code required in Authorization header.", + }); + await clientHandler.connectCodeListener(code, peer); + }, +}); diff --git a/server/api/v1/client/auth/callback/index.get.ts b/server/api/v1/client/auth/index.get.ts similarity index 71% rename from server/api/v1/client/auth/callback/index.get.ts rename to server/api/v1/client/auth/index.get.ts index d9545347..92cf16c5 100644 --- a/server/api/v1/client/auth/callback/index.get.ts +++ b/server/api/v1/client/auth/index.get.ts @@ -13,14 +13,20 @@ export default defineEventHandler(async (h3) => { statusMessage: "Provide client ID in request params as 'id'", }); - const data = await clientHandler.fetchClientMetadata(providedClientId); - if (!data) + const client = await clientHandler.fetchClient(providedClientId); + if (!client) throw createError({ statusCode: 404, statusMessage: "Request not found.", }); + if (client.userId && user.userId !== client.userId) + throw createError({ + statusCode: 400, + statusMessage: "Client already claimed.", + }); + await clientHandler.attachUserId(providedClientId, user.userId); - return data; + return client.data; }); diff --git a/server/api/v1/client/auth/initiate.post.ts b/server/api/v1/client/auth/initiate.post.ts index b03ac7d7..64186b5e 100644 --- a/server/api/v1/client/auth/initiate.post.ts +++ b/server/api/v1/client/auth/initiate.post.ts @@ -1,3 +1,5 @@ +import { type } from "arktype"; +import { readDropValidatedBody, throwingArktype } from "~/server/arktype"; import type { CapabilityConfiguration, InternalClientCapability, @@ -5,23 +7,23 @@ import type { import capabilityManager, { validCapabilities, } from "~/server/internal/clients/capabilities"; -import clientHandler from "~/server/internal/clients/handler"; +import clientHandler, { AuthMode } from "~/server/internal/clients/handler"; import { parsePlatform } from "~/server/internal/utils/parseplatform"; +const ClientAuthInitiate = type({ + name: "string", + platform: "string", + capabilities: "object", + mode: type.valueOf(AuthMode).default(AuthMode.Callback), +}).configure(throwingArktype); + export default defineEventHandler(async (h3) => { - const body = await readBody(h3); + const body = await readDropValidatedBody(h3, ClientAuthInitiate); - const name = body.name; const platformRaw = body.platform; const capabilities: Partial = body.capabilities ?? {}; - if (!name || !platformRaw) - throw createError({ - statusCode: 400, - statusMessage: "Missing name or platform in body", - }); - const platform = parsePlatform(platformRaw); if (!platform) throw createError({ @@ -29,12 +31,6 @@ export default defineEventHandler(async (h3) => { statusMessage: "Invalid or unsupported platform", }); - if (!capabilities || typeof capabilities !== "object") - throw createError({ - statusCode: 400, - statusMessage: "Capabilities must be an array", - }); - const capabilityIterable = Object.entries(capabilities) as Array< [InternalClientCapability, object] >; @@ -64,11 +60,12 @@ export default defineEventHandler(async (h3) => { statusMessage: "Invalid capability configuration.", }); - const clientId = await clientHandler.initiate({ - name, + const result = await clientHandler.initiate({ + name: body.name, platform, capabilities, + mode: body.mode, }); - return `/client/${clientId}/callback`; + return result; }); diff --git a/server/internal/clients/handler.ts b/server/internal/clients/handler.ts index 5ab748e0..c7505748 100644 --- a/server/internal/clients/handler.ts +++ b/server/internal/clients/handler.ts @@ -7,11 +7,19 @@ import type { InternalClientCapability, } from "./capabilities"; import capabilityManager from "./capabilities"; +import type { PeerImpl } from "../tasks"; + +export enum AuthMode { + Callback = "callback", + Code = "code", +} export interface ClientMetadata { name: string; platform: Platform; capabilities: Partial; + mode: AuthMode; + peer?: PeerImpl; } export class ClientHandler { @@ -24,6 +32,7 @@ export class ClientHandler { authToken?: string; } >(); + private codeClientMap = new Map(); async initiate(metadata: ClientMetadata) { const clientId = randomUUID(); @@ -34,12 +43,47 @@ export class ClientHandler { () => { if (this.temporaryClientTable.has(clientId)) this.temporaryClientTable.delete(clientId); + const code = this.codeClientMap + .entries() + .find(([_, v]) => v === clientId); + if (code) this.codeClientMap.delete(code[0]); }, 1000 * 60 * 10, ), // 10 minutes }); - return clientId; + switch (metadata.mode) { + case AuthMode.Callback: + return `/client/authorize/${clientId}`; + case AuthMode.Code: { + const code = randomUUID().replaceAll(/-/, "").slice(0, 7).toUpperCase(); + this.codeClientMap.set(code, clientId); + return code; + } + } + } + + async connectCodeListener(code: string, peer: PeerImpl) { + const clientId = this.codeClientMap.get(code); + if (!clientId) + throw createError({ + statusCode: 403, + statusMessage: "Invalid or unknown code.", + }); + const metadata = this.temporaryClientTable.get(clientId); + if (!metadata) + throw createError({ statusCode: 500, statusMessage: "Broken code." }); + if (metadata.data.peer) + throw createError({ + statusCode: 400, + statusMessage: "Pre-existing listener for this code.", + }); + metadata.data.peer = peer; + this.temporaryClientTable.set(clientId, metadata); + } + + async fetchClientIdByCode(code: string){ + return this.codeClientMap.get(code); } async fetchClientMetadata(clientId: string) { From 9c231c624a76899bd61e56ad36b174ea5692928e Mon Sep 17 00:00:00 2001 From: DecDuck Date: Thu, 31 Jul 2025 13:01:10 +1000 Subject: [PATCH 2/6] fix: final touches --- components/UserHeader/UserWidget.vue | 5 +++ i18n/locales/en_us.json | 4 ++ pages/client/authorize/[id].vue | 12 +++--- pages/client/code/index.vue | 43 +++++++++++++++----- server/api/v1/client/auth/code/index.get.ts | 2 +- server/api/v1/client/auth/code/index.post.ts | 4 +- server/api/v1/client/auth/code/ws.get.ts | 20 +++++---- server/internal/clients/handler.ts | 13 +++--- 8 files changed, 71 insertions(+), 32 deletions(-) diff --git a/components/UserHeader/UserWidget.vue b/components/UserHeader/UserWidget.vue index 35e238d9..cf75bba5 100644 --- a/components/UserHeader/UserWidget.vue +++ b/components/UserHeader/UserWidget.vue @@ -100,6 +100,11 @@ const navigation = computed(() => route: "/account", prefix: "", }, + { + label: "Authorize client", + route: "/client/code", + prefix: "", + }, ].filter((e) => e !== undefined), ); diff --git a/i18n/locales/en_us.json b/i18n/locales/en_us.json index 67408f92..fb0865a6 100644 --- a/i18n/locales/en_us.json +++ b/i18n/locales/en_us.json @@ -38,6 +38,10 @@ "requestedAccess": "\"{name}\" has requested access to your Drop account.", "success": "Successful!" }, + "code": { + "title": "Connect your Drop client", + "description": "Use a code to connect your Drop client if you are unable to open a web browser on your device." + }, "confirmPassword": "Confirm @:auth.password", "displayName": "Display Name", "email": "Email", diff --git a/pages/client/authorize/[id].vue b/pages/client/authorize/[id].vue index f8d05769..a3d2a9d1 100644 --- a/pages/client/authorize/[id].vue +++ b/pages/client/authorize/[id].vue @@ -1,7 +1,7 @@ diff --git a/server/api/v1/client/auth/code/index.get.ts b/server/api/v1/client/auth/code/index.get.ts index 03300aa9..6985231d 100644 --- a/server/api/v1/client/auth/code/index.get.ts +++ b/server/api/v1/client/auth/code/index.get.ts @@ -2,7 +2,7 @@ import clientHandler from "~/server/internal/clients/handler"; export default defineEventHandler(async (h3) => { const query = getQuery(h3); - const code = query.code?.toString(); + const code = query.code?.toString()?.toUpperCase(); if (!code) throw createError({ statusCode: 400, diff --git a/server/api/v1/client/auth/code/index.post.ts b/server/api/v1/client/auth/code/index.post.ts index 998dbef0..d47ba8f9 100644 --- a/server/api/v1/client/auth/code/index.post.ts +++ b/server/api/v1/client/auth/code/index.post.ts @@ -21,7 +21,7 @@ export default defineEventHandler(async (h3) => { statusMessage: "Not allowed to authorize this client.", }); - if (!client.data.peer) + if (!client.peer) throw createError({ statusCode: 500, statusMessage: "No client listening for authorization.", @@ -29,7 +29,7 @@ export default defineEventHandler(async (h3) => { const token = await clientHandler.generateAuthToken(clientId); - await client.data.peer.send(token); + await client.peer.send(`${clientId}/${token}`); return; }); diff --git a/server/api/v1/client/auth/code/ws.get.ts b/server/api/v1/client/auth/code/ws.get.ts index 43968284..d89504be 100644 --- a/server/api/v1/client/auth/code/ws.get.ts +++ b/server/api/v1/client/auth/code/ws.get.ts @@ -2,13 +2,17 @@ import clientHandler from "~/server/internal/clients/handler"; export default defineWebSocketHandler({ async open(peer) { - const h3 = { headers: peer.request?.headers ?? new Headers() }; - const code = h3.headers.get("Authorization"); - if (!code) - throw createError({ - statusCode: 400, - statusMessage: "Code required in Authorization header.", - }); - await clientHandler.connectCodeListener(code, peer); + try { + const h3 = { headers: peer.request?.headers ?? new Headers() }; + const code = h3.headers.get("Authorization"); + if (!code) + throw createError({ + statusCode: 400, + statusMessage: "Code required in Authorization header.", + }); + await clientHandler.connectCodeListener(code, peer); + } catch { + peer.close(); + } }, }); diff --git a/server/internal/clients/handler.ts b/server/internal/clients/handler.ts index c7505748..17a10e40 100644 --- a/server/internal/clients/handler.ts +++ b/server/internal/clients/handler.ts @@ -19,7 +19,6 @@ export interface ClientMetadata { platform: Platform; capabilities: Partial; mode: AuthMode; - peer?: PeerImpl; } export class ClientHandler { @@ -30,6 +29,7 @@ export class ClientHandler { data: ClientMetadata; userId?: string; authToken?: string; + peer?: PeerImpl; } >(); private codeClientMap = new Map(); @@ -56,7 +56,10 @@ export class ClientHandler { case AuthMode.Callback: return `/client/authorize/${clientId}`; case AuthMode.Code: { - const code = randomUUID().replaceAll(/-/, "").slice(0, 7).toUpperCase(); + const code = randomUUID() + .replaceAll(/-/g, "") + .slice(0, 7) + .toUpperCase(); this.codeClientMap.set(code, clientId); return code; } @@ -73,16 +76,16 @@ export class ClientHandler { const metadata = this.temporaryClientTable.get(clientId); if (!metadata) throw createError({ statusCode: 500, statusMessage: "Broken code." }); - if (metadata.data.peer) + if (metadata.peer) throw createError({ statusCode: 400, statusMessage: "Pre-existing listener for this code.", }); - metadata.data.peer = peer; + metadata.peer = peer; this.temporaryClientTable.set(clientId, metadata); } - async fetchClientIdByCode(code: string){ + async fetchClientIdByCode(code: string) { return this.codeClientMap.get(code); } From b7880003da4d642471020b3f9c81b08bc149be2b Mon Sep 17 00:00:00 2001 From: DecDuck Date: Thu, 31 Jul 2025 13:02:35 +1000 Subject: [PATCH 3/6] fix: require session on code fetch endpoint --- server/api/v1/client/auth/code/index.get.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/api/v1/client/auth/code/index.get.ts b/server/api/v1/client/auth/code/index.get.ts index 6985231d..74bc6e87 100644 --- a/server/api/v1/client/auth/code/index.get.ts +++ b/server/api/v1/client/auth/code/index.get.ts @@ -1,6 +1,10 @@ import clientHandler from "~/server/internal/clients/handler"; +import sessionHandler from "~/server/internal/session"; export default defineEventHandler(async (h3) => { + const user = await sessionHandler.getSession(h3); + if (!user) throw createError({ statusCode: 403 }); + const query = getQuery(h3); const code = query.code?.toString()?.toUpperCase(); if (!code) From d0dcf367fb9fe81614d83e34afb0a824c467f429 Mon Sep 17 00:00:00 2001 From: DecDuck Date: Fri, 1 Aug 2025 12:54:22 +1000 Subject: [PATCH 4/6] feat: better error handling --- server/api/v1/client/auth/code/index.post.ts | 2 +- server/api/v1/client/auth/code/ws.get.ts | 9 ++++++++- server/internal/clients/handler.ts | 11 ++++++++++- server/internal/tasks/index.ts | 1 + 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/server/api/v1/client/auth/code/index.post.ts b/server/api/v1/client/auth/code/index.post.ts index d47ba8f9..fcbb8afe 100644 --- a/server/api/v1/client/auth/code/index.post.ts +++ b/server/api/v1/client/auth/code/index.post.ts @@ -29,7 +29,7 @@ export default defineEventHandler(async (h3) => { const token = await clientHandler.generateAuthToken(clientId); - await client.peer.send(`${clientId}/${token}`); + await client.peer.send(JSON.stringify({ type: "token", value: `${clientId}/${token}` })); return; }); diff --git a/server/api/v1/client/auth/code/ws.get.ts b/server/api/v1/client/auth/code/ws.get.ts index d89504be..637d8df4 100644 --- a/server/api/v1/client/auth/code/ws.get.ts +++ b/server/api/v1/client/auth/code/ws.get.ts @@ -1,3 +1,4 @@ +import type { FetchError } from "ofetch"; import clientHandler from "~/server/internal/clients/handler"; export default defineWebSocketHandler({ @@ -11,7 +12,13 @@ export default defineWebSocketHandler({ statusMessage: "Code required in Authorization header.", }); await clientHandler.connectCodeListener(code, peer); - } catch { + } catch (e) { + peer.send( + JSON.stringify({ + type: "error", + value: (e as FetchError)?.statusMessage, + }), + ); peer.close(); } }, diff --git a/server/internal/clients/handler.ts b/server/internal/clients/handler.ts index 17a10e40..aa7e2d7f 100644 --- a/server/internal/clients/handler.ts +++ b/server/internal/clients/handler.ts @@ -41,8 +41,17 @@ export class ClientHandler { data: metadata, timeout: setTimeout( () => { - if (this.temporaryClientTable.has(clientId)) + const client = this.temporaryClientTable.get(clientId); + if (client) { + if (client.peer) { + client.peer.send( + JSON.stringify({ type: "error", value: "Request timed out." }), + ); + client.peer.close(); + } this.temporaryClientTable.delete(clientId); + } + const code = this.codeClientMap .entries() .find(([_, v]) => v === clientId); diff --git a/server/internal/tasks/index.ts b/server/internal/tasks/index.ts index bb79301c..8e75da1b 100644 --- a/server/internal/tasks/index.ts +++ b/server/internal/tasks/index.ts @@ -445,6 +445,7 @@ export type TaskMessage = { export type PeerImpl = { send: (message: string) => void; + close: () => void; }; export interface BuildTask { From 813c27626643806819eee6a7b23d471819eb62e9 Mon Sep 17 00:00:00 2001 From: DecDuck Date: Fri, 1 Aug 2025 12:57:04 +1000 Subject: [PATCH 5/6] refactor: move auth send to client handler --- server/api/v1/client/auth/code/index.post.ts | 2 +- server/internal/clients/handler.ts | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/server/api/v1/client/auth/code/index.post.ts b/server/api/v1/client/auth/code/index.post.ts index fcbb8afe..593b773b 100644 --- a/server/api/v1/client/auth/code/index.post.ts +++ b/server/api/v1/client/auth/code/index.post.ts @@ -29,7 +29,7 @@ export default defineEventHandler(async (h3) => { const token = await clientHandler.generateAuthToken(clientId); - await client.peer.send(JSON.stringify({ type: "token", value: `${clientId}/${token}` })); + await clientHandler.sendAuthToken(clientId, token); return; }); diff --git a/server/internal/clients/handler.ts b/server/internal/clients/handler.ts index aa7e2d7f..87817525 100644 --- a/server/internal/clients/handler.ts +++ b/server/internal/clients/handler.ts @@ -124,6 +124,13 @@ export class ClientHandler { return token; } + async sendAuthToken(clientId: string, token: string){ + const client = this.temporaryClientTable.get(clientId); + if(!client) throw createError({statusCode: 500, statusMessage: "Corrupted code, please restart the process."}); + if(!client.peer) throw createError({statusCode: 400, statusMessage: "Client has not connected yet. Please try again later."}); + await client.peer.send(JSON.stringify({ type: "token", value: `${clientId}/${token}` })); + } + async fetchClientMetadataByToken(token: string) { return this.temporaryClientTable .entries() From 45d8567ac9bb8a1a291fd7601e1c3241097d92b5 Mon Sep 17 00:00:00 2001 From: DecDuck Date: Fri, 1 Aug 2025 13:07:23 +1000 Subject: [PATCH 6/6] fix: lint --- server/internal/clients/handler.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/server/internal/clients/handler.ts b/server/internal/clients/handler.ts index 87817525..eaae288f 100644 --- a/server/internal/clients/handler.ts +++ b/server/internal/clients/handler.ts @@ -124,11 +124,21 @@ export class ClientHandler { return token; } - async sendAuthToken(clientId: string, token: string){ + async sendAuthToken(clientId: string, token: string) { const client = this.temporaryClientTable.get(clientId); - if(!client) throw createError({statusCode: 500, statusMessage: "Corrupted code, please restart the process."}); - if(!client.peer) throw createError({statusCode: 400, statusMessage: "Client has not connected yet. Please try again later."}); - await client.peer.send(JSON.stringify({ type: "token", value: `${clientId}/${token}` })); + if (!client) + throw createError({ + statusCode: 500, + statusMessage: "Corrupted code, please restart the process.", + }); + if (!client.peer) + throw createError({ + statusCode: 400, + statusMessage: "Client has not connected yet. Please try again later.", + }); + await client.peer.send( + JSON.stringify({ type: "token", value: `${clientId}/${token}` }), + ); } async fetchClientMetadataByToken(token: string) {