Skip to content

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

FeaturePASETOJWT
AlgorithmEd25519 (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:

  1. PASETO Signature - Ed25519 cryptographic verification
  2. Expiration Check - Token must not be expired
  3. Redis Status - Token must not be invalidated
  4. Database Status - Token must exist in database
  5. User Ownership - User must own the resource
  6. 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 days

Best 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 verification
  • password_reset - Password reset
  • org_invitation - Organization invitations
  • magic_link - Passwordless login
  • api_access - API access tokens
  • webhook_signature - Webhook signatures

Next Steps

Need Help?

Released under the MIT License.