Why Use Access Tokens?
While every request could require payment, this creates poor UX and high latency due to on-chain verification. Access tokens solve this by issuing a reusable credential after the first payment.
Token Flow
- Client pays for access
- Server verifies payment and issues token
- Client stores token
- Subsequent requests use token instead of payment
- Token expires, client pays again
Issuing Tokens
token-issuer.ts
import { VantaTokenIssuer } from '@vanta/middleware'
const tokenIssuer = new VantaTokenIssuer({
secret: process.env.TOKEN_SECRET!,
expiresIn: '1h', // Token lifetime
algorithm: 'HS256', // or 'RS256' for RSA
issuer: 'api.example.com',
})
// In payment handler
onPaymentVerified: async (payment, req, res) => {
const token = await tokenIssuer.issue(req, {
paymentId: payment.transactionHash,
amount: payment.amount,
tier: 'premium',
})
res.setHeader('X-Vanta-Token', token)
}Token Structure
// Decoded token payload
{
"sub": "vnt_tk_abc123", // Token ID
"iat": 1704067200, // Issued at
"exp": 1704070800, // Expires at
"iss": "api.example.com", // Issuer
"paymentId": "0x1234...", // Payment tx hash
"amount": "0.01", // Amount paid
"tier": "premium", // Custom claims
"resource": "/api/premium/*" // Scope
}Validating Tokens
validate.ts
const tokenMiddleware = tokenIssuer.middleware({
// Fall back to payment if token invalid/expired
fallback: paymentMiddleware,
// Custom validation
validate: async (claims) => {
// Check custom business logic
return claims.tier === 'premium'
},
})
app.use('/api/premium', tokenMiddleware)Client Token Handling
client.ts
const client = new VantaClient({
provider,
tokenStorage: {
get: (key) => localStorage.getItem(`vanta_${key}`),
set: (key, token) => localStorage.setItem(`vanta_${key}`, token),
remove: (key) => localStorage.removeItem(`vanta_${key}`),
},
})
// Client automatically:
// 1. Checks for valid cached token
// 2. Uses token if available
// 3. Pays and gets new token if expiredSecurity
Store tokens securely. In browsers, consider httpOnly cookies for sensitive applications. Always use HTTPS in production.
Token Refresh
Tokens can be refreshed before expiry without requiring a new payment:
// Server-side refresh endpoint
app.post('/api/refresh-token', tokenMiddleware, async (req, res) => {
const newToken = await tokenIssuer.refresh(req.vanta.token)
res.json({ token: newToken })
})