feat(auth): generic HMAC signed-cookie module for operator auth
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
# tests/test_operator_cookies.py
|
||||
import time
|
||||
import base64
|
||||
import json
|
||||
from backend.auth_cookies import sign, read
|
||||
|
||||
|
||||
def test_sign_then_read_round_trips():
|
||||
now = int(time.time())
|
||||
raw = sign({"uid": "abc", "iat": now})
|
||||
data = read(raw, max_age=3600)
|
||||
assert data == {"uid": "abc", "iat": now}
|
||||
|
||||
|
||||
def test_tampered_signature_is_rejected():
|
||||
raw = sign({"uid": "abc", "iat": int(time.time())})
|
||||
body, _sig = raw.rsplit(".", 1)
|
||||
assert read(body + ".deadbeef", max_age=3600) is None
|
||||
|
||||
|
||||
def test_tampered_body_is_rejected():
|
||||
raw = sign({"uid": "abc", "iat": int(time.time())})
|
||||
body, sig = raw.rsplit(".", 1)
|
||||
forged = base64.urlsafe_b64encode(json.dumps({"uid": "evil", "iat": int(time.time())}).encode()).decode()
|
||||
assert read(forged + "." + sig, max_age=3600) is None
|
||||
|
||||
|
||||
def test_expired_by_iat_is_rejected():
|
||||
raw = sign({"uid": "abc", "iat": int(time.time()) - 10_000})
|
||||
assert read(raw, max_age=3600) is None
|
||||
|
||||
|
||||
def test_garbage_input_is_none_not_raise():
|
||||
assert read("not-a-cookie", max_age=3600) is None
|
||||
assert read("", max_age=3600) is None
|
||||
assert read(None, max_age=3600) is None
|
||||
|
||||
|
||||
def test_wrong_secret_is_rejected(monkeypatch):
|
||||
import backend.auth_cookies as ac
|
||||
monkeypatch.setattr(ac, "SECRET_KEY", "secret-A")
|
||||
raw = ac.sign({"uid": "x", "iat": int(time.time())})
|
||||
monkeypatch.setattr(ac, "SECRET_KEY", "secret-B")
|
||||
assert ac.read(raw, max_age=3600) is None
|
||||
|
||||
|
||||
def test_future_dated_iat_is_rejected():
|
||||
raw = sign({"uid": "x", "iat": int(time.time()) + 10_000})
|
||||
assert read(raw, max_age=3600) is None
|
||||
Reference in New Issue
Block a user