MikelWL's picture
Add config view and shared-password gate
ca1ff64
import base64
import hashlib
import hmac
import json
import os
import time
from typing import Optional
COOKIE_NAME = "converta_session"
INTERNAL_HEADER = "x-internal-auth"
def get_app_password() -> Optional[str]:
value = os.environ.get("APP_PASSWORD")
if value is None:
return None
value = value.strip()
return value or None
def _b64url_encode(data: bytes) -> str:
return base64.urlsafe_b64encode(data).decode("ascii").rstrip("=")
def _b64url_decode(data: str) -> bytes:
padded = data + "=" * (-len(data) % 4)
return base64.urlsafe_b64decode(padded.encode("ascii"))
def _sign(payload_b64: str, secret: str) -> str:
sig = hmac.new(secret.encode("utf-8"), payload_b64.encode("ascii"), hashlib.sha256).digest()
return _b64url_encode(sig)
def create_session_token(secret: str, ttl_seconds: int = 7 * 24 * 60 * 60) -> str:
now = int(time.time())
payload = {"iat": now, "exp": now + ttl_seconds}
payload_b64 = _b64url_encode(json.dumps(payload, separators=(",", ":")).encode("utf-8"))
sig_b64 = _sign(payload_b64, secret)
return f"{payload_b64}.{sig_b64}"
def verify_session_token(token: str, secret: str) -> bool:
try:
payload_b64, sig_b64 = token.split(".", 1)
except ValueError:
return False
expected = _sign(payload_b64, secret)
if not hmac.compare_digest(expected, sig_b64):
return False
try:
payload = json.loads(_b64url_decode(payload_b64).decode("utf-8"))
except Exception:
return False
exp = payload.get("exp")
if not isinstance(exp, int):
return False
if exp < int(time.time()):
return False
return True
def constant_time_equals(a: str, b: str) -> bool:
return hmac.compare_digest(a.encode("utf-8"), b.encode("utf-8"))