diff --git a/config/config.example.yml b/config/config.example.yml index e62af16d..50c1a623 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -9,7 +9,6 @@ server: internal_port: 3001 next_port: 3002 internal_hostname: "pangolin" - secure_cookies: true session_cookie_name: "p_session_token" resource_access_token_param: "p_token" resource_session_request_param: "p_session_request" diff --git a/install/fs/config.yml b/install/fs/config.yml index d2a2ffec..4a97dbb5 100644 --- a/install/fs/config.yml +++ b/install/fs/config.yml @@ -9,7 +9,6 @@ server: internal_port: 3001 next_port: 3002 internal_hostname: "pangolin" - secure_cookies: true session_cookie_name: "p_session_token" resource_access_token_param: "p_token" resource_session_request_param: "p_session_request" diff --git a/server/auth/index.ts b/server/auth/index.ts deleted file mode 100644 index 4bf2c40d..00000000 --- a/server/auth/index.ts +++ /dev/null @@ -1,118 +0,0 @@ -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 { eq } from "drizzle-orm"; -import config from "@server/lib/config"; -import type { RandomReader } from "@oslojs/crypto/random"; -import { generateRandomString } from "@oslojs/crypto/random"; - -export const SESSION_COOKIE_NAME = config.getRawConfig().server.session_cookie_name; -export const SESSION_COOKIE_EXPIRES = 1000 * 60 * 60 * 24 * 30; -export const SECURE_COOKIES = config.getRawConfig().server.secure_cookies; -export const COOKIE_DOMAIN = "." + config.getBaseDomain(); - -export function generateSessionToken(): string { - const bytes = new Uint8Array(20); - crypto.getRandomValues(bytes); - const token = encodeBase32LowerCaseNoPadding(bytes); - return token; -} - -export async function createSession( - token: string, - userId: string, -): Promise { - 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; -} - -export async function validateSessionToken( - token: string, -): Promise { - 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 { - await db.delete(sessions).where(eq(sessions.sessionId, sessionId)); -} - -export async function invalidateAllSessions(userId: string): Promise { - 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=Strict; Max-Age=${SESSION_COOKIE_EXPIRES}; Path=/; Secure; Domain=${COOKIE_DOMAIN}`; - } else { - return `${SESSION_COOKIE_NAME}=${token}; HttpOnly; SameSite=Strict; Max-Age=${SESSION_COOKIE_EXPIRES}; Path=/; Domain=${COOKIE_DOMAIN}`; - } -} - -export function createBlankSessionTokenCookie(): string { - if (SECURE_COOKIES) { - return `${SESSION_COOKIE_NAME}=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/; Secure; Domain=${COOKIE_DOMAIN}`; - } else { - return `${SESSION_COOKIE_NAME}=; HttpOnly; SameSite=Strict; 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 }; diff --git a/server/auth/sessions/app.ts b/server/auth/sessions/app.ts index 29c54eee..e58ff815 100644 --- a/server/auth/sessions/app.ts +++ b/server/auth/sessions/app.ts @@ -24,7 +24,6 @@ export const SESSION_COOKIE_EXPIRES = 60 * 60 * config.getRawConfig().server.dashboard_session_length_hours; -export const SECURE_COOKIES = config.getRawConfig().server.secure_cookies; export const COOKIE_DOMAIN = "." + new URL(config.getRawConfig().app.dashboard_url).hostname; @@ -108,12 +107,7 @@ export function serializeSessionCookie( isSecure: boolean ): string { if (isSecure) { - logger.debug("Setting cookie for secure origin"); - if (SECURE_COOKIES) { - return `${SESSION_COOKIE_NAME}=${token}; HttpOnly; SameSite=Strict; Max-Age=${SESSION_COOKIE_EXPIRES / 1000}; Path=/; Secure; Domain=${COOKIE_DOMAIN}`; - } else { - return `${SESSION_COOKIE_NAME}=${token}; HttpOnly; SameSite=Strict; Max-Age=${SESSION_COOKIE_EXPIRES / 1000}; Path=/; Domain=${COOKIE_DOMAIN}`; - } + return `${SESSION_COOKIE_NAME}=${token}; HttpOnly; SameSite=Strict; Max-Age=${SESSION_COOKIE_EXPIRES / 1000}; Path=/; Secure; Domain=${COOKIE_DOMAIN}`; } else { return `${SESSION_COOKIE_NAME}=${token}; HttpOnly; SameSite=Lax; Max-Age=${SESSION_COOKIE_EXPIRES}; Path=/;`; } @@ -121,11 +115,7 @@ export function serializeSessionCookie( export function createBlankSessionTokenCookie(isSecure: boolean): string { if (isSecure) { - if (SECURE_COOKIES) { - return `${SESSION_COOKIE_NAME}=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/; Secure; Domain=${COOKIE_DOMAIN}`; - } else { - return `${SESSION_COOKIE_NAME}=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/; Domain=${COOKIE_DOMAIN}`; - } + return `${SESSION_COOKIE_NAME}=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/; Secure; Domain=${COOKIE_DOMAIN}`; } else { return `${SESSION_COOKIE_NAME}=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/;`; } diff --git a/server/auth/sessions/resource.ts b/server/auth/sessions/resource.ts index e9dd9b96..0bc7f092 100644 --- a/server/auth/sessions/resource.ts +++ b/server/auth/sessions/resource.ts @@ -9,7 +9,6 @@ export const SESSION_COOKIE_NAME = config.getRawConfig().server.session_cookie_name; export const SESSION_COOKIE_EXPIRES = 1000 * 60 * 60 * config.getRawConfig().server.resource_session_length_hours; -export const SECURE_COOKIES = config.getRawConfig().server.secure_cookies; export async function createResourceSession(opts: { token: string; @@ -170,7 +169,7 @@ export function serializeResourceSessionCookie( token: string, isHttp: boolean = false ): string { - if (SECURE_COOKIES && !isHttp) { + if (!isHttp) { return `${cookieName}_s=${token}; HttpOnly; SameSite=Strict; Max-Age=${SESSION_COOKIE_EXPIRES / 1000}; Path=/; Secure; Domain=${"." + domain}`; } else { return `${cookieName}=${token}; HttpOnly; SameSite=Strict; Max-Age=${SESSION_COOKIE_EXPIRES / 1000}; Path=/; Domain=${"." + domain}`; @@ -179,9 +178,10 @@ export function serializeResourceSessionCookie( export function createBlankResourceSessionTokenCookie( cookieName: string, - domain: string + domain: string, + isHttp: boolean = false ): string { - if (SECURE_COOKIES) { + if (!isHttp) { return `${cookieName}_s=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/; Secure; Domain=${"." + domain}`; } else { return `${cookieName}=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/; Domain=${"." + domain}`; diff --git a/server/routers/badger/exchangeSession.ts b/server/routers/badger/exchangeSession.ts index 18925279..093dfbb9 100644 --- a/server/routers/badger/exchangeSession.ts +++ b/server/routers/badger/exchangeSession.ts @@ -12,8 +12,7 @@ import { serializeResourceSessionCookie, validateResourceSessionToken } from "@server/auth/sessions/resource"; -import { generateSessionToken } from "@server/auth"; -import { SESSION_COOKIE_EXPIRES } from "@server/auth/sessions/app"; +import { generateSessionToken, SESSION_COOKIE_EXPIRES } from "@server/auth/sessions/app"; import { SESSION_COOKIE_EXPIRES as RESOURCE_SESSION_COOKIE_EXPIRES } from "@server/auth/sessions/resource"; import config from "@server/lib/config"; import { response } from "@server/lib"; diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index b1a6fb12..5830d805 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -26,8 +26,8 @@ import { import { Resource, roleResources, userResources } from "@server/db/schema"; import logger from "@server/logger"; import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken"; -import { generateSessionToken } from "@server/auth"; import NodeCache from "node-cache"; +import { generateSessionToken } from "@server/auth/sessions/app"; // We'll see if this speeds anything up const cache = new NodeCache({ diff --git a/server/setup/scripts/1.0.0-beta10.ts b/server/setup/scripts/1.0.0-beta10.ts index e5bf5ba2..15c9532e 100644 --- a/server/setup/scripts/1.0.0-beta10.ts +++ b/server/setup/scripts/1.0.0-beta10.ts @@ -27,14 +27,16 @@ export default async function migration() { const fileContents = fs.readFileSync(filePath, "utf8"); rawConfig = yaml.load(fileContents); - rawConfig.server.secure_cookies = true; + delete rawConfig.server.secure_cookies; // Write the updated YAML back to the file const updatedYaml = yaml.dump(rawConfig); fs.writeFileSync(filePath, updatedYaml, "utf8"); + + console.log(`Removed deprecated config option: secure_cookies.`); } catch (e) { console.log( - `Failed to set secure_cookies to true in config. Please set it manually. https://docs.fossorial.io/Pangolin/Configuration/config` + `Was unable to remove deprecated config option: secure_cookies. Error: ${e}` ); return; }