Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,65 @@ async def verify_dpop_token(access_token, dpop_proof, http_method, http_url):
"proof_claims": proof_claims
}
```

## Anonymous Callers

Anonymous Sessions give a visitor an Auth0 identity before they log in. The access token issued for an anonymous session is a standard Auth0 Bearer JWT, so this SDK validates it like any other token. The one difference is the `sub` claim, which starts with `anon@`.

An anonymous token passes verification by default. To treat anonymous callers differently, or block them, check the `sub` claim after verifying the token. The SDK does not make that authorization decision for you.

### Serve everyone, branch in the handler

```python
import asyncio
from auth0_api_python import ApiClient, ApiClientOptions

async def handle_cart(headers):
api_client = ApiClient(ApiClientOptions(
domain="your-tenant.auth0.com",
audience="https://api.example.com"
))

claims = await api_client.verify_request(headers=headers)
is_anonymous = claims.get("sub", "").startswith("anon@")

if is_anonymous:
return {"cart": load_guest_cart(claims["sub"])}
return {"cart": load_user_cart(claims["sub"])}
```

### Block anonymous callers on a specific route

```python
async def handle_checkout(headers):
api_client = ApiClient(ApiClientOptions(
domain="your-tenant.auth0.com",
audience="https://api.example.com"
))

claims = await api_client.verify_request(headers=headers)
if claims.get("sub", "").startswith("anon@"):
raise PermissionError("Anonymous callers are not allowed on this route")

return {"order": create_order(claims["sub"])}
```

### Block anonymous callers everywhere

The SDK has no global "reject anonymous" switch. Centralize the check in whatever shared layer your framework uses for auth (middleware, a FastAPI dependency, a decorator).

```python
async def require_logged_in_user(headers):
api_client = ApiClient(ApiClientOptions(
domain="your-tenant.auth0.com",
audience="https://api.example.com"
))

claims = await api_client.verify_request(headers=headers)
if claims.get("sub", "").startswith("anon@"):
raise PermissionError("Anonymous callers are not allowed")
return claims
```

> [!NOTE]
> The `anon@` prefix on `sub` is the only signal that distinguishes an anonymous caller from a logged-in user.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,20 @@ For hybrid mode (migration scenarios), resolver patterns, error handling, and ca
- **[Multi-Custom Domain Guide](docs/MultipleCustomDomain.md)** - Configuration modes, resolver patterns, migration, error handling
- **[Caching Guide](docs/Caching.md)** - Cache tuning, custom adapters (Redis, Memcached)

### 8. Anonymous Sessions

[Anonymous Sessions](https://auth0.com/docs) give a visitor an Auth0 identity before they log in. The access token issued for an anonymous session is a standard Auth0 Bearer JWT, so `verify_access_token()` and `verify_request()` validate it exactly like any other token. The only difference is the `sub` claim, which starts with `anon@`.

An anonymous token passes verification by default. If a route must not serve anonymous callers, check the `sub` claim after verification and reject it in your handler:

```python
claims = await api_client.verify_request(headers=headers)
if claims.get("sub", "").startswith("anon@"):
raise PermissionError("Anonymous callers are not allowed on this route")
```

Deciding whether an anonymous caller is authorized is your application's responsibility. See [Anonymous Callers](EXAMPLES.md#anonymous-callers) for allow, block-per-route, and block-globally patterns.

## Feedback

### Contributing
Expand Down