52 lines
1.5 KiB
Python
52 lines
1.5 KiB
Python
"""Auth module — bcrypt password verification and session token management."""
|
|
|
|
import os
|
|
import secrets
|
|
|
|
import bcrypt
|
|
|
|
# Module-level defaults (read from env at import time for AUTH_USER/AUTH_PASS_HASH)
|
|
AUTH_USER: str = os.environ.get("AUTH_USER", "admin@localhost")
|
|
AUTH_PASS_HASH: str = os.environ.get("AUTH_PASS_HASH", "")
|
|
|
|
# In-memory session store: token -> email
|
|
_sessions: dict[str, str] = {}
|
|
|
|
|
|
def verify_password(password: str) -> bool:
|
|
"""Return True if password matches the bcrypt hash in AUTH_PASS_HASH env var.
|
|
|
|
Reads AUTH_PASS_HASH from env at call time so runtime env changes are respected.
|
|
Returns False if no hash is configured, if password is empty, or on any error.
|
|
"""
|
|
if not password:
|
|
return False
|
|
|
|
hash_str = os.environ.get("AUTH_PASS_HASH", "")
|
|
if not hash_str:
|
|
return False
|
|
|
|
try:
|
|
return bcrypt.checkpw(password.encode(), hash_str.encode())
|
|
except (ValueError, TypeError):
|
|
return False
|
|
|
|
|
|
def create_session_token(email: str) -> str:
|
|
"""Generate a session token, store it mapped to email, and return the token."""
|
|
token = secrets.token_hex(32)
|
|
_sessions[token] = email
|
|
return token
|
|
|
|
|
|
def validate_session_token(token: str) -> str | None:
|
|
"""Return the email for a valid token, or None if token is empty/unknown."""
|
|
if not token:
|
|
return None
|
|
return _sessions.get(token)
|
|
|
|
|
|
def invalidate_session_token(token: str) -> None:
|
|
"""Remove a session token from the store."""
|
|
_sessions.pop(token, None)
|