remove lucia

This commit is contained in:
Milo Schwartz
2024-10-13 17:13:47 -04:00
parent f14fb90ab6
commit 99d6cababa
39 changed files with 234 additions and 167 deletions

View File

@@ -45,14 +45,14 @@ export async function verifyBackUpCode(
parallelism: 1,
});
if (validCode) {
validId = hashedCode.id;
validId = hashedCode.codeId;
}
}
if (validId) {
await db
.delete(twoFactorBackupCodes)
.where(eq(twoFactorBackupCodes.id, validId));
.where(eq(twoFactorBackupCodes.codeId, validId));
}
return validId ? true : false;

View File

@@ -55,8 +55,7 @@ export enum ActionsEnum {
}
export async function checkUserActionPermission(actionId: string, req: Request): Promise<boolean> {
const userId = req.user?.id;
const userId = req.user?.userId;
if (!userId) {
throw createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated');
}
@@ -116,4 +115,4 @@ export async function checkUserActionPermission(actionId: string, req: Request):
console.error('Error checking user action permission:', error);
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error checking action permission');
}
}
}

View File

@@ -1,51 +1,122 @@
export * from "./unauthorizedResponse";
export * from "./verifySession";
export * from "./unauthorizedResponse";
import { Lucia, TimeSpan } from "lucia";
import { DrizzleSQLiteAdapter } from "@lucia-auth/adapter-drizzle";
import {
encodeBase32LowerCaseNoPadding,
encodeHexLowerCase,
} from "@oslojs/encoding";
import { sha256 } from "@oslojs/crypto/sha2";
import { Session, sessions, User, users } from "@server/db/schema";
import db from "@server/db";
import { sessions, users } from "@server/db/schema";
import { eq } from "drizzle-orm";
import config from "@server/config";
import type { RandomReader } from "@oslojs/crypto/random";
import { generateRandomString } from "@oslojs/crypto/random";
const adapter = new DrizzleSQLiteAdapter(db, sessions, users);
export const SESSION_COOKIE_NAME = "session";
export const SESSION_COOKIE_EXPIRES = 1000 * 60 * 60 * 24 * 30;
export const SECURE_COOKIES = config.server.secure_cookies;
export const COOKIE_DOMAIN =
"." + new URL(config.app.base_url).hostname.split(".").slice(-2).join(".");
export const lucia = new Lucia(adapter, {
getUserAttributes: (attributes) => {
return {
email: attributes.email,
twoFactorEnabled: attributes.twoFactorEnabled,
twoFactorSecret: attributes.twoFactorSecret,
emailVerified: attributes.emailVerified,
dateCreated: attributes.dateCreated,
};
},
sessionCookie: {
name: "session",
expires: false,
attributes: {
sameSite: "strict",
secure: config.server.secure_cookies || false,
domain:
"." + new URL(config.app.base_url).hostname.split(".").slice(-2).join("."),
},
},
sessionExpiresIn: new TimeSpan(2, "w"),
});
export function generateSessionToken(): string {
const bytes = new Uint8Array(20);
crypto.getRandomValues(bytes);
const token = encodeBase32LowerCaseNoPadding(bytes);
return token;
}
export default lucia;
export async function createSession(
token: string,
userId: string,
): Promise<Session> {
const sessionId = encodeHexLowerCase(
sha256(new TextEncoder().encode(token)),
);
const session: Session = {
sessionId: sessionId,
userId,
expiresAt: new Date(Date.now() + SESSION_COOKIE_EXPIRES).getTime(),
};
await db.insert(sessions).values(session);
return session;
}
declare module "lucia" {
interface Register {
Lucia: typeof lucia;
DatabaseUserAttributes: DatabaseUserAttributes;
export async function validateSessionToken(
token: string,
): Promise<SessionValidationResult> {
const sessionId = encodeHexLowerCase(
sha256(new TextEncoder().encode(token)),
);
const result = await db
.select({ user: users, session: sessions })
.from(sessions)
.innerJoin(users, eq(sessions.userId, users.userId))
.where(eq(sessions.sessionId, sessionId));
if (result.length < 1) {
return { session: null, user: null };
}
const { user, session } = result[0];
if (Date.now() >= session.expiresAt) {
await db
.delete(sessions)
.where(eq(sessions.sessionId, session.sessionId));
return { session: null, user: null };
}
if (Date.now() >= session.expiresAt - (SESSION_COOKIE_EXPIRES / 2)) {
session.expiresAt = new Date(
Date.now() + SESSION_COOKIE_EXPIRES,
).getTime();
await db
.update(sessions)
.set({
expiresAt: session.expiresAt,
})
.where(eq(sessions.sessionId, session.sessionId));
}
return { session, user };
}
export async function invalidateSession(sessionId: string): Promise<void> {
await db.delete(sessions).where(eq(sessions.sessionId, sessionId));
}
export async function invalidateAllSessions(userId: string): Promise<void> {
await db.delete(sessions).where(eq(sessions.userId, userId));
}
export function serializeSessionCookie(token: string): string {
if (SECURE_COOKIES) {
return `${SESSION_COOKIE_NAME}=${token}; HttpOnly; SameSite=Lax; Max-Age=${SESSION_COOKIE_EXPIRES}; Path=/; Secure; Domain=${COOKIE_DOMAIN}`;
} else {
return `${SESSION_COOKIE_NAME}=${token}; HttpOnly; SameSite=Lax; Max-Age=${SESSION_COOKIE_EXPIRES}; Path=/; Domain=${COOKIE_DOMAIN}`;
}
}
interface DatabaseUserAttributes {
email: string;
passwordHash: string;
twoFactorEnabled: boolean;
twoFactorSecret?: string;
emailVerified: boolean;
dateCreated: string;
export function createBlankSessionTokenCookie(): string {
if (SECURE_COOKIES) {
return `${SESSION_COOKIE_NAME}=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/; Secure; Domain=${COOKIE_DOMAIN}`;
} else {
return `${SESSION_COOKIE_NAME}=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/; Domain=${COOKIE_DOMAIN}`;
}
}
const random: RandomReader = {
read(bytes: Uint8Array): void {
crypto.getRandomValues(bytes);
},
};
export function generateId(length: number): string {
const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
return generateRandomString(random, alphabet, length);
}
export function generateIdFromEntropySize(size: number): string {
const buffer = crypto.getRandomValues(new Uint8Array(size));
return encodeBase32LowerCaseNoPadding(buffer);
}
export type SessionValidationResult =
| { session: Session; user: User }
| { session: null; user: null };

View File

@@ -1,9 +1,9 @@
import { Request } from "express";
import { lucia } from "@server/auth";
import { validateSessionToken, SESSION_COOKIE_NAME } from "@server/auth";
export async function verifySession(req: Request) {
const res = await lucia.validateSession(
req.cookies[lucia.sessionCookieName],
const res = await validateSessionToken(
req.cookies[SESSION_COOKIE_NAME] ?? "",
);
return res;
}