""" Token revocation list for local-identity OIDC serve. Revoked JTIs (JWT IDs) are stored in ~/.local-identity/revoked.json (mode 600). The list is append-only: JTIs are added but never removed. Usage: revoke(jti) Add a JTI to the revocation list. is_revoked(jti) Return True if the JTI has been revoked. """ import json import os from pathlib import Path from .store import _store_dir def _revoked_path() -> Path: return _store_dir() / "revoked.json" def _load() -> set[str]: path = _revoked_path() if not path.exists(): return set() try: data = json.loads(path.read_text(encoding="utf-8")) return set(data.get("revoked", [])) except (json.JSONDecodeError, OSError): return set() def _save(revoked: set[str]) -> None: path = _revoked_path() path.write_text( json.dumps({"revoked": sorted(revoked)}, indent=2), encoding="utf-8", ) os.chmod(path, 0o600) def is_revoked(jti: str) -> bool: """Return True if this JTI has been revoked. Defaults to False on read error.""" return jti in _load() def revoke(jti: str) -> None: """Add a JTI to the revocation list.""" revoked = _load() revoked.add(jti) _save(revoked)