HTTP-native wallet authentication protocol for Solana.
OpenKitx403 is an open-source, TypeScript-first protocol that standardizes HTTP 403 as the semantic "prove you control this wallet" challenge for Solana wallet authentication.
HTTP-native wallet authentication for Solana
- HTTP-native: Uses standard HTTP 403 challenges
- Stateless: No server-side sessions required
- Secure: Ed25519 signature verification with replay protection
- Easy to use: Drop-in middleware for Express, Fastify, and FastAPI
- Production-ready: Full TypeScript and Python SDKs
- AI-friendly: LangChain integration and agent support
- Token-gating ready: Built-in support for NFT/token requirements
npm install @openkitx403/client
npm install @openkitx403/server
pip install openkitx403
or
poetry add openkitx403
import { OpenKit403Client } from '@openkitx403/client';
const client = new OpenKit403Client();
// Connect wallet
await client.connect('phantom');
// Authenticate with API
const response = await client.authenticate({
resource: 'https://api.example.com/protected',
method: 'GET'
});
if (response.ok) {
const data = await response.json();
console.log('β
Authenticated as:', client.getAddress());
console.log('Data:', data);
} else {
console.error('β Authentication failed:', response.status);
}
import express from 'express';
import { createOpenKit403, inMemoryLRU } from '@openkitx403/server';
const app = express();
const openkit = createOpenKit403({
issuer: 'my-api-v1',
audience: 'https://api.example.com',
ttlSeconds: 60,
bindMethodPath: true,
replayStore: inMemoryLRU()
});
app.use(openkit.middleware());
app.get('/protected', (req, res) => {
const user = req.openkitx403User;
res.json({ message: 'Hello!', wallet: user.address });
});
app.listen(3000);
from fastapi import FastAPI, Depends
from openkitx403 import OpenKit403Middleware, require_openkitx403_user
app = FastAPI()
app.add_middleware(
OpenKit403Middleware,
audience="https://api.example.com",
issuer="my-api-v1",
ttl_seconds=60,
bind_method_path=True,
replay_backend="memory"
)
@app.get("/protected")
async def protected(user=Depends(require_openkitx403_user)):
return {"message": "Hello!", "wallet": user.address}
- Client requests a protected resource β 403 Forbidden
- Server responds with
WWW-Authenticate: OpenKitx403 ...header containing a challenge - Client asks user's Solana wallet (Phantom/Backpack/Solflare) to sign the challenge
- Client re-sends request with
Authorization: OpenKitx403 ...header - Server verifies signature and grants access β 200 OK
Client Server | | | GET /protected | |----------------------------->| | | | 403 + Challenge | |<-----------------------------| | | | [User signs with wallet] | | | | GET /protected + Auth | |----------------------------->| | | | 200 OK + Data | |<-----------------------------|
- Short-lived challenges (60s default TTL)
- Replay protection via nonce store
- Method/path binding prevents cross-endpoint replay
- Origin/User-Agent binding (optional)
- Clock skew tolerance (Β±120s default)
- Token-gating support for NFT/SPL token requirements
- Complete Usage Examples - All use cases with code
- Protocol Specification - RFC-style spec
- Internals Guide - Implementation details
- Security Model - Threat model and mitigations
| Wallet | Browser | Mobile | Status |
|---|---|---|---|
| Phantom | β | Supported | |
| Backpack | β | β | Supported |
| Solflare | β | Supported |
OpenKitx403 supports autonomous agents and LangChain tools:
import { SolanaWalletAuthTool } from '@openkitx403/langchain';
import { initializeAgentExecutorWithOptions } from 'langchain/agents';
const tools = [new SolanaWalletAuthTool()];
const executor = await initializeAgentExecutorWithOptions(tools, model, {
agentType: "zero-shot-react-description"
});
const result = await executor.call({
input: "Connect my wallet and fetch my NFT collection"
});
See USAGE_EXAMPLES.md for complete examples.
openkitx403/ βββ packages/ β βββ ts-client/ # Browser & Node.js SDK β βββ ts-server/ # Express & Fastify middleware β βββ py-server/ # FastAPI middleware β βββ examples/ # Demo applications βββ docs/ # Protocol specification βββ tests/ # Test suites βββ USAGE_EXAMPLES.md # Complete usage guide
Install dependencies
npm install
Build all packages
npm run build
Run tests
npm run test
npm run test --workspace=packages/ts-client
npm run test --workspace=packages/ts-server
Python tests
cd packages/py-server
pytest
const openkit = createOpenKit403({
issuer: 'my-api',
audience: 'https://api.example.com',
tokenGate: async (address: string) => {
// Check if wallet holds required NFT/token
const hasToken = await checkTokenHolding(address);
return hasToken;
}
});
class RedisReplayStore implements ReplayStore {
async check(key: string, ttl: number): Promise<boolean> {
return await redis.exists(key);
}
async store(key: string, ttl: number): Promise<void> {
await redis.setex(key, ttl, '1');
}
}
const openkit = createOpenKit403({
replayStore: new RedisReplayStore()
});
Contributions welcome! Please read CONTRIBUTING.md first.
MIT License - see LICENSE
Inspired by the "HTTP-native + wallet + open" philosophy, OpenKitx403 is built from scratch with:
- Different header names and message format
- Enhanced security model
- Production-grade implementations
- Comprehensive documentation
- GitHub: https://github.com/openkitx403/openkitx403
- Documentation: https://openkitx403.github.io/openkitx403-docs/
- Twitter: https://x.com/openkitx403dev
- Email: support@openkitx403.dev
- Issues: GitHub Issues
Built with β€οΈ for the Solana ecosystem