API reference¶
Generated from the apogee-auth-runtime source with mkdocstrings. Every symbol below is exported from apogee_auth_runtime, so it is part of the supported public surface.
Domain¶
JwtKeyPair
dataclass
¶
Active signing key + previously valid keys (rotation).
kid (key id) is added to JWT headers so clients holding tokens
signed with older keys can still validate during the rotation window.
secret_for
¶
Domain · Exceptions¶
AuthError
¶
Bases: Exception
Base for apogee-auth-runtime errors.
Domain · Protocols (ports)¶
IJwtService
¶
Infrastructure¶
Argon2PasswordHasher
¶
Python
Argon2PasswordHasher(time_cost: int = 3, memory_cost: int = 65536, parallelism: int = 2, hash_len: int = 32)
BcryptPasswordHasher
¶
HS256JwtService
¶
Python
HS256JwtService(keypair: JwtKeyPair, issuer: str = 'apogee-auth-runtime', blacklist: Any | None = None, clock: Any | None = None)
JWT signer/verifier using HS256 with key rotation + optional blacklist.
Source code in apogee_auth_runtime/infrastructure/jwt/hs256_jwt_service.py
sign
¶
Python
sign(subject: str, ttl_s: int = 900, token_type: str = 'access', extra: dict[str, Any] | None = None) -> str
Source code in apogee_auth_runtime/infrastructure/jwt/hs256_jwt_service.py
Python
def sign(
self,
subject: str,
ttl_s: int = 900,
token_type: str = "access",
extra: dict[str, Any] | None = None,
) -> str:
if not subject:
raise ValueError("subject cannot be empty")
if ttl_s <= 0:
raise ValueError("ttl_s must be positive")
now = int(self._now())
payload: dict[str, Any] = {
"iss": self._issuer,
"sub": subject,
"iat": now,
"exp": now + ttl_s,
"jti": uuid.uuid4().hex,
"type": token_type,
}
if extra:
payload.update(extra)
return _hs256.encode(payload, self._keys.active_secret, kid=self._keys.active_kid)
verify
¶
Source code in apogee_auth_runtime/infrastructure/jwt/hs256_jwt_service.py
Python
def verify(self, token: str, expected_type: str | None = None) -> dict[str, Any]:
try:
header = _hs256.decode_unsafe_header(token)
except Exception as exc:
raise InvalidTokenError(f"malformed token: {exc}") from exc
kid = header.get("kid")
secret = self._keys.secret_for(kid) if kid else self._keys.active_secret
if secret is None:
raise InvalidTokenError(f"unknown kid: {kid!r}")
try:
payload = _hs256.decode(token, secret)
except Exception as exc:
raise InvalidTokenError(str(exc)) from exc
# exp check
exp = payload.get("exp")
if exp is not None and int(self._now()) >= int(exp):
raise InvalidTokenError("token expired")
if expected_type is not None and payload.get("type") != expected_type:
raise InvalidTokenError(
f"expected token_type={expected_type!r}, got {payload.get('type')!r}"
)
if self._blacklist is not None:
jti = payload.get("jti")
# blacklist may be sync (in-memory) or async (Redis).
# we accept both; sync path used here for verify().
is_revoked = self._blacklist.is_revoked_sync(jti) if hasattr(self._blacklist, "is_revoked_sync") else None
if is_revoked:
raise TokenRevokedError("token revoked")
return payload
revoke
async
¶
Source code in apogee_auth_runtime/infrastructure/jwt/hs256_jwt_service.py
Python
async def revoke(self, token: str) -> None:
if self._blacklist is None:
return
try:
payload = self.verify(token)
except InvalidTokenError:
return
jti = payload.get("jti")
if jti is None:
return
ttl_s = max(int(payload.get("exp", self._now())) - int(self._now()), 1)
await self._blacklist.add(jti, ttl_s)
InMemoryTokenBlacklist
¶
JoseJwtService
¶
Python
JoseJwtService(keypair: JwtKeyPair, algorithm: str = 'HS256', issuer: str = 'apogee-auth-runtime', blacklist: Any | None = None)
Wraps PyJWT (preferred) or joserfc.
Supports HS256 (default) and asymmetric algos via private/public keys
when caller passes them as active_secret/legacy_secrets.
Source code in apogee_auth_runtime/infrastructure/jwt/jose_jwt_service.py
sign
¶
Python
sign(subject: str, ttl_s: int = 900, token_type: str = 'access', extra: dict[str, Any] | None = None) -> str
Source code in apogee_auth_runtime/infrastructure/jwt/jose_jwt_service.py
Python
def sign(
self,
subject: str,
ttl_s: int = 900,
token_type: str = "access",
extra: dict[str, Any] | None = None,
) -> str:
import time
import uuid
pyjwt = self._ensure_jwt()
now = int(time.time())
payload: dict[str, Any] = {
"iss": self._issuer,
"sub": subject,
"iat": now,
"exp": now + ttl_s,
"jti": uuid.uuid4().hex,
"type": token_type,
}
if extra:
payload.update(extra)
return pyjwt.encode(
payload,
self._keys.active_secret,
algorithm=self._algorithm,
headers={"kid": self._keys.active_kid},
)
verify
¶
Source code in apogee_auth_runtime/infrastructure/jwt/jose_jwt_service.py
Python
def verify(self, token: str, expected_type: str | None = None) -> dict[str, Any]:
pyjwt = self._ensure_jwt()
try:
header = pyjwt.get_unverified_header(token)
except Exception as exc:
raise InvalidTokenError(str(exc)) from exc
kid = header.get("kid")
secret = self._keys.secret_for(kid) if kid else self._keys.active_secret
if secret is None:
raise InvalidTokenError(f"unknown kid: {kid!r}")
try:
payload = pyjwt.decode(
token, secret, algorithms=[self._algorithm], issuer=self._issuer
)
except Exception as exc:
raise InvalidTokenError(str(exc)) from exc
if expected_type is not None and payload.get("type") != expected_type:
raise InvalidTokenError(
f"expected token_type={expected_type!r}, got {payload.get('type')!r}"
)
return payload
revoke
async
¶
Source code in apogee_auth_runtime/infrastructure/jwt/jose_jwt_service.py
Python
async def revoke(self, token: str) -> None:
if self._blacklist is None:
return
try:
payload = self.verify(token)
except InvalidTokenError:
return
jti = payload.get("jti")
if jti is None:
return
import time
ttl_s = max(int(payload.get("exp", time.time())) - int(time.time()), 1)
await self._blacklist.add(jti, ttl_s)