Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crm/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Shared password (16+ chars) and a random secret (16+ chars) that signs session cookies.
# Shared password (12+ chars) and a random secret (16+ chars) that signs session cookies.
# Rotating either logs everyone out.
CRM_PASSWORD=
CRM_SESSION_SECRET=
Expand Down
3 changes: 2 additions & 1 deletion crm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ The cookie is `nonce.expiry.signature`, signed with `CRM_SESSION_SECRET` (random
not the password, so a leaked cookie gives nothing to brute force) and valid
only while its nonce is listed in `/data/sessions.json`: logout revokes it,
rotating either variable logs everyone out. Login attempts are limited to 10
per client per 15 minutes. Both variables must be 16 characters or more.
per client per 15 minutes. The password needs 12 characters or more, the
secret 16.

Module state (snapshot cache, refresh mutex, PostHog budget, login counters)
lives on `globalThis` and the snapshot file is re-read whenever its mtime
Expand Down
2 changes: 1 addition & 1 deletion crm/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default async function LoginPage({ searchParams }: { searchParams: Promis
</p>
{!authConfigured() ? (
<p className="panel mt-6 px-3 py-2 text-xs" style={{ color: "var(--bad)" }}>
CRM_PASSWORD and CRM_SESSION_SECRET must both be set (16 characters minimum). Nobody can log in until they are.
CRM_PASSWORD (12 characters minimum) and CRM_SESSION_SECRET (16 minimum) must both be set. Nobody can log in until they are.
</p>
) : (
<form action="/api/login" method="post" className="panel mt-6 space-y-3 p-4">
Expand Down
7 changes: 5 additions & 2 deletions crm/lib/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import { listSession, sessionListed, unlistSession } from "@/lib/sessions";

export const COOKIE = "ocb_crm";
export const SESSION_DAYS = 30;
const MIN_LEN = 16;
// 12 for the password (a memorable one, behind the per-client and global
// login limits), 16 for the signing secret (random, never typed).
const MIN_PASSWORD_LEN = 12;
const MIN_SECRET_LEN = 16;

const password = () => process.env.CRM_PASSWORD ?? "";
const secret = () => process.env.CRM_SESSION_SECRET ?? "";

export function authConfigured(): boolean {
return password().length >= MIN_LEN && secret().length >= MIN_LEN;
return password().length >= MIN_PASSWORD_LEN && secret().length >= MIN_SECRET_LEN;
}

const enc = new TextEncoder();
Expand Down
Loading