Files
flowdeck/app/password_utils.py
T
bruno 32c1f70c53
FlowDeck CI / test (push) Failing after 6s
FlowDeck CI / docker (push) Has been skipped
feat: multi-user auth — local accounts, settings page, OAuth prep
Phase 1 foundation:
- DB: users table extended (password_hash, is_active, login_attempts, locked_until)
- DB: user_oauth_tokens table (user_id, provider, access_token, refresh_token)
- password_utils.py: SHA-256+salt hashing, verify, rate-limit lock check
- auth.py: POST /auth/register + POST /auth/local-login + /auth/login?provider=local
- Login page: tabs Login/Register with Gitea OAuth button
- settings.html: profile (name/password), forges, API tokens, sessions
- ALTER TABLE migrations for existing DBs
2026-07-10 15:34:58 -04:00

36 lines
1.0 KiB
Python

"""Password hashing and login security utilities."""
import hashlib
import secrets
import time
def hash_password(password: str) -> str:
"""Hash a password using SHA-256 + random salt (16 bytes).
Format: salt_hex:hash_hex (64 + 64 = 128 chars)
Fallback for bcrypt — we use SHA-256 for SQLite simplicity
but with proper salt per password."""
salt = secrets.token_hex(16)
h = hashlib.sha256(f"{salt}:{password}".encode()).hexdigest()
return f"{salt}:{h}"
def verify_password(password: str, stored: str) -> bool:
"""Verify a password against its stored hash."""
try:
salt, h = stored.split(":", 1)
expected = hashlib.sha256(f"{salt}:{password}".encode()).hexdigest()
return h == expected
except (ValueError, AttributeError):
return False
def is_locked(locked_until: str | None) -> bool:
"""Check if account is temporarily locked."""
if not locked_until:
return False
try:
return float(locked_until) > time.time()
except (ValueError, TypeError):
return False