From 853bca824ad2048544523186f98c9295c12ae34c Mon Sep 17 00:00:00 2001 From: Jage9 Date: Tue, 24 Feb 2026 23:12:01 -0500 Subject: [PATCH] Tighten auth defaults and register form behavior --- client/index.html | 9 +++++--- client/public/version.js | 2 +- client/src/main.ts | 20 +++++++++++------ deploy/scripts/up.sh | 3 --- server/app/auth_service.py | 46 ++++++++++++++++++++++++++------------ 5 files changed, 52 insertions(+), 28 deletions(-) diff --git a/client/index.html b/client/index.html index 9ab3b37..c7de718 100644 --- a/client/index.html +++ b/client/index.html @@ -19,8 +19,7 @@ -

- +
diff --git a/client/public/version.js b/client/public/version.js index 82d151e..2bc04bc 100644 --- a/client/public/version.js +++ b/client/public/version.js @@ -1,5 +1,5 @@ // Maintainer-controlled web client version. // Format: YYYY.MM.DD Rn (example: 2026.02.20 R2) -window.CHGRID_WEB_VERSION = "2026.02.25 R247"; +window.CHGRID_WEB_VERSION = "2026.02.25 R248"; // Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid. window.CHGRID_TIME_ZONE = "America/Detroit"; diff --git a/client/src/main.ts b/client/src/main.ts index dab65e0..34364ac 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -95,9 +95,9 @@ type Dom = { registerView: HTMLElement; authUsername: HTMLInputElement; authPassword: HTMLInputElement; - authPolicyHintLogin: HTMLParagraphElement; registerUsername: HTMLInputElement; registerPassword: HTMLInputElement; + registerPasswordConfirm: HTMLInputElement; registerEmail: HTMLInputElement; authPolicyHintRegister: HTMLParagraphElement; showRegisterButton: HTMLButtonElement; @@ -128,9 +128,9 @@ const dom: Dom = { registerView: requiredById('registerView'), authUsername: requiredById('authUsername'), authPassword: requiredById('authPassword'), - authPolicyHintLogin: requiredById('authPolicyHintLogin'), registerUsername: requiredById('registerUsername'), registerPassword: requiredById('registerPassword'), + registerPasswordConfirm: requiredById('registerPasswordConfirm'), registerEmail: requiredById('registerEmail'), authPolicyHintRegister: requiredById('authPolicyHintRegister'), showRegisterButton: requiredById('showRegisterButton'), @@ -551,9 +551,7 @@ function applyAuthPolicy(policy: unknown): void { passwordMaxLength: passwordMax, }; localStorage.setItem(AUTH_POLICY_STORAGE_KEY, JSON.stringify(authPolicy)); - const hint = `Username: ${usernameMin}-${usernameMax} chars (a-z, 0-9, _, -). Password: ${passwordMin}-${passwordMax} chars.`; - dom.authPolicyHintLogin.textContent = hint; - dom.authPolicyHintRegister.textContent = hint; + dom.authPolicyHintRegister.textContent = `Username, ${usernameMin}-${usernameMax} characters. Password, ${passwordMin}-${passwordMax} characters.`; dom.authUsername.minLength = usernameMin; dom.authUsername.maxLength = usernameMax; dom.registerUsername.minLength = usernameMin; @@ -562,6 +560,8 @@ function applyAuthPolicy(policy: unknown): void { dom.authPassword.maxLength = passwordMax; dom.registerPassword.minLength = passwordMin; dom.registerPassword.maxLength = passwordMax; + dom.registerPasswordConfirm.minLength = passwordMin; + dom.registerPasswordConfirm.maxLength = passwordMax; updateConnectAvailability(); } @@ -597,7 +597,8 @@ function updateConnectAvailability(): void { sanitizeAuthUsername(dom.authUsername.value).length >= usernameMin && dom.authPassword.value.trim().length >= passwordMin; const hasRegisterCredentials = sanitizeAuthUsername(dom.registerUsername.value).length >= usernameMin && - dom.registerPassword.value.trim().length >= passwordMin; + dom.registerPassword.value.trim().length >= passwordMin && + dom.registerPassword.value === dom.registerPasswordConfirm.value; const authReady = hasSessionToken || (authMode === 'login' ? hasLoginCredentials : hasRegisterCredentials); dom.connectButton.textContent = hasSessionToken ? 'Connect' : authMode === 'login' ? 'Log In & Connect' : 'Register & Connect'; dom.connectButton.disabled = mediaSession.isConnecting() || !authReady; @@ -1423,7 +1424,7 @@ function buildAuthRequestPacket(): OutgoingMessage | null { const username = sanitizeAuthUsername(dom.registerUsername.value); const password = dom.registerPassword.value; const email = dom.registerEmail.value.trim(); - if (!username || !password) return null; + if (!username || !password || password !== dom.registerPasswordConfirm.value) return null; return { type: 'auth_register', username, password, ...(email ? { email } : {}) }; } const username = sanitizeAuthUsername(dom.authUsername.value); @@ -1462,6 +1463,7 @@ async function handleAuthResult(message: Extract { updateConnectAvailability(); }); + dom.registerPasswordConfirm.addEventListener('input', () => { + updateConnectAvailability(); + }); dom.registerEmail.addEventListener('input', () => { updateConnectAvailability(); }); diff --git a/deploy/scripts/up.sh b/deploy/scripts/up.sh index eca875b..d59671c 100644 --- a/deploy/scripts/up.sh +++ b/deploy/scripts/up.sh @@ -5,9 +5,6 @@ REPO_ROOT="${1:-/home/bestmidi/chgrid}" PUBLISH_DIR="${2:-/home/bestmidi/public_html/chgrid}" BASE_PATH="${3:-/chgrid/}" SERVICE_NAME="${4:-chat-grid.service}" -if [[ -z "${SERVICE_NAME// }" || "$SERVICE_NAME" == "-" || "$SERVICE_NAME" == ".service" ]]; then - SERVICE_NAME="chat-grid.service" -fi "$REPO_ROOT/deploy/scripts/deploy_client.sh" "$REPO_ROOT" "$PUBLISH_DIR" "$BASE_PATH" sudo systemctl restart "$SERVICE_NAME" diff --git a/server/app/auth_service.py b/server/app/auth_service.py index 37e6859..82fec3b 100644 --- a/server/app/auth_service.py +++ b/server/app/auth_service.py @@ -12,7 +12,6 @@ import re import secrets import sqlite3 import time -import uuid SESSION_TTL_MS = 14 * 24 * 60 * 60 * 1000 @@ -111,16 +110,15 @@ class AuthService: if role not in {"user", "admin"}: raise AuthError("role must be user or admin.") now_ms = self.now_ms() - user_id = str(uuid.uuid4()) password_hash = self._hash_password(password) try: self._conn.execute( """ INSERT INTO users ( - id, username, password_hash, email, role, status, last_nickname, created_at_ms, updated_at_ms, last_login_at_ms - ) VALUES (?, ?, ?, ?, ?, 'active', NULL, ?, ?, ?) + username, password_hash, email, role, status, last_nickname, created_at_ms, updated_at_ms, last_login_at_ms + ) VALUES (?, ?, ?, ?, 'active', NULL, ?, ?, ?) """, - (user_id, normalized_username, password_hash, normalized_email, role, now_ms, now_ms, now_ms), + (normalized_username, password_hash, normalized_email, role, now_ms, now_ms, now_ms), ) self._conn.commit() except sqlite3.IntegrityError as exc: @@ -154,6 +152,16 @@ class AuthService: if not self._verify_password(password, user_row["password_hash"]): raise AuthError("Invalid username or password.") user = self._row_to_user(user_row) + if not user.last_nickname: + self.set_last_nickname(user.id, user.username) + user = AuthUser( + id=user.id, + username=user.username, + role=user.role, + status=user.status, + email=user.email, + last_nickname=user.username, + ) self._conn.execute( "UPDATE users SET last_login_at_ms = ?, updated_at_ms = ? WHERE id = ?", (self.now_ms(), self.now_ms(), user.id), @@ -196,13 +204,23 @@ class AuthService: ) self._conn.commit() user = AuthUser( - id=row["user_id"], + id=str(row["user_id"]), username=row["username"], role=row["role"], status=row["status"], email=row["email"], last_nickname=row["last_nickname"], ) + if not user.last_nickname: + self.set_last_nickname(user.id, user.username) + user = AuthUser( + id=user.id, + username=user.username, + role=user.role, + status=user.status, + email=user.email, + last_nickname=user.username, + ) return AuthSession(session_id=row["session_id"], token=cleaned, user=user) def revoke(self, token: str) -> None: @@ -243,7 +261,7 @@ class AuthService: self._conn.execute( """ CREATE TABLE IF NOT EXISTS users ( - id TEXT PRIMARY KEY, + id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, email TEXT UNIQUE, @@ -259,8 +277,8 @@ class AuthService: self._conn.execute( """ CREATE TABLE IF NOT EXISTS sessions ( - id TEXT PRIMARY KEY, - user_id TEXT NOT NULL, + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, token_hash TEXT NOT NULL UNIQUE, created_at_ms INTEGER NOT NULL, last_seen_at_ms INTEGER NOT NULL, @@ -288,14 +306,14 @@ class AuthService: token_hash = self._hash_token(token) now_ms = self.now_ms() expires_at_ms = now_ms + SESSION_TTL_MS - session_id = str(uuid.uuid4()) self._conn.execute( """ - INSERT INTO sessions (id, user_id, token_hash, created_at_ms, last_seen_at_ms, expires_at_ms, revoked_at_ms, ip, user_agent) - VALUES (?, ?, ?, ?, ?, ?, NULL, NULL, NULL) + INSERT INTO sessions (user_id, token_hash, created_at_ms, last_seen_at_ms, expires_at_ms, revoked_at_ms, ip, user_agent) + VALUES (?, ?, ?, ?, ?, NULL, NULL, NULL) """, - (session_id, user.id, token_hash, now_ms, now_ms, expires_at_ms), + (user.id, token_hash, now_ms, now_ms, expires_at_ms), ) + session_id = str(self._conn.execute("SELECT last_insert_rowid() AS id").fetchone()["id"]) self._conn.commit() return AuthSession(session_id=session_id, token=token, user=user) @@ -315,7 +333,7 @@ class AuthService: """Convert a DB row into AuthUser.""" return AuthUser( - id=row["id"], + id=str(row["id"]), username=row["username"], role=row["role"], status=row["status"],