1- """
2- Token storage for tokens the SDK itself mints (e.g. On Behalf Of exchanges),
3- distinct from CacheAdapter which only caches OIDC discovery metadata and JWKS.
4- """
1+ """Token storage for SDK-minted tokens (OBO, M2M, Token Vault), distinct from CacheAdapter."""
52
63from abc import ABC , abstractmethod
74from collections .abc import Mapping
@@ -24,43 +21,14 @@ class TokenSet(_TokenSetRequired, total=False):
2421
2522@dataclass (frozen = True )
2623class VerifiedToken :
27- """An access token and the claims from verifying it.
28-
29- The claims must come from verify_access_token, since they are trusted to build the
30- cache identity.
31- """
24+ """Access token with verified claims from verify_access_token, trusted to build the cache key."""
3225
3326 access_token : str
3427 claims : Mapping [str , Any ]
3528
3629
3730class AbstractTokenStore (ABC ):
38- """
39- Base class for token stores that persist exchanged tokens outside process memory
40- (e.g. Redis, Memcached, a database). Requires a secret at construction and provides
41- encrypt/decrypt helpers so subclasses can protect tokens at rest without having to
42- implement the encryption themselves.
43-
44- Example:
45- class RedisTokenStore(AbstractTokenStore):
46- def __init__(self, redis_client, *, secret: str):
47- super().__init__(secret=secret)
48- self.redis = redis_client
49-
50- async def get(self, key: str) -> Optional[TokenSet]:
51- raw = await self.redis.get(key)
52- if raw is None:
53- return None
54- return self.decrypt(key, raw)
55-
56- async def set(self, key: str, value: TokenSet) -> None:
57- encrypted = self.encrypt(key, value)
58- ttl = max(value["expires_at"] - int(time.time()), 0)
59- await self.redis.set(key, encrypted, ex=ttl)
60-
61- async def delete(self, key: str) -> None:
62- await self.redis.delete(key)
63- """
31+ """Base class for external token stores with built-in JWE encrypt and decrypt helpers."""
6432
6533 def __init__ (self , * , secret : str ) -> None :
6634 self ._secret = secret
@@ -98,16 +66,7 @@ class TokenIndexMember(TypedDict):
9866
9967
10068class IndexedTokenStore (AbstractTokenStore ):
101- """
102- A token store that maintains a token index safely under concurrent writes and prunes it by expiry.
103-
104- Needed for any cache layout that keeps several tokens per principal and reuses one whose
105- granted scopes cover a request. A plain AbstractTokenStore can only maintain such an index by
106- reading it, adding to it, and writing it back, which loses a concurrent addition made in
107- between. Implementations back the index with a structure that adds one member atomically and
108- drops members once they expire, for example a Redis sorted set scored by each member's
109- expiry. Adding a short-lived member must not shorten the whole index's lifetime.
110- """
69+ """Token store variant that maintains a scope index with atomic member writes to avoid concurrent-add races."""
11170
11271 @abstractmethod
11372 async def add_index_member (self , index_key : str , member : TokenIndexMember ) -> None :
0 commit comments