|
| 1 | +# Caching |
| 2 | + |
| 3 | +The SDK caches OIDC discovery metadata and JWKS (JSON Web Key Sets) to avoid redundant network calls on every token verification. In MCD mode, each issuer domain gets its own cache entries. |
| 4 | + |
| 5 | +## Default Behavior |
| 6 | + |
| 7 | +By default, the SDK uses an in-memory LRU cache with: |
| 8 | + |
| 9 | +- **TTL**: 600 seconds (10 minutes), or the server's `Cache-Control: max-age` value - whichever is lower |
| 10 | +- **Max entries**: 100 per cache (discovery and JWKS caches are separate) |
| 11 | +- **Eviction**: Least Recently Used (LRU) when max entries is reached |
| 12 | + |
| 13 | +No configuration is needed for the default cache. It works well for single-server deployments. |
| 14 | + |
| 15 | +## Configuration |
| 16 | + |
| 17 | +### TTL and Max Entries |
| 18 | + |
| 19 | +```python |
| 20 | +from auth0_api_python import ApiClient, ApiClientOptions |
| 21 | + |
| 22 | +api_client = ApiClient(ApiClientOptions( |
| 23 | + domains=["tenant.auth0.com", "auth.example.com"], |
| 24 | + audience="https://api.example.com", |
| 25 | + cache_ttl_seconds=300, # 5 minutes max TTL |
| 26 | + cache_max_entries=50 # 50 entries per cache |
| 27 | +)) |
| 28 | +``` |
| 29 | + |
| 30 | +The effective TTL for each entry is `min(server_max_age, cache_ttl_seconds)`. Auth0 typically sends `Cache-Control: max-age=15` for discovery metadata, so the effective TTL will be 15 seconds even if you configure a higher value. |
| 31 | + |
| 32 | +### Custom Cache Adapter |
| 33 | + |
| 34 | +For distributed deployments (multiple servers, containers), use a shared cache backend by implementing `CacheAdapter`: |
| 35 | + |
| 36 | +```python |
| 37 | +import json |
| 38 | +from typing import Any, Optional |
| 39 | +from auth0_api_python import ApiClient, ApiClientOptions, CacheAdapter |
| 40 | + |
| 41 | +class RedisCache(CacheAdapter): |
| 42 | + def __init__(self, redis_client): |
| 43 | + self.redis = redis_client |
| 44 | + |
| 45 | + def get(self, key: str) -> Optional[Any]: |
| 46 | + value = self.redis.get(key) |
| 47 | + return json.loads(value) if value else None |
| 48 | + |
| 49 | + def set(self, key: str, value: Any, ttl_seconds: Optional[int] = None) -> None: |
| 50 | + serialized = json.dumps(value) |
| 51 | + if ttl_seconds: |
| 52 | + self.redis.set(key, serialized, ex=ttl_seconds) |
| 53 | + else: |
| 54 | + self.redis.set(key, serialized) |
| 55 | + |
| 56 | + def delete(self, key: str) -> None: |
| 57 | + self.redis.delete(key) |
| 58 | + |
| 59 | + def clear(self) -> None: |
| 60 | + # Be careful: this clears the entire Redis database |
| 61 | + self.redis.flushdb() |
| 62 | + |
| 63 | +# Usage |
| 64 | +import redis |
| 65 | +redis_client = redis.Redis(host="localhost", port=6379, db=0) |
| 66 | + |
| 67 | +api_client = ApiClient(ApiClientOptions( |
| 68 | + domains=["tenant.auth0.com", "auth.example.com"], |
| 69 | + audience="https://api.example.com", |
| 70 | + cache_adapter=RedisCache(redis_client) |
| 71 | +)) |
| 72 | +``` |
| 73 | + |
| 74 | +When a custom adapter is provided, both the discovery cache and JWKS cache use it. Cache keys are inherently distinct — discovery keys are normalized issuer URLs (e.g., `https://tenant.auth0.com/`) and JWKS keys are `jwks_uri` values (e.g., `https://tenant.auth0.com/.well-known/jwks.json`). |
| 75 | + |
| 76 | +## Tuning Recommendations |
| 77 | + |
| 78 | +### TTL |
| 79 | + |
| 80 | +- **Development**: Use a short TTL (e.g., `cache_ttl_seconds=10`) to pick up configuration changes quickly |
| 81 | +- **Production**: The default (600 seconds) is a reasonable upper bound. Auth0's `Cache-Control: max-age` headers will typically set a lower effective TTL |
| 82 | + |
| 83 | +### Max Entries |
| 84 | + |
| 85 | +Each issuer domain consumes **2 cache entries** (one for discovery metadata, one for JWKS). Size the cache based on the number of distinct issuers you expect: |
| 86 | + |
| 87 | +- **Static list with 3 domains**: `cache_max_entries=10` is more than enough |
| 88 | +- **Dynamic resolver with many issuers**: Set to `(expected_issuers * 2) + buffer` |
| 89 | + |
| 90 | +When the cache is full, the least recently used entry is evicted. A cache miss triggers a network fetch on the next verification for that issuer. |
| 91 | + |
| 92 | +## CacheAdapter API |
| 93 | + |
| 94 | +| Method | Signature | Description | |
| 95 | +|---|---|---| |
| 96 | +| `get` | `(key: str) -> Optional[Any]` | Return cached value or `None` if not found / expired | |
| 97 | +| `set` | `(key: str, value: Any, ttl_seconds: Optional[int]) -> None` | Store value with optional TTL | |
| 98 | +| `delete` | `(key: str) -> None` | Remove a single entry | |
| 99 | +| `clear` | `() -> None` | Remove all entries | |
| 100 | + |
| 101 | +All methods are synchronous. The `value` passed to `set` is a dictionary (parsed JSON from Auth0's OIDC and JWKS endpoints). |
0 commit comments