mirror of
https://github.com/kmc7468/arkvault.git
synced 2026-02-04 08:06:56 +00:00
사소한 리팩토링
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import { error, redirect, type Handle } from "@sveltejs/kit";
|
import { error, redirect, type Handle } from "@sveltejs/kit";
|
||||||
import env from "$lib/server/loadenv";
|
import { cookieOptions, authenticate, AuthenticationError } from "$lib/server/modules/auth";
|
||||||
import { authenticate, AuthenticationError } from "$lib/server/modules/auth";
|
|
||||||
|
|
||||||
export const authenticateMiddleware: Handle = async ({ event, resolve }) => {
|
export const authenticateMiddleware: Handle = async ({ event, resolve }) => {
|
||||||
try {
|
try {
|
||||||
@@ -11,12 +10,7 @@ export const authenticateMiddleware: Handle = async ({ event, resolve }) => {
|
|||||||
|
|
||||||
const { ip, userAgent } = event.locals;
|
const { ip, userAgent } = event.locals;
|
||||||
event.locals.session = await authenticate(sessionIdSigned, ip, userAgent);
|
event.locals.session = await authenticate(sessionIdSigned, ip, userAgent);
|
||||||
event.cookies.set("sessionId", sessionIdSigned, {
|
event.cookies.set("sessionId", sessionIdSigned, cookieOptions);
|
||||||
path: "/",
|
|
||||||
maxAge: env.session.exp / 1000,
|
|
||||||
secure: true,
|
|
||||||
sameSite: "strict",
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof AuthenticationError) {
|
if (e instanceof AuthenticationError) {
|
||||||
const { pathname, search } = event.url;
|
const { pathname, search } = event.url;
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { error } from "@sveltejs/kit";
|
import { error } from "@sveltejs/kit";
|
||||||
import { getUserClient } from "$lib/server/db/client";
|
import { ClientRepo, SessionRepo, IntegrityError } from "$lib/server/db";
|
||||||
import { IntegrityError } from "$lib/server/db/error";
|
|
||||||
import { createSession, refreshSession } from "$lib/server/db/session";
|
|
||||||
import env from "$lib/server/loadenv";
|
import env from "$lib/server/loadenv";
|
||||||
import { issueSessionId, verifySessionId } from "$lib/server/modules/crypto";
|
import { verifySessionId } from "$lib/server/modules/crypto";
|
||||||
|
|
||||||
interface Session {
|
export interface Session {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
userId: number;
|
userId: number;
|
||||||
clientId?: number;
|
clientId?: number;
|
||||||
@@ -42,11 +40,12 @@ export class AuthorizationError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const startSession = async (userId: number, ip: string, userAgent: string) => {
|
export const cookieOptions = {
|
||||||
const { sessionId, sessionIdSigned } = await issueSessionId(32, env.session.secret);
|
path: "/",
|
||||||
await createSession(userId, sessionId, ip, userAgent);
|
maxAge: env.session.exp / 1000,
|
||||||
return sessionIdSigned;
|
secure: true,
|
||||||
};
|
sameSite: "strict",
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const authenticate = async (sessionIdSigned: string, ip: string, userAgent: string) => {
|
export const authenticate = async (sessionIdSigned: string, ip: string, userAgent: string) => {
|
||||||
const sessionId = verifySessionId(sessionIdSigned, env.session.secret);
|
const sessionId = verifySessionId(sessionIdSigned, env.session.secret);
|
||||||
@@ -55,7 +54,7 @@ export const authenticate = async (sessionIdSigned: string, ip: string, userAgen
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { userId, clientId } = await refreshSession(sessionId, ip, userAgent);
|
const { userId, clientId } = await SessionRepo.refreshSession(sessionId, ip, userAgent);
|
||||||
return {
|
return {
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
userId,
|
userId,
|
||||||
@@ -96,7 +95,7 @@ export const authorizeInternal = async (
|
|||||||
if (!clientId) {
|
if (!clientId) {
|
||||||
throw new AuthorizationError(403, "Forbidden");
|
throw new AuthorizationError(403, "Forbidden");
|
||||||
}
|
}
|
||||||
const userClient = await getUserClient(userId, clientId);
|
const userClient = await ClientRepo.getUserClient(userId, clientId);
|
||||||
if (!userClient) {
|
if (!userClient) {
|
||||||
throw new AuthorizationError(500, "Invalid session id");
|
throw new AuthorizationError(500, "Invalid session id");
|
||||||
} else if (userClient.state !== "pending") {
|
} else if (userClient.state !== "pending") {
|
||||||
@@ -108,7 +107,7 @@ export const authorizeInternal = async (
|
|||||||
if (!clientId) {
|
if (!clientId) {
|
||||||
throw new AuthorizationError(403, "Forbidden");
|
throw new AuthorizationError(403, "Forbidden");
|
||||||
}
|
}
|
||||||
const userClient = await getUserClient(userId, clientId);
|
const userClient = await ClientRepo.getUserClient(userId, clientId);
|
||||||
if (!userClient) {
|
if (!userClient) {
|
||||||
throw new AuthorizationError(500, "Invalid session id");
|
throw new AuthorizationError(500, "Invalid session id");
|
||||||
} else if (userClient.state !== "active") {
|
} else if (userClient.state !== "active") {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const requestSessionUpgrade = async (
|
|||||||
const trpc = useTRPC();
|
const trpc = useTRPC();
|
||||||
let id, challenge;
|
let id, challenge;
|
||||||
try {
|
try {
|
||||||
({ id, challenge } = await trpc.auth.upgradeSession.mutate({
|
({ id, challenge } = await trpc.auth.upgrade.mutate({
|
||||||
encPubKey: encryptKeyBase64,
|
encPubKey: encryptKeyBase64,
|
||||||
sigPubKey: verifyKeyBase64,
|
sigPubKey: verifyKeyBase64,
|
||||||
}));
|
}));
|
||||||
@@ -26,7 +26,7 @@ export const requestSessionUpgrade = async (
|
|||||||
const answerSig = await signMessageRSA(answer, signKey);
|
const answerSig = await signMessageRSA(answer, signKey);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await trpc.auth.verifySessionUpgrade.mutate({
|
await trpc.auth.verifyUpgrade.mutate({
|
||||||
id,
|
id,
|
||||||
answerSig: encodeToBase64(answerSig),
|
answerSig: encodeToBase64(answerSig),
|
||||||
force,
|
force,
|
||||||
|
|||||||
@@ -3,51 +3,39 @@ import argon2 from "argon2";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { ClientRepo, SessionRepo, UserRepo, IntegrityError } from "$lib/server/db";
|
import { ClientRepo, SessionRepo, UserRepo, IntegrityError } from "$lib/server/db";
|
||||||
import env from "$lib/server/loadenv";
|
import env from "$lib/server/loadenv";
|
||||||
import { startSession } from "$lib/server/modules/auth";
|
import { cookieOptions } from "$lib/server/modules/auth";
|
||||||
import { generateChallenge, verifySignature } from "$lib/server/modules/crypto";
|
import { generateChallenge, verifySignature, issueSessionId } from "$lib/server/modules/crypto";
|
||||||
import { publicProcedure, roleProcedure, router } from "../init.server";
|
import { router, publicProcedure, roleProcedure } from "../init.server";
|
||||||
|
|
||||||
const hashPassword = async (password: string) => {
|
|
||||||
return await argon2.hash(password);
|
|
||||||
};
|
|
||||||
|
|
||||||
const verifyPassword = async (hash: string, password: string) => {
|
|
||||||
return await argon2.verify(hash, password);
|
|
||||||
};
|
|
||||||
|
|
||||||
const authRouter = router({
|
const authRouter = router({
|
||||||
login: publicProcedure
|
login: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
email: z.email(),
|
email: z.email(),
|
||||||
password: z.string().trim().nonempty(),
|
password: z.string().nonempty(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const user = await UserRepo.getUserByEmail(input.email);
|
const user = await UserRepo.getUserByEmail(input.email);
|
||||||
if (!user || !(await verifyPassword(user.password, input.password))) {
|
if (!user || !(await argon2.verify(user.password, input.password))) {
|
||||||
throw new TRPCError({ code: "UNAUTHORIZED", message: "Invalid email or password" });
|
throw new TRPCError({ code: "UNAUTHORIZED", message: "Invalid email or password" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionIdSigned = await startSession(user.id, ctx.locals.ip, ctx.locals.userAgent);
|
const { sessionId, sessionIdSigned } = await issueSessionId(32, env.session.secret);
|
||||||
ctx.cookies.set("sessionId", sessionIdSigned, {
|
await SessionRepo.createSession(user.id, sessionId, ctx.locals.ip, ctx.locals.userAgent);
|
||||||
path: "/",
|
ctx.cookies.set("sessionId", sessionIdSigned, cookieOptions);
|
||||||
maxAge: env.session.exp / 1000,
|
|
||||||
secure: true,
|
|
||||||
sameSite: "strict",
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
logout: roleProcedure["any"].mutation(async ({ ctx }) => {
|
logout: roleProcedure["any"].mutation(async ({ ctx }) => {
|
||||||
await SessionRepo.deleteSession(ctx.session.sessionId);
|
await SessionRepo.deleteSession(ctx.session.sessionId);
|
||||||
ctx.cookies.delete("sessionId", { path: "/" });
|
ctx.cookies.delete("sessionId", cookieOptions);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
changePassword: roleProcedure["any"]
|
changePassword: roleProcedure["any"]
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
oldPassword: z.string().trim().nonempty(),
|
oldPassword: z.string().nonempty(),
|
||||||
newPassword: z.string().trim().nonempty(),
|
newPassword: z.string().nonempty(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
@@ -60,15 +48,15 @@ const authRouter = router({
|
|||||||
const user = await UserRepo.getUser(ctx.session.userId);
|
const user = await UserRepo.getUser(ctx.session.userId);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Invalid session id" });
|
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Invalid session id" });
|
||||||
} else if (!(await verifyPassword(user.password, input.oldPassword))) {
|
} else if (!(await argon2.verify(user.password, input.oldPassword))) {
|
||||||
throw new TRPCError({ code: "FORBIDDEN", message: "Invalid password" });
|
throw new TRPCError({ code: "FORBIDDEN", message: "Invalid password" });
|
||||||
}
|
}
|
||||||
|
|
||||||
await UserRepo.setUserPassword(ctx.session.userId, await hashPassword(input.newPassword));
|
await UserRepo.setUserPassword(ctx.session.userId, await argon2.hash(input.newPassword));
|
||||||
await SessionRepo.deleteAllOtherSessions(ctx.session.userId, ctx.session.sessionId);
|
await SessionRepo.deleteAllOtherSessions(ctx.session.userId, ctx.session.sessionId);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
upgradeSession: roleProcedure["notClient"]
|
upgrade: roleProcedure["notClient"]
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
encPubKey: z.base64().nonempty(),
|
encPubKey: z.base64().nonempty(),
|
||||||
@@ -94,10 +82,11 @@ const authRouter = router({
|
|||||||
ctx.locals.ip,
|
ctx.locals.ip,
|
||||||
new Date(Date.now() + env.challenge.sessionUpgradeExp),
|
new Date(Date.now() + env.challenge.sessionUpgradeExp),
|
||||||
);
|
);
|
||||||
|
|
||||||
return { id, challenge: challenge.toString("base64") };
|
return { id, challenge: challenge.toString("base64") };
|
||||||
}),
|
}),
|
||||||
|
|
||||||
verifySessionUpgrade: roleProcedure["notClient"]
|
verifyUpgrade: roleProcedure["notClient"]
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
id: z.int().positive(),
|
id: z.int().positive(),
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const createUserClientChallenge = async (
|
|||||||
ip,
|
ip,
|
||||||
new Date(Date.now() + env.challenge.userClientExp),
|
new Date(Date.now() + env.challenge.userClientExp),
|
||||||
);
|
);
|
||||||
|
|
||||||
return { id, challenge: challenge.toString("base64") };
|
return { id, challenge: challenge.toString("base64") };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user