mirror of
https://github.com/kmc7468/arkvault.git
synced 2026-02-04 08:06:56 +00:00
/api/client 아래의 Endpoint들을 tRPC로 마이그레이션
This commit is contained in:
@@ -98,22 +98,6 @@ export const createUserClient = async (userId: number, clientId: number) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAllUserClients = async (userId: number) => {
|
|
||||||
const userClients = await db
|
|
||||||
.selectFrom("user_client")
|
|
||||||
.selectAll()
|
|
||||||
.where("user_id", "=", userId)
|
|
||||||
.execute();
|
|
||||||
return userClients.map(
|
|
||||||
({ user_id, client_id, state }) =>
|
|
||||||
({
|
|
||||||
userId: user_id,
|
|
||||||
clientId: client_id,
|
|
||||||
state,
|
|
||||||
}) satisfies UserClient,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getUserClient = async (userId: number, clientId: number) => {
|
export const getUserClient = async (userId: number, clientId: number) => {
|
||||||
const userClient = await db
|
const userClient = await db
|
||||||
.selectFrom("user_client")
|
.selectFrom("user_client")
|
||||||
|
|||||||
10
src/lib/server/db/index.ts
Normal file
10
src/lib/server/db/index.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export * as CategoryRepo from "./category";
|
||||||
|
export * as ClientRepo from "./client";
|
||||||
|
export * as FileRepo from "./file";
|
||||||
|
export * as HskRepo from "./hsk";
|
||||||
|
export * as MediaRepo from "./media";
|
||||||
|
export * as MekRepo from "./mek";
|
||||||
|
export * as SessionRepo from "./session";
|
||||||
|
export * as UserRepo from "./user";
|
||||||
|
|
||||||
|
export * from "./error";
|
||||||
@@ -60,19 +60,6 @@ export const registerInitialMek = async (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getInitialMek = async (userId: number) => {
|
|
||||||
const mek = await db
|
|
||||||
.selectFrom("master_encryption_key")
|
|
||||||
.selectAll()
|
|
||||||
.where("user_id", "=", userId)
|
|
||||||
.where("version", "=", 1)
|
|
||||||
.limit(1)
|
|
||||||
.executeTakeFirst();
|
|
||||||
return mek
|
|
||||||
? ({ userId: mek.user_id, version: mek.version, state: mek.state } satisfies Mek)
|
|
||||||
: null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAllValidClientMeks = async (userId: number, clientId: number) => {
|
export const getAllValidClientMeks = async (userId: number, clientId: number) => {
|
||||||
const clientMeks = await db
|
const clientMeks = await db
|
||||||
.selectFrom("client_master_encryption_key")
|
.selectFrom("client_master_encryption_key")
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
import { error } from "@sveltejs/kit";
|
import { error } from "@sveltejs/kit";
|
||||||
import { getUserClientWithDetails } from "$lib/server/db/client";
|
import { getUserClientWithDetails } from "$lib/server/db/client";
|
||||||
import { getInitialMek } from "$lib/server/db/mek";
|
|
||||||
import { verifySignature } from "$lib/server/modules/crypto";
|
import { verifySignature } from "$lib/server/modules/crypto";
|
||||||
|
|
||||||
export const isInitialMekNeeded = async (userId: number) => {
|
|
||||||
const initialMek = await getInitialMek(userId);
|
|
||||||
return !initialMek;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const verifyClientEncMekSig = async (
|
export const verifyClientEncMekSig = async (
|
||||||
userId: number,
|
userId: number,
|
||||||
clientId: number,
|
clientId: number,
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
export const clientListResponse = z.object({
|
|
||||||
clients: z.array(
|
|
||||||
z.object({
|
|
||||||
id: z.number().int().positive(),
|
|
||||||
state: z.enum(["pending", "active"]),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
});
|
|
||||||
export type ClientListResponse = z.output<typeof clientListResponse>;
|
|
||||||
|
|
||||||
export const clientRegisterRequest = z.object({
|
|
||||||
encPubKey: z.string().base64().nonempty(),
|
|
||||||
sigPubKey: z.string().base64().nonempty(),
|
|
||||||
});
|
|
||||||
export type ClientRegisterRequest = z.input<typeof clientRegisterRequest>;
|
|
||||||
|
|
||||||
export const clientRegisterResponse = z.object({
|
|
||||||
id: z.number().int().positive(),
|
|
||||||
challenge: z.string().base64().nonempty(),
|
|
||||||
});
|
|
||||||
export type ClientRegisterResponse = z.output<typeof clientRegisterResponse>;
|
|
||||||
|
|
||||||
export const clientRegisterVerifyRequest = z.object({
|
|
||||||
id: z.number().int().positive(),
|
|
||||||
answerSig: z.string().base64().nonempty(),
|
|
||||||
});
|
|
||||||
export type ClientRegisterVerifyRequest = z.input<typeof clientRegisterVerifyRequest>;
|
|
||||||
|
|
||||||
export const clientStatusResponse = z.object({
|
|
||||||
id: z.number().int().positive(),
|
|
||||||
state: z.enum(["pending", "active"]),
|
|
||||||
isInitialMekNeeded: z.boolean(),
|
|
||||||
});
|
|
||||||
export type ClientStatusResponse = z.output<typeof clientStatusResponse>;
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
export * from "./auth";
|
export * from "./auth";
|
||||||
export * from "./category";
|
export * from "./category";
|
||||||
export * from "./client";
|
|
||||||
export * from "./directory";
|
export * from "./directory";
|
||||||
export * from "./file";
|
export * from "./file";
|
||||||
export * from "./hsk";
|
export * from "./hsk";
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
import { error } from "@sveltejs/kit";
|
|
||||||
import {
|
|
||||||
createClient,
|
|
||||||
getClient,
|
|
||||||
getClientByPubKeys,
|
|
||||||
createUserClient,
|
|
||||||
getAllUserClients,
|
|
||||||
getUserClient,
|
|
||||||
setUserClientStateToPending,
|
|
||||||
registerUserClientChallenge,
|
|
||||||
consumeUserClientChallenge,
|
|
||||||
} from "$lib/server/db/client";
|
|
||||||
import { IntegrityError } from "$lib/server/db/error";
|
|
||||||
import { verifyPubKey, verifySignature, generateChallenge } from "$lib/server/modules/crypto";
|
|
||||||
import { isInitialMekNeeded } from "$lib/server/modules/mek";
|
|
||||||
import env from "$lib/server/loadenv";
|
|
||||||
|
|
||||||
export const getUserClientList = async (userId: number) => {
|
|
||||||
const userClients = await getAllUserClients(userId);
|
|
||||||
return {
|
|
||||||
userClients: userClients.map(({ clientId, state }) => ({
|
|
||||||
id: clientId,
|
|
||||||
state: state as "pending" | "active",
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const expiresAt = () => new Date(Date.now() + env.challenge.userClientExp);
|
|
||||||
|
|
||||||
const createUserClientChallenge = async (
|
|
||||||
ip: string,
|
|
||||||
userId: number,
|
|
||||||
clientId: number,
|
|
||||||
encPubKey: string,
|
|
||||||
) => {
|
|
||||||
const { answer, challenge } = await generateChallenge(32, encPubKey);
|
|
||||||
const { id } = await registerUserClientChallenge(
|
|
||||||
userId,
|
|
||||||
clientId,
|
|
||||||
answer.toString("base64"),
|
|
||||||
ip,
|
|
||||||
expiresAt(),
|
|
||||||
);
|
|
||||||
return { id, challenge: challenge.toString("base64") };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const registerUserClient = async (
|
|
||||||
userId: number,
|
|
||||||
ip: string,
|
|
||||||
encPubKey: string,
|
|
||||||
sigPubKey: string,
|
|
||||||
) => {
|
|
||||||
const client = await getClientByPubKeys(encPubKey, sigPubKey);
|
|
||||||
if (client) {
|
|
||||||
try {
|
|
||||||
await createUserClient(userId, client.id);
|
|
||||||
return await createUserClientChallenge(ip, userId, client.id, encPubKey);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof IntegrityError && e.message === "User client already exists") {
|
|
||||||
error(409, "Client already registered");
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (encPubKey === sigPubKey) {
|
|
||||||
error(400, "Same public keys");
|
|
||||||
} else if (!verifyPubKey(encPubKey) || !verifyPubKey(sigPubKey)) {
|
|
||||||
error(400, "Invalid public key(s)");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { id: clientId } = await createClient(encPubKey, sigPubKey, userId);
|
|
||||||
return await createUserClientChallenge(ip, userId, clientId, encPubKey);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof IntegrityError && e.message === "Public key(s) already registered") {
|
|
||||||
error(409, "Public key(s) already used");
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const verifyUserClient = async (
|
|
||||||
userId: number,
|
|
||||||
ip: string,
|
|
||||||
challengeId: number,
|
|
||||||
answerSig: string,
|
|
||||||
) => {
|
|
||||||
const challenge = await consumeUserClientChallenge(challengeId, userId, ip);
|
|
||||||
if (!challenge) {
|
|
||||||
error(403, "Invalid challenge answer");
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = await getClient(challenge.clientId);
|
|
||||||
if (!client) {
|
|
||||||
error(500, "Invalid challenge answer");
|
|
||||||
} else if (
|
|
||||||
!verifySignature(Buffer.from(challenge.answer, "base64"), answerSig, client.sigPubKey)
|
|
||||||
) {
|
|
||||||
error(403, "Invalid challenge answer signature");
|
|
||||||
}
|
|
||||||
|
|
||||||
await setUserClientStateToPending(userId, client.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getUserClientStatus = async (userId: number, clientId: number) => {
|
|
||||||
const userClient = await getUserClient(userId, clientId);
|
|
||||||
if (!userClient) {
|
|
||||||
error(500, "Invalid session id");
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
state: userClient.state as "pending" | "active",
|
|
||||||
isInitialMekNeeded: await isInitialMekNeeded(userId),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -10,15 +10,13 @@ import {
|
|||||||
verifyMasterKeyWrapped,
|
verifyMasterKeyWrapped,
|
||||||
} from "$lib/modules/crypto";
|
} from "$lib/modules/crypto";
|
||||||
import type {
|
import type {
|
||||||
ClientRegisterRequest,
|
|
||||||
ClientRegisterResponse,
|
|
||||||
ClientRegisterVerifyRequest,
|
|
||||||
InitialHmacSecretRegisterRequest,
|
InitialHmacSecretRegisterRequest,
|
||||||
MasterKeyListResponse,
|
MasterKeyListResponse,
|
||||||
InitialMasterKeyRegisterRequest,
|
InitialMasterKeyRegisterRequest,
|
||||||
} from "$lib/server/schemas";
|
} from "$lib/server/schemas";
|
||||||
import { requestSessionUpgrade } from "$lib/services/auth";
|
import { requestSessionUpgrade } from "$lib/services/auth";
|
||||||
import { masterKeyStore, type ClientKeys } from "$lib/stores";
|
import { masterKeyStore, type ClientKeys } from "$lib/stores";
|
||||||
|
import { useTRPC } from "$trpc/client";
|
||||||
|
|
||||||
export const requestClientRegistration = async (
|
export const requestClientRegistration = async (
|
||||||
encryptKeyBase64: string,
|
encryptKeyBase64: string,
|
||||||
@@ -26,21 +24,24 @@ export const requestClientRegistration = async (
|
|||||||
verifyKeyBase64: string,
|
verifyKeyBase64: string,
|
||||||
signKey: CryptoKey,
|
signKey: CryptoKey,
|
||||||
) => {
|
) => {
|
||||||
let res = await callPostApi<ClientRegisterRequest>("/api/client/register", {
|
const trpc = useTRPC();
|
||||||
encPubKey: encryptKeyBase64,
|
|
||||||
sigPubKey: verifyKeyBase64,
|
|
||||||
});
|
|
||||||
if (!res.ok) return false;
|
|
||||||
|
|
||||||
const { id, challenge }: ClientRegisterResponse = await res.json();
|
try {
|
||||||
const answer = await decryptChallenge(challenge, decryptKey);
|
const { id, challenge } = await trpc.client.register.mutate({
|
||||||
const answerSig = await signMessageRSA(answer, signKey);
|
encPubKey: encryptKeyBase64,
|
||||||
|
sigPubKey: verifyKeyBase64,
|
||||||
res = await callPostApi<ClientRegisterVerifyRequest>("/api/client/register/verify", {
|
});
|
||||||
id,
|
const answer = await decryptChallenge(challenge, decryptKey);
|
||||||
answerSig: encodeToBase64(answerSig),
|
const answerSig = await signMessageRSA(answer, signKey);
|
||||||
});
|
await trpc.client.verify.mutate({
|
||||||
return res.ok;
|
id,
|
||||||
|
answerSig: encodeToBase64(answerSig),
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
// TODO: Error Handling
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const requestClientRegistrationAndSessionUpgrade = async (
|
export const requestClientRegistrationAndSessionUpgrade = async (
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import { json } from "@sveltejs/kit";
|
|
||||||
import { authorize } from "$lib/server/modules/auth";
|
|
||||||
import { clientListResponse, type ClientListResponse } from "$lib/server/schemas";
|
|
||||||
import { getUserClientList } from "$lib/server/services/client";
|
|
||||||
import type { RequestHandler } from "./$types";
|
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ locals }) => {
|
|
||||||
const { userId } = await authorize(locals, "anyClient");
|
|
||||||
const { userClients } = await getUserClientList(userId);
|
|
||||||
return json(clientListResponse.parse({ clients: userClients } satisfies ClientListResponse));
|
|
||||||
};
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { error, json } from "@sveltejs/kit";
|
|
||||||
import { authorize } from "$lib/server/modules/auth";
|
|
||||||
import {
|
|
||||||
clientRegisterRequest,
|
|
||||||
clientRegisterResponse,
|
|
||||||
type ClientRegisterResponse,
|
|
||||||
} from "$lib/server/schemas";
|
|
||||||
import { registerUserClient } from "$lib/server/services/client";
|
|
||||||
import type { RequestHandler } from "./$types";
|
|
||||||
|
|
||||||
export const POST: RequestHandler = async ({ locals, request }) => {
|
|
||||||
const { userId } = await authorize(locals, "notClient");
|
|
||||||
|
|
||||||
const zodRes = clientRegisterRequest.safeParse(await request.json());
|
|
||||||
if (!zodRes.success) error(400, "Invalid request body");
|
|
||||||
const { encPubKey, sigPubKey } = zodRes.data;
|
|
||||||
|
|
||||||
const { id, challenge } = await registerUserClient(userId, locals.ip, encPubKey, sigPubKey);
|
|
||||||
return json(clientRegisterResponse.parse({ id, challenge } satisfies ClientRegisterResponse));
|
|
||||||
};
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { error, text } from "@sveltejs/kit";
|
|
||||||
import { authorize } from "$lib/server/modules/auth";
|
|
||||||
import { clientRegisterVerifyRequest } from "$lib/server/schemas";
|
|
||||||
import { verifyUserClient } from "$lib/server/services/client";
|
|
||||||
import type { RequestHandler } from "./$types";
|
|
||||||
|
|
||||||
export const POST: RequestHandler = async ({ locals, request }) => {
|
|
||||||
const { userId } = await authorize(locals, "notClient");
|
|
||||||
|
|
||||||
const zodRes = clientRegisterVerifyRequest.safeParse(await request.json());
|
|
||||||
if (!zodRes.success) error(400, "Invalid request body");
|
|
||||||
const { id, answerSig } = zodRes.data;
|
|
||||||
|
|
||||||
await verifyUserClient(userId, locals.ip, id, answerSig);
|
|
||||||
return text("Client verified", { headers: { "Content-Type": "text/plain" } });
|
|
||||||
};
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { json } from "@sveltejs/kit";
|
|
||||||
import { authorize } from "$lib/server/modules/auth";
|
|
||||||
import { clientStatusResponse, type ClientStatusResponse } from "$lib/server/schemas";
|
|
||||||
import { getUserClientStatus } from "$lib/server/services/client";
|
|
||||||
import type { RequestHandler } from "./$types";
|
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ locals }) => {
|
|
||||||
const { userId, clientId } = await authorize(locals, "anyClient");
|
|
||||||
const { state, isInitialMekNeeded } = await getUserClientStatus(userId, clientId);
|
|
||||||
return json(
|
|
||||||
clientStatusResponse.parse({
|
|
||||||
id: clientId,
|
|
||||||
state,
|
|
||||||
isInitialMekNeeded,
|
|
||||||
} satisfies ClientStatusResponse),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -14,7 +14,7 @@ const createClient = (fetch: typeof globalThis.fetch) =>
|
|||||||
|
|
||||||
let browserClient: ReturnType<typeof createClient>;
|
let browserClient: ReturnType<typeof createClient>;
|
||||||
|
|
||||||
export const trpc = (fetch = globalThis.fetch) => {
|
export const useTRPC = (fetch = globalThis.fetch) => {
|
||||||
const client = browserClient ?? createClient(fetch);
|
const client = browserClient ?? createClient(fetch);
|
||||||
if (browser) {
|
if (browser) {
|
||||||
browserClient ??= client;
|
browserClient ??= client;
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import type { RequestEvent } from "@sveltejs/kit";
|
import type { RequestEvent } from "@sveltejs/kit";
|
||||||
import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
|
import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
|
||||||
import { createContext, router } from "./init.server";
|
import { createContext, router } from "./init.server";
|
||||||
|
import { clientRouter } from "./routers";
|
||||||
|
|
||||||
export const appRouter = router({
|
export const appRouter = router({
|
||||||
// TODO
|
client: clientRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const createCaller = (event: RequestEvent) => appRouter.createCaller(createContext(event));
|
export const createCaller = (event: RequestEvent) => appRouter.createCaller(createContext(event));
|
||||||
|
|||||||
96
src/trpc/routers/client.ts
Normal file
96
src/trpc/routers/client.ts
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import { TRPCError } from "@trpc/server";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { ClientRepo, IntegrityError } from "$lib/server/db";
|
||||||
|
import { verifyPubKey, verifySignature, generateChallenge } from "$lib/server/modules/crypto";
|
||||||
|
import env from "$lib/server/loadenv";
|
||||||
|
import { router, roleProcedure } from "../init.server";
|
||||||
|
|
||||||
|
const createUserClientChallenge = async (
|
||||||
|
ip: string,
|
||||||
|
userId: number,
|
||||||
|
clientId: number,
|
||||||
|
encPubKey: string,
|
||||||
|
) => {
|
||||||
|
const { answer, challenge } = await generateChallenge(32, encPubKey);
|
||||||
|
const { id } = await ClientRepo.registerUserClientChallenge(
|
||||||
|
userId,
|
||||||
|
clientId,
|
||||||
|
answer.toString("base64"),
|
||||||
|
ip,
|
||||||
|
new Date(Date.now() + env.challenge.userClientExp),
|
||||||
|
);
|
||||||
|
return { id, challenge: challenge.toString("base64") };
|
||||||
|
};
|
||||||
|
|
||||||
|
const clientRouter = router({
|
||||||
|
register: roleProcedure["notClient"]
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
encPubKey: z.string().base64().nonempty(),
|
||||||
|
sigPubKey: z.string().base64().nonempty(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const { userId } = ctx.session;
|
||||||
|
const { encPubKey, sigPubKey } = input;
|
||||||
|
const client = await ClientRepo.getClientByPubKeys(encPubKey, sigPubKey);
|
||||||
|
if (client) {
|
||||||
|
try {
|
||||||
|
await ClientRepo.createUserClient(userId, client.id);
|
||||||
|
return await createUserClientChallenge(ctx.locals.ip, userId, client.id, encPubKey);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof IntegrityError && e.message === "User client already exists") {
|
||||||
|
throw new TRPCError({ code: "CONFLICT", message: "Client already registered" });
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (encPubKey === sigPubKey) {
|
||||||
|
throw new TRPCError({ code: "BAD_REQUEST", message: "Same public keys" });
|
||||||
|
} else if (!verifyPubKey(encPubKey) || !verifyPubKey(sigPubKey)) {
|
||||||
|
throw new TRPCError({ code: "BAD_REQUEST", message: "Invalid public key(s)" });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { id: clientId } = await ClientRepo.createClient(encPubKey, sigPubKey, userId);
|
||||||
|
return await createUserClientChallenge(ctx.locals.ip, userId, clientId, encPubKey);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof IntegrityError && e.message === "Public key(s) already registered") {
|
||||||
|
throw new TRPCError({ code: "CONFLICT", message: "Public key(s) already used" });
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
verify: roleProcedure["notClient"]
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
id: z.number().int().positive(),
|
||||||
|
answerSig: z.string().base64().nonempty(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const challenge = await ClientRepo.consumeUserClientChallenge(
|
||||||
|
input.id,
|
||||||
|
ctx.session.userId,
|
||||||
|
ctx.locals.ip,
|
||||||
|
);
|
||||||
|
if (!challenge) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "Invalid challenge answer" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = await ClientRepo.getClient(challenge.clientId);
|
||||||
|
if (!client) {
|
||||||
|
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Invalid challenge answer" });
|
||||||
|
} else if (
|
||||||
|
!verifySignature(Buffer.from(challenge.answer, "base64"), input.answerSig, client.sigPubKey)
|
||||||
|
) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "Invalid challenge answer signature" });
|
||||||
|
}
|
||||||
|
|
||||||
|
await ClientRepo.setUserClientStateToPending(ctx.session.userId, client.id);
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clientRouter;
|
||||||
1
src/trpc/routers/index.ts
Normal file
1
src/trpc/routers/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default as clientRouter } from "./client";
|
||||||
Reference in New Issue
Block a user