Server-side session TTL enforcement
- session_exists() now rejects rows older than 30 days, matching the client cookie max-age. - Opportunistic cleanup of expired rows on session_exists() calls, preventing unbounded growth of sessions.db from orphaned tokens (PWA reinstalls, manual cookie clears). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+7
-3
@@ -4,7 +4,7 @@ import sqlite3
|
|||||||
import subprocess
|
import subprocess
|
||||||
import hashlib
|
import hashlib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from sentence_transformers import SentenceTransformer
|
from sentence_transformers import SentenceTransformer
|
||||||
import anthropic
|
import anthropic
|
||||||
@@ -132,6 +132,7 @@ When making factual claims about Aaron — his history, credentials, locations,
|
|||||||
# Auth configuration
|
# Auth configuration
|
||||||
import os
|
import os
|
||||||
SESSION_PASSWORD = os.getenv("AARON_AI_PASSWORD", "changeme")
|
SESSION_PASSWORD = os.getenv("AARON_AI_PASSWORD", "changeme")
|
||||||
|
SESSION_MAX_AGE_SECONDS = 60 * 60 * 24 * 30
|
||||||
SESSIONS_DB = str(Path.home() / "aaronai" / "sessions.db")
|
SESSIONS_DB = str(Path.home() / "aaronai" / "sessions.db")
|
||||||
|
|
||||||
def _init_sessions():
|
def _init_sessions():
|
||||||
@@ -163,7 +164,10 @@ def delete_session(token: str):
|
|||||||
|
|
||||||
def session_exists(token: str) -> bool:
|
def session_exists(token: str) -> bool:
|
||||||
conn = _connect_sessions()
|
conn = _connect_sessions()
|
||||||
row = conn.execute("SELECT 1 FROM sessions WHERE token = ?", (token,)).fetchone()
|
cutoff = (datetime.now() - timedelta(seconds=SESSION_MAX_AGE_SECONDS)).isoformat()
|
||||||
|
conn.execute("DELETE FROM sessions WHERE created_at < ?", (cutoff,))
|
||||||
|
conn.commit()
|
||||||
|
row = conn.execute("SELECT 1 FROM sessions WHERE token = ? AND created_at >= ?", (token, cutoff)).fetchone()
|
||||||
conn.close()
|
conn.close()
|
||||||
return row is not None
|
return row is not None
|
||||||
|
|
||||||
@@ -381,7 +385,7 @@ async def login(request: Request, response: Response):
|
|||||||
httponly=True,
|
httponly=True,
|
||||||
secure=True,
|
secure=True,
|
||||||
samesite="lax",
|
samesite="lax",
|
||||||
max_age=60 * 60 * 24 * 30
|
max_age=SESSION_MAX_AGE_SECONDS
|
||||||
)
|
)
|
||||||
response.body = b'{"ok": true}'
|
response.body = b'{"ok": true}'
|
||||||
response.status_code = 200
|
response.status_code = 200
|
||||||
|
|||||||
Reference in New Issue
Block a user