feat(auth): session cookie signing and verification
This commit is contained in:
@@ -5,6 +5,8 @@ muxplex authentication — password and signing secret file management.
|
||||
import secrets
|
||||
from pathlib import Path
|
||||
|
||||
from itsdangerous import BadSignature, SignatureExpired, TimestampSigner
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Config directory
|
||||
@@ -66,3 +68,28 @@ def load_or_create_secret() -> str:
|
||||
path.write_text(secret + "\n")
|
||||
path.chmod(0o600)
|
||||
return secret
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Session cookie signing / verification
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def create_session_cookie(secret: str, ttl_seconds: int) -> str:
|
||||
"""Create a signed, timestamped session cookie value."""
|
||||
signer = TimestampSigner(secret)
|
||||
return signer.sign("muxplex-session").decode()
|
||||
|
||||
|
||||
def verify_session_cookie(secret: str, cookie: str, ttl_seconds: int) -> bool:
|
||||
"""Verify a session cookie's signature and expiry. Returns True/False.
|
||||
|
||||
ttl_seconds=0 means session cookie — no server-side expiry check.
|
||||
"""
|
||||
signer = TimestampSigner(secret)
|
||||
try:
|
||||
max_age = ttl_seconds if ttl_seconds > 0 else None
|
||||
signer.unsign(cookie, max_age=max_age)
|
||||
return True
|
||||
except (BadSignature, SignatureExpired):
|
||||
return False
|
||||
|
||||
@@ -119,3 +119,54 @@ def test_load_or_create_secret_returns_same_value_on_second_call(monkeypatch, tm
|
||||
first = load_or_create_secret()
|
||||
second = load_or_create_secret()
|
||||
assert first == second
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Session cookie signing / verification
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_create_session_cookie_returns_string():
|
||||
"""create_session_cookie() returns a non-empty string."""
|
||||
from muxplex.auth import create_session_cookie
|
||||
|
||||
cookie = create_session_cookie("test-secret", ttl_seconds=3600)
|
||||
assert isinstance(cookie, str)
|
||||
assert len(cookie) > 0
|
||||
|
||||
|
||||
def test_verify_session_cookie_valid_roundtrip():
|
||||
"""A cookie created by create_session_cookie verifies successfully."""
|
||||
from muxplex.auth import create_session_cookie, verify_session_cookie
|
||||
|
||||
cookie = create_session_cookie("test-secret", ttl_seconds=3600)
|
||||
assert verify_session_cookie("test-secret", cookie, ttl_seconds=3600) is True
|
||||
|
||||
|
||||
def test_verify_session_cookie_tampered():
|
||||
"""A tampered cookie fails verification."""
|
||||
from muxplex.auth import create_session_cookie, verify_session_cookie
|
||||
|
||||
cookie = create_session_cookie("test-secret", ttl_seconds=3600)
|
||||
tampered = cookie + "X"
|
||||
assert verify_session_cookie("test-secret", tampered, ttl_seconds=3600) is False
|
||||
|
||||
|
||||
def test_verify_session_cookie_wrong_secret():
|
||||
"""A cookie signed with a different secret fails verification."""
|
||||
from muxplex.auth import create_session_cookie, verify_session_cookie
|
||||
|
||||
cookie = create_session_cookie("secret-A", ttl_seconds=3600)
|
||||
assert verify_session_cookie("secret-B", cookie, ttl_seconds=3600) is False
|
||||
|
||||
|
||||
def test_verify_session_cookie_expired():
|
||||
"""A cookie verified with a very short TTL fails (simulates expiry)."""
|
||||
import time
|
||||
from muxplex.auth import create_session_cookie, verify_session_cookie
|
||||
|
||||
cookie = create_session_cookie("test-secret", ttl_seconds=1)
|
||||
# itsdangerous uses integer-second timestamps; sleep 2s to guarantee
|
||||
# age = 2 > max_age = 1, ensuring reliable expiry detection
|
||||
time.sleep(2)
|
||||
assert verify_session_cookie("test-secret", cookie, ttl_seconds=1) is False
|
||||
|
||||
Reference in New Issue
Block a user