openkitx403 — Python Server SDK
FastAPI middleware for OpenKitx403 wallet-based authentication. Add Solana wallet verification to any API endpoint with one line.
🚀 Installation
pip install openkitx403
# or
poetry add openkitx403
⚡ Quick Start
from fastapi import FastAPI, Depends
from openkitx403 import OpenKit403Middleware, require_openkitx403_user
app = FastAPI(title="My Wallet-Protected API")
# Attach OpenKitx403 middleware
app.add_middleware(
OpenKit403Middleware,
audience="https://api.example.com",
issuer="my-api-v1",
ttl_seconds=60,
bind_method_path=False, # Set True to bind challenges to specific paths
origin_binding=False,
replay_backend="memory"
)
@app.get("/protected")
async def protected(user = Depends(require_openkitx403_user)):
"""Example protected endpoint"""
return {
"message": f"Authenticated as {user.address}",
"wallet": user.address
}
@app.get("/public")
async def public():
"""Public endpoint - no authentication required"""
return {"message": "Hello World"}
🔒 Optional Token Gating
from openkitx403 import OpenKit403Middleware
from solana.rpc.api import Client
from solders.pubkey import Pubkey
solana_client = Client("https://api.mainnet-beta.solana.com")
async def check_token_holder(address: str) -> bool:
"""Example: verify wallet holds specific token"""
try:
pubkey = Pubkey.from_string(address)
resp = solana_client.get_token_accounts_by_owner(
pubkey,
opts={"mint": str(Pubkey.from_string("YOUR_TOKEN_MINT"))}
)
return len(resp.value) > 0
except Exception as e:
print("Token check failed:", e)
return False
app.add_middleware(
OpenKit403Middleware,
audience="https://api.example.com",
issuer="my-api-v1",
token_gate=check_token_holder,
)
🛤️ Exclude Paths from Authentication
app.add_middleware(
OpenKit403Middleware,
audience="https://api.example.com",
issuer="my-api-v1",
excluded_paths=["/health", "/docs", "/openapi.json"]
)
🧩 API Reference
OpenKit403Middleware
FastAPI middleware that adds OpenKitx403 authentication to your routes.
Configuration Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
audience |
str |
required | Expected API audience/origin |
issuer |
str |
required | Server identifier |
ttl_seconds |
int |
60 |
Challenge TTL in seconds |
clock_skew_seconds |
int |
120 |
Allowed clock skew for timestamp validation |
bind_method_path |
bool |
False |
Bind challenge to specific HTTP method/path |
origin_binding |
bool |
False |
Enable origin header validation |
ua_binding |
bool |
False |
Enable user-agent header validation |
replay_backend |
str |
"memory" |
Replay protection backend |
token_gate |
Callable[[str], bool] |
None |
Optional async function for wallet gating |
excluded_paths |
list[str] |
None |
Paths that bypass authentication |
🔐 Token Gate Function Signature
Your token gate function must:
- Accept a single
address: strparameter (base58-encoded public key) - Return
bool(True = allowed, False = denied) - Can be async or sync
async def my_token_gate(address: str) -> bool:
# Your validation logic
return True # or False
🎯 Accessing User Information
Use the require_openkitx403_user dependency to access authenticated user:
from fastapi import Depends
from openkitx403 import require_openkitx403_user, OpenKit403User
@app.get("/profile")
async def profile(user: OpenKit403User = Depends(require_openkitx403_user)):
return {
"address": user.address,
"challenge": user.challenge # Original challenge object
}
🔧 Custom Replay Store
By default, middleware uses in-memory replay protection. For production with multiple servers:
from openkitx403 import ReplayStore
class RedisReplayStore:
"""Example Redis-based replay store"""
def __init__(self, redis_client):
self.redis = redis_client
async def check(self, key: str, ttl_seconds: int) -> bool:
return await self.redis.exists(key)
async def store(self, key: str, ttl_seconds: int) -> None:
await self.redis.setex(key, ttl_seconds, "1")
# Use custom store
app.add_middleware(
OpenKit403Middleware,
audience="https://api.example.com",
issuer="my-api-v1",
replay_store=RedisReplayStore(redis_client)
)
🔑 Dependencies
fastapi- Web frameworkstarlette- ASGI toolkitsolders- Solana SDK (for types)base58- Base58 encodingpynacl- Ed25519 signature verification
📚 Documentation
🪪 License
Release files for openkitx403 0.1.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| openkitx403-0.1.5.tar.gz | 8.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| openkitx403-0.1.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.1 kB
Release files / openkitx403-0.1.5.tar.gz
| Download URL | openkitx403-0.1.5.tar.gz |
|---|---|
| Size | 8.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e8fd34d5f37b31c4c39ceecca200db5da4d42e2c6f5effd09faf7a4b01207549
|
|
BLAKE2b-256 checksum How to use checksums |
af8f523377cb7ce0afb5e745dc965e45bd71b2acefe4ef5a44f89ffc9696d418
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.2.1 CPython/3.13.6 Windows/11
|
Release files / openkitx403-0.1.5-py3-none-any.whl
| Download URL | openkitx403-0.1.5-py3-none-any.whl |
|---|---|
| Size | 9.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bb370f2041b2000d137ac3e3146f82e7564626e991ac5161b05e9d105a32f4a3
|
|
BLAKE2b-256 checksum How to use checksums |
fe6af0faedd0954f28822bacfa03382159ae9a814aff9e4b2325147d6763d729
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.2.1 CPython/3.13.6 Windows/11
|