From 45e214d49f86a77426e19f562c955119e117664d Mon Sep 17 00:00:00 2001 From: static Date: Thu, 26 Dec 2024 17:04:52 +0900 Subject: [PATCH] =?UTF-8?q?=ED=86=A0=ED=81=B0=EC=97=90=20=ED=81=B4?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EC=96=B8=ED=8A=B8=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=EB=A5=BC=20=ED=95=A8=EA=BB=98=20=EC=A0=80=EC=9E=A5=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/server/auth.ts | 35 ++++++++++++++----- src/lib/server/db/client.ts | 17 +++++++++ .../server/db/schema/{device.ts => client.ts} | 18 +++++----- src/lib/server/db/schema/index.ts | 2 +- src/routes/api/auth/login/+server.ts | 8 ++--- 5 files changed, 57 insertions(+), 23 deletions(-) create mode 100644 src/lib/server/db/client.ts rename src/lib/server/db/schema/{device.ts => client.ts} (51%) diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index 85efdff..87b3bf7 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -1,27 +1,44 @@ import argon2 from "argon2"; import jwt from "jsonwebtoken"; +import { getClientByPubKey } from "$lib/server/db/client"; import { getUserByEmail } from "$lib/server/db/user"; import env from "$lib/server/loadenv"; +interface TokenData { + type: "access" | "refresh"; + userId: number; + clientId?: number; +} + const verifyPassword = async (hash: string, password: string) => { return await argon2.verify(hash, password); }; -const issueToken = (id: number, type: "access" | "refresh") => { - return jwt.sign({ id, type }, env.jwt.secret, { - expiresIn: type === "access" ? env.jwt.accessExp : env.jwt.refreshExp, - }); +const issueToken = (type: "access" | "refresh", userId: number, clientId?: number) => { + return jwt.sign( + { + type, + userId, + clientId, + } satisfies TokenData, + env.jwt.secret, + { + expiresIn: type === "access" ? env.jwt.accessExp : env.jwt.refreshExp, + }, + ); }; -export const login = async (email: string, password: string) => { +export const login = async (email: string, password: string, pubKey?: string) => { const user = await getUserByEmail(email); if (!user) return null; - const valid = await verifyPassword(user.password, password); - if (!valid) return null; + const isValid = await verifyPassword(user.password, password); + if (!isValid) return null; + + const client = pubKey ? await getClientByPubKey(pubKey) : null; return { - accessToken: issueToken(user.id, "access"), - refreshToken: issueToken(user.id, "refresh"), + accessToken: issueToken("access", user.id, client?.id), + refreshToken: issueToken("refresh", user.id, client?.id), }; }; diff --git a/src/lib/server/db/client.ts b/src/lib/server/db/client.ts new file mode 100644 index 0000000..a4e2cdb --- /dev/null +++ b/src/lib/server/db/client.ts @@ -0,0 +1,17 @@ +import { and, eq } from "drizzle-orm"; +import db from "./drizzle"; +import { client, userClient } from "./schema"; + +export const getClientByPubKey = async (pubKey: string) => { + const clients = await db.select().from(client).where(eq(client.pubKey, pubKey)).execute(); + return clients[0] ?? null; +}; + +export const getUserClient = async (userId: number, clientId: number) => { + const userClients = await db + .select() + .from(userClient) + .where(and(eq(userClient.userId, userId), eq(userClient.clientId, clientId))) + .execute(); + return userClients[0] ?? null; +}; diff --git a/src/lib/server/db/schema/device.ts b/src/lib/server/db/schema/client.ts similarity index 51% rename from src/lib/server/db/schema/device.ts rename to src/lib/server/db/schema/client.ts index 44526c6..ad5d678 100644 --- a/src/lib/server/db/schema/device.ts +++ b/src/lib/server/db/schema/client.ts @@ -1,29 +1,29 @@ import { sqliteTable, text, integer, primaryKey } from "drizzle-orm/sqlite-core"; import { user } from "./user"; -export enum UserDeviceState { +export enum UserClientState { PENDING = 0, ACTIVE = 1, } -export const device = sqliteTable("device", { +export const client = sqliteTable("client", { id: integer("id").primaryKey(), - pubKey: text("pub_key").notNull().unique(), + pubKey: text("public_key").notNull().unique(), }); -export const userDevice = sqliteTable( - "user_device", +export const userClient = sqliteTable( + "user_client", { userId: integer("user_id") .notNull() .references(() => user.id), - deviceId: integer("device_id") + clientId: integer("client_id") .notNull() - .references(() => device.id), + .references(() => client.id), state: integer("state").notNull().default(0), - encKey: text("enc_key"), + encKey: text("encrypted_key"), }, (t) => ({ - pk: primaryKey({ columns: [t.userId, t.deviceId] }), + pk: primaryKey({ columns: [t.userId, t.clientId] }), }), ); diff --git a/src/lib/server/db/schema/index.ts b/src/lib/server/db/schema/index.ts index bcd10e8..6674d19 100644 --- a/src/lib/server/db/schema/index.ts +++ b/src/lib/server/db/schema/index.ts @@ -1,2 +1,2 @@ -export * from "./device"; +export * from "./client"; export * from "./user"; diff --git a/src/routes/api/auth/login/+server.ts b/src/routes/api/auth/login/+server.ts index 09bcdcf..2ec24c3 100644 --- a/src/routes/api/auth/login/+server.ts +++ b/src/routes/api/auth/login/+server.ts @@ -8,15 +8,15 @@ export const POST: RequestHandler = async ({ request }) => { .object({ email: z.string().email().nonempty(), password: z.string().nonempty(), + pubKey: z.string().nonempty().optional(), }) .safeParse(await request.json()); if (!zodRes.success) error(400, zodRes.error.message); - const { email, password } = zodRes.data; - const loginRes = await login(email.trim(), password.trim()); + const { email, password, pubKey } = zodRes.data; + const loginRes = await login(email.trim(), password.trim(), pubKey?.trim()); + if (!loginRes) error(401, "Invalid email, password, or public key"); - if (!loginRes) error(401, "Invalid email or password"); const { accessToken, refreshToken } = loginRes; - return json({ accessToken, refreshToken }); };