mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-14 22:08:45 +00:00
토큰에 클라이언트 정보를 함께 저장하도록 변경
This commit is contained in:
@@ -1,27 +1,44 @@
|
|||||||
import argon2 from "argon2";
|
import argon2 from "argon2";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
|
import { getClientByPubKey } from "$lib/server/db/client";
|
||||||
import { getUserByEmail } from "$lib/server/db/user";
|
import { getUserByEmail } from "$lib/server/db/user";
|
||||||
import env from "$lib/server/loadenv";
|
import env from "$lib/server/loadenv";
|
||||||
|
|
||||||
|
interface TokenData {
|
||||||
|
type: "access" | "refresh";
|
||||||
|
userId: number;
|
||||||
|
clientId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
const verifyPassword = async (hash: string, password: string) => {
|
const verifyPassword = async (hash: string, password: string) => {
|
||||||
return await argon2.verify(hash, password);
|
return await argon2.verify(hash, password);
|
||||||
};
|
};
|
||||||
|
|
||||||
const issueToken = (id: number, type: "access" | "refresh") => {
|
const issueToken = (type: "access" | "refresh", userId: number, clientId?: number) => {
|
||||||
return jwt.sign({ id, type }, env.jwt.secret, {
|
return jwt.sign(
|
||||||
expiresIn: type === "access" ? env.jwt.accessExp : env.jwt.refreshExp,
|
{
|
||||||
});
|
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);
|
const user = await getUserByEmail(email);
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
|
|
||||||
const valid = await verifyPassword(user.password, password);
|
const isValid = await verifyPassword(user.password, password);
|
||||||
if (!valid) return null;
|
if (!isValid) return null;
|
||||||
|
|
||||||
|
const client = pubKey ? await getClientByPubKey(pubKey) : null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accessToken: issueToken(user.id, "access"),
|
accessToken: issueToken("access", user.id, client?.id),
|
||||||
refreshToken: issueToken(user.id, "refresh"),
|
refreshToken: issueToken("refresh", user.id, client?.id),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
17
src/lib/server/db/client.ts
Normal file
17
src/lib/server/db/client.ts
Normal file
@@ -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;
|
||||||
|
};
|
||||||
@@ -1,29 +1,29 @@
|
|||||||
import { sqliteTable, text, integer, primaryKey } from "drizzle-orm/sqlite-core";
|
import { sqliteTable, text, integer, primaryKey } from "drizzle-orm/sqlite-core";
|
||||||
import { user } from "./user";
|
import { user } from "./user";
|
||||||
|
|
||||||
export enum UserDeviceState {
|
export enum UserClientState {
|
||||||
PENDING = 0,
|
PENDING = 0,
|
||||||
ACTIVE = 1,
|
ACTIVE = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const device = sqliteTable("device", {
|
export const client = sqliteTable("client", {
|
||||||
id: integer("id").primaryKey(),
|
id: integer("id").primaryKey(),
|
||||||
pubKey: text("pub_key").notNull().unique(),
|
pubKey: text("public_key").notNull().unique(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userDevice = sqliteTable(
|
export const userClient = sqliteTable(
|
||||||
"user_device",
|
"user_client",
|
||||||
{
|
{
|
||||||
userId: integer("user_id")
|
userId: integer("user_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => user.id),
|
.references(() => user.id),
|
||||||
deviceId: integer("device_id")
|
clientId: integer("client_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => device.id),
|
.references(() => client.id),
|
||||||
state: integer("state").notNull().default(0),
|
state: integer("state").notNull().default(0),
|
||||||
encKey: text("enc_key"),
|
encKey: text("encrypted_key"),
|
||||||
},
|
},
|
||||||
(t) => ({
|
(t) => ({
|
||||||
pk: primaryKey({ columns: [t.userId, t.deviceId] }),
|
pk: primaryKey({ columns: [t.userId, t.clientId] }),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from "./device";
|
export * from "./client";
|
||||||
export * from "./user";
|
export * from "./user";
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ export const POST: RequestHandler = async ({ request }) => {
|
|||||||
.object({
|
.object({
|
||||||
email: z.string().email().nonempty(),
|
email: z.string().email().nonempty(),
|
||||||
password: z.string().nonempty(),
|
password: z.string().nonempty(),
|
||||||
|
pubKey: z.string().nonempty().optional(),
|
||||||
})
|
})
|
||||||
.safeParse(await request.json());
|
.safeParse(await request.json());
|
||||||
if (!zodRes.success) error(400, zodRes.error.message);
|
if (!zodRes.success) error(400, zodRes.error.message);
|
||||||
|
|
||||||
const { email, password } = zodRes.data;
|
const { email, password, pubKey } = zodRes.data;
|
||||||
const loginRes = await login(email.trim(), password.trim());
|
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;
|
const { accessToken, refreshToken } = loginRes;
|
||||||
|
|
||||||
return json({ accessToken, refreshToken });
|
return json({ accessToken, refreshToken });
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user