Trust Tokens (PASETO)
Trust Tokens are cryptographically secure, single-use tokens using PASETO v4 with Ed25519 signatures for sensitive operations like email verification, password reset, and invitations.
What are Trust Tokens?
Trust Tokens are NOT session tokens. They are:
- ✅ Single-use tokens for specific actions
- ✅ Cryptographically signed with Ed25519
- ✅ Time-limited (expire after use or timeout)
- ✅ Validated with 6 security layers
Use Cases
1. Email Verification
typescript
// Generate verification token
const token = await trustTokens.generate({
type: 'email_verification',
userId: user.id,
email: user.email,
expiresIn: 86400 // 24 hours
});
// Send email
await sendEmail({
to: user.email,
subject: 'Verify your email',
body: `Click here: https://app.com/verify?token=${token}`
});
// Verify token
const payload = await trustTokens.verify(token, 'email_verification');
if (payload) {
await db.updateTable('users')
.set({ email_verified: true })
.where('id', '=', payload.userId)
.execute();
}2. Password Reset
typescript
// Generate reset token
const token = await trustTokens.generate({
type: 'password_reset',
userId: user.id,
email: user.email,
expiresIn: 3600 // 1 hour
});
// Send email
await sendEmail({
to: user.email,
subject: 'Reset your password',
body: `Reset link: https://app.com/reset?token=${token}`
});
// Verify and reset
const payload = await trustTokens.verify(token, 'password_reset');
if (payload) {
await db.updateTable('users')
.set({ password: hashedPassword })
.where('id', '=', payload.userId)
.execute();
// Invalidate token
await trustTokens.invalidate(token);
}3. Organization Invitations
typescript
// Generate invitation token
const token = await trustTokens.generate({
type: 'org_invitation',
organizationId: org.id,
email: inviteeEmail,
role: 'member',
expiresIn: 604800 // 7 days
});
// Send invitation
await sendEmail({
to: inviteeEmail,
subject: 'Join our organization',
body: `Accept invitation: https://app.com/join?token=${token}`
});
// Accept invitation
const payload = await trustTokens.verify(token, 'org_invitation');
if (payload) {
await db.insertInto('org_members')
.values({
organization_id: payload.organizationId,
user_id: currentUser.id,
role: payload.role
})
.execute();
await trustTokens.invalidate(token);
}PASETO vs JWT
| Feature | PASETO | JWT |
|---|---|---|
| Algorithm | Ed25519 (fixed) | Multiple (configurable) |
| Security | ✅ Secure by default | ⚠️ Depends on config |
| Simplicity | ✅ Simple | ⚠️ Complex |
| Vulnerabilities | ✅ None known | ⚠️ Many (alg:none, etc.) |
| Performance | ✅ Fast | ✅ Fast |
Why PASETO?
- No algorithm confusion attacks
- No "alg:none" vulnerability
- Simpler API
- Secure by default
6 Security Layers
Trust Tokens are validated through 6 layers:
- PASETO Signature - Ed25519 cryptographic verification
- Expiration Check - Token must not be expired
- Redis Status - Token must not be invalidated
- Database Status - Token must exist in database
- User Ownership - User must own the resource
- Resource Validation - Resource must exist and be valid
Implementation
Node.js/TypeScript
typescript
import { TrustTokens } from './lib/trust-tokens';
const trustTokens = new TrustTokens({
secretKey: process.env.PASETO_SECRET_KEY,
redisUrl: process.env.REDIS_URL
});
// Generate token
const token = await trustTokens.generate({
type: 'email_verification',
userId: 'user_123',
email: 'user@example.com',
expiresIn: 86400
});
// Verify token
const payload = await trustTokens.verify(token, 'email_verification');
// Invalidate token
await trustTokens.invalidate(token);Go
go
import "github.com/pubflow/flowfull-go/trusttokens"
tt := trusttokens.New(trusttokens.Config{
SecretKey: os.Getenv("PASETO_SECRET_KEY"),
RedisURL: os.Getenv("REDIS_URL"),
})
// Generate
token, err := tt.Generate(trusttokens.Payload{
Type: "email_verification",
UserID: "user_123",
ExpiresIn: 86400,
})
// Verify
payload, err := tt.Verify(token, "email_verification")Python
python
from flowfull import TrustTokens
tt = TrustTokens(
secret_key=os.getenv("PASETO_SECRET_KEY"),
redis_url=os.getenv("REDIS_URL")
)
# Generate
token = await tt.generate(
type="email_verification",
user_id="user_123",
expires_in=86400
)
# Verify
payload = await tt.verify(token, "email_verification")Configuration
env
# PASETO Secret Key (Ed25519)
PASETO_SECRET_KEY=your-secret-key-here
# Redis for token invalidation
REDIS_URL=redis://localhost:6379
# Token expiration (seconds)
EMAIL_VERIFICATION_TTL=86400 # 24 hours
PASSWORD_RESET_TTL=3600 # 1 hour
INVITATION_TTL=604800 # 7 daysBest Practices
✅ Do
- Use short expiration times
- Invalidate tokens after use
- Store tokens in database
- Use Redis for invalidation
- Validate token type
❌ Don't
- Reuse tokens
- Use long expiration times
- Skip invalidation
- Use for sessions
- Hardcode secret keys
Token Types
Common token types:
email_verification- Email verificationpassword_reset- Password resetorg_invitation- Organization invitationsmagic_link- Passwordless loginapi_access- API access tokenswebhook_signature- Webhook signatures
Next Steps
- Complete Trust Tokens Guide - Advanced patterns
- Auth Middleware - Protect routes
- HybridCache - Cache tokens
Need Help?
- 🌐 Notside.com - Professional security implementation
- 📧 Email: contact@notside.com