mirror of
https://github.com/fosrl/pangolin.git
synced 2025-07-03 10:34:55 +02:00
remove secure_cookies option from config
This commit is contained in:
parent
f5e894e06a
commit
f40d91ff9e
8 changed files with 12 additions and 141 deletions
|
@ -9,7 +9,6 @@ server:
|
||||||
internal_port: 3001
|
internal_port: 3001
|
||||||
next_port: 3002
|
next_port: 3002
|
||||||
internal_hostname: "pangolin"
|
internal_hostname: "pangolin"
|
||||||
secure_cookies: true
|
|
||||||
session_cookie_name: "p_session_token"
|
session_cookie_name: "p_session_token"
|
||||||
resource_access_token_param: "p_token"
|
resource_access_token_param: "p_token"
|
||||||
resource_session_request_param: "p_session_request"
|
resource_session_request_param: "p_session_request"
|
||||||
|
|
|
@ -9,7 +9,6 @@ server:
|
||||||
internal_port: 3001
|
internal_port: 3001
|
||||||
next_port: 3002
|
next_port: 3002
|
||||||
internal_hostname: "pangolin"
|
internal_hostname: "pangolin"
|
||||||
secure_cookies: true
|
|
||||||
session_cookie_name: "p_session_token"
|
session_cookie_name: "p_session_token"
|
||||||
resource_access_token_param: "p_token"
|
resource_access_token_param: "p_token"
|
||||||
resource_session_request_param: "p_session_request"
|
resource_session_request_param: "p_session_request"
|
||||||
|
|
|
@ -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<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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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=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 };
|
|
|
@ -24,7 +24,6 @@ export const SESSION_COOKIE_EXPIRES =
|
||||||
60 *
|
60 *
|
||||||
60 *
|
60 *
|
||||||
config.getRawConfig().server.dashboard_session_length_hours;
|
config.getRawConfig().server.dashboard_session_length_hours;
|
||||||
export const SECURE_COOKIES = config.getRawConfig().server.secure_cookies;
|
|
||||||
export const COOKIE_DOMAIN =
|
export const COOKIE_DOMAIN =
|
||||||
"." + new URL(config.getRawConfig().app.dashboard_url).hostname;
|
"." + new URL(config.getRawConfig().app.dashboard_url).hostname;
|
||||||
|
|
||||||
|
@ -108,12 +107,7 @@ export function serializeSessionCookie(
|
||||||
isSecure: boolean
|
isSecure: boolean
|
||||||
): string {
|
): string {
|
||||||
if (isSecure) {
|
if (isSecure) {
|
||||||
logger.debug("Setting cookie for secure origin");
|
return `${SESSION_COOKIE_NAME}=${token}; HttpOnly; SameSite=Strict; Max-Age=${SESSION_COOKIE_EXPIRES / 1000}; Path=/; Secure; Domain=${COOKIE_DOMAIN}`;
|
||||||
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}`;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return `${SESSION_COOKIE_NAME}=${token}; HttpOnly; SameSite=Lax; Max-Age=${SESSION_COOKIE_EXPIRES}; Path=/;`;
|
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 {
|
export function createBlankSessionTokenCookie(isSecure: boolean): string {
|
||||||
if (isSecure) {
|
if (isSecure) {
|
||||||
if (SECURE_COOKIES) {
|
return `${SESSION_COOKIE_NAME}=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/; Secure; 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=Strict; Max-Age=0; Path=/; Domain=${COOKIE_DOMAIN}`;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return `${SESSION_COOKIE_NAME}=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/;`;
|
return `${SESSION_COOKIE_NAME}=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/;`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ export const SESSION_COOKIE_NAME =
|
||||||
config.getRawConfig().server.session_cookie_name;
|
config.getRawConfig().server.session_cookie_name;
|
||||||
export const SESSION_COOKIE_EXPIRES =
|
export const SESSION_COOKIE_EXPIRES =
|
||||||
1000 * 60 * 60 * config.getRawConfig().server.resource_session_length_hours;
|
1000 * 60 * 60 * config.getRawConfig().server.resource_session_length_hours;
|
||||||
export const SECURE_COOKIES = config.getRawConfig().server.secure_cookies;
|
|
||||||
|
|
||||||
export async function createResourceSession(opts: {
|
export async function createResourceSession(opts: {
|
||||||
token: string;
|
token: string;
|
||||||
|
@ -170,7 +169,7 @@ export function serializeResourceSessionCookie(
|
||||||
token: string,
|
token: string,
|
||||||
isHttp: boolean = false
|
isHttp: boolean = false
|
||||||
): string {
|
): string {
|
||||||
if (SECURE_COOKIES && !isHttp) {
|
if (!isHttp) {
|
||||||
return `${cookieName}_s=${token}; HttpOnly; SameSite=Strict; Max-Age=${SESSION_COOKIE_EXPIRES / 1000}; Path=/; Secure; Domain=${"." + domain}`;
|
return `${cookieName}_s=${token}; HttpOnly; SameSite=Strict; Max-Age=${SESSION_COOKIE_EXPIRES / 1000}; Path=/; Secure; Domain=${"." + domain}`;
|
||||||
} else {
|
} else {
|
||||||
return `${cookieName}=${token}; HttpOnly; SameSite=Strict; Max-Age=${SESSION_COOKIE_EXPIRES / 1000}; Path=/; Domain=${"." + domain}`;
|
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(
|
export function createBlankResourceSessionTokenCookie(
|
||||||
cookieName: string,
|
cookieName: string,
|
||||||
domain: string
|
domain: string,
|
||||||
|
isHttp: boolean = false
|
||||||
): string {
|
): string {
|
||||||
if (SECURE_COOKIES) {
|
if (!isHttp) {
|
||||||
return `${cookieName}_s=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/; Secure; Domain=${"." + domain}`;
|
return `${cookieName}_s=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/; Secure; Domain=${"." + domain}`;
|
||||||
} else {
|
} else {
|
||||||
return `${cookieName}=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/; Domain=${"." + domain}`;
|
return `${cookieName}=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/; Domain=${"." + domain}`;
|
||||||
|
|
|
@ -12,8 +12,7 @@ import {
|
||||||
serializeResourceSessionCookie,
|
serializeResourceSessionCookie,
|
||||||
validateResourceSessionToken
|
validateResourceSessionToken
|
||||||
} from "@server/auth/sessions/resource";
|
} from "@server/auth/sessions/resource";
|
||||||
import { generateSessionToken } from "@server/auth";
|
import { generateSessionToken, SESSION_COOKIE_EXPIRES } from "@server/auth/sessions/app";
|
||||||
import { SESSION_COOKIE_EXPIRES } from "@server/auth/sessions/app";
|
|
||||||
import { SESSION_COOKIE_EXPIRES as RESOURCE_SESSION_COOKIE_EXPIRES } from "@server/auth/sessions/resource";
|
import { SESSION_COOKIE_EXPIRES as RESOURCE_SESSION_COOKIE_EXPIRES } from "@server/auth/sessions/resource";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
|
|
|
@ -26,8 +26,8 @@ import {
|
||||||
import { Resource, roleResources, userResources } from "@server/db/schema";
|
import { Resource, roleResources, userResources } from "@server/db/schema";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken";
|
import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken";
|
||||||
import { generateSessionToken } from "@server/auth";
|
|
||||||
import NodeCache from "node-cache";
|
import NodeCache from "node-cache";
|
||||||
|
import { generateSessionToken } from "@server/auth/sessions/app";
|
||||||
|
|
||||||
// We'll see if this speeds anything up
|
// We'll see if this speeds anything up
|
||||||
const cache = new NodeCache({
|
const cache = new NodeCache({
|
||||||
|
|
|
@ -27,14 +27,16 @@ export default async function migration() {
|
||||||
const fileContents = fs.readFileSync(filePath, "utf8");
|
const fileContents = fs.readFileSync(filePath, "utf8");
|
||||||
rawConfig = yaml.load(fileContents);
|
rawConfig = yaml.load(fileContents);
|
||||||
|
|
||||||
rawConfig.server.secure_cookies = true;
|
delete rawConfig.server.secure_cookies;
|
||||||
|
|
||||||
// Write the updated YAML back to the file
|
// Write the updated YAML back to the file
|
||||||
const updatedYaml = yaml.dump(rawConfig);
|
const updatedYaml = yaml.dump(rawConfig);
|
||||||
fs.writeFileSync(filePath, updatedYaml, "utf8");
|
fs.writeFileSync(filePath, updatedYaml, "utf8");
|
||||||
|
|
||||||
|
console.log(`Removed deprecated config option: secure_cookies.`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue