feat(auth): jti claim and token-revocation store
Stateless JWTs had no revocation path: a stolen token stayed valid for its full 24h even after the victim changed their password, and there was no logout. This lays the foundation for revoking them. - User.tokens_valid_from: per-user bulk-revocation cutoff (compared against the token's iat). RevokedToken(jti PK, exp): single-token denylist, pruned opportunistically on insert so it never outgrows live-but-revoked tokens. - login() now mints a jti; create_access_token already stamps iat/exp. - repo.revoke_token / is_token_revoked / set_tokens_valid_from (abstract + shared sqlmodel impl + DummyRepo coverage stubs). - Centralized validate path in dependencies.py: every auth dependency now resolves the user and fails closed on (1) missing jti (legacy/pre-deploy token -> one forced re-login), (2) iat before the cutoff, (3) a denylisted jti. Denylist lookups ride a 10s membership cache mirroring the user cache. - Contract/fuzz harness seeds its fixed-uuid principal under DECNET_CONTRACT_TEST so its minted token resolves to a live admin user.
This commit is contained in:
@@ -114,6 +114,25 @@ class BaseRepository(ABC):
|
||||
"""Update a user's role."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def revoke_token(self, jti: str, user_uuid: str, expires_at: datetime) -> None:
|
||||
"""Add a token's ``jti`` to the logout denylist.
|
||||
|
||||
Implementations also prune rows whose ``expires_at`` has passed, so the
|
||||
denylist never outgrows the set of live-but-revoked tokens.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def is_token_revoked(self, jti: str) -> bool:
|
||||
"""True if ``jti`` is currently on the logout denylist."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def set_tokens_valid_from(self, user_uuid: str, ts: datetime) -> None:
|
||||
"""Bulk-revoke: reject every token for this user issued before ``ts``."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def purge_logs_and_bounties(self) -> dict[str, int]:
|
||||
"""Delete all logs, bounties, and attacker profiles. Returns counts of deleted rows."""
|
||||
|
||||
Reference in New Issue
Block a user