2ce7862058
Replace complex JWT + refresh token authentication with simple session-based auth using signed cookies. **Removed:** - JWT token service (jwt_service.py) - Refresh token store (refresh_store.py) - Refresh token model and database table - JWKS fetching and OIDC token verification - python-jose dependency **Added:** - Session service (session.py) with HMAC-SHA256 signed cookies - Auth dependencies module for shared auth logic - Session-based auth endpoints **Updated:** - All API endpoints to use session-based auth - Config: removed JWT settings, added SESSION_SECRET/SESSION_TTL_HOURS - Tests: rewritten for session-based flow - Frontend: no changes needed (already uses cookies) Quality gates: ruff ✓, mypy ✓, typecheck ✓, lint ✓
75 lines
2.4 KiB
Python
75 lines
2.4 KiB
Python
import pytest
|
|
|
|
from src.auth.cookies import build_cookie_options
|
|
from src.auth.oidc import build_login_redirect_url
|
|
from src.auth.session import create_session_cookie, decode_session_cookie
|
|
from src.config import Settings
|
|
|
|
|
|
@pytest.mark.integration
|
|
def test_cookie_options_follow_environment_defaults(monkeypatch) -> None:
|
|
monkeypatch.setenv("APP_ENV", "development")
|
|
dev_settings = Settings()
|
|
dev_options = build_cookie_options(dev_settings)
|
|
|
|
monkeypatch.setenv("APP_ENV", "production")
|
|
prod_settings = Settings()
|
|
prod_options = build_cookie_options(prod_settings)
|
|
|
|
assert dev_options["httponly"] is True
|
|
assert dev_options["secure"] is False
|
|
assert dev_options["samesite"] == "lax"
|
|
assert prod_options["secure"] is True
|
|
assert prod_options["samesite"] == "strict"
|
|
|
|
|
|
@pytest.mark.integration
|
|
def test_login_redirect_url_contains_required_oidc_params() -> None:
|
|
settings = Settings()
|
|
|
|
url = build_login_redirect_url(
|
|
settings=settings,
|
|
redirect_uri="http://localhost:8000/auth/callback",
|
|
state="state-123",
|
|
)
|
|
|
|
assert "response_type=code" in url
|
|
assert "client_id=headquarter-web" in url
|
|
assert "scope=openid+profile+email" in url
|
|
assert "state=state-123" in url
|
|
|
|
|
|
@pytest.mark.integration
|
|
def test_create_and_decode_session_cookie_round_trip() -> None:
|
|
settings = Settings()
|
|
user_id = "test-user-123"
|
|
|
|
cookie = create_session_cookie(settings=settings, user_id=user_id)
|
|
payload = decode_session_cookie(settings=settings, cookie_value=cookie)
|
|
|
|
assert payload["user_id"] == user_id
|
|
assert "exp" in payload
|
|
|
|
|
|
@pytest.mark.integration
|
|
def test_decode_session_rejects_invalid_signature() -> None:
|
|
settings = Settings()
|
|
other_settings = Settings(session_secret="different-secret")
|
|
user_id = "test-user-123"
|
|
|
|
cookie = create_session_cookie(settings=other_settings, user_id=user_id)
|
|
|
|
with pytest.raises(ValueError, match="invalid session signature"):
|
|
decode_session_cookie(settings=settings, cookie_value=cookie)
|
|
|
|
|
|
@pytest.mark.integration
|
|
def test_decode_session_rejects_expired_cookie(monkeypatch) -> None:
|
|
settings = Settings(session_ttl_hours=-1) # Already expired
|
|
user_id = "test-user-123"
|
|
|
|
cookie = create_session_cookie(settings=settings, user_id=user_id)
|
|
|
|
with pytest.raises(ValueError, match="session expired"):
|
|
decode_session_cookie(settings=settings, cookie_value=cookie)
|