Account OAuth Token Encryption
This document describes the implementation of encryption for OAuth tokens stored in the Account table.
Overview
The Account table stores OAuth provider credentials (access tokens and refresh tokens) for third-party authentication providers like Google and Facebook. These tokens must be encrypted at rest for security and compliance.
Implementation
1. Encryption Utilities
Location: /lib/utils/oauth-token-encryption.ts
This module provides wrapper functions for encrypting and decrypting OAuth tokens using the existing AES-256-GCM encryption infrastructure from /lib/utils/encryption.ts.
Key Functions:
encryptAccessToken(token, userId?)- Encrypts an OAuth access tokendecryptAccessToken(encrypted, userId?)- Decrypts an OAuth access tokenencryptRefreshToken(token, userId?)- Encrypts an OAuth refresh tokendecryptRefreshToken(encrypted, userId?)- Decrypts an OAuth refresh tokenisTokenEncrypted(token)- Checks if a token is already encryptedsafeEncryptToken(token, userId?)- Idempotent encryption (safe to call multiple times)
Features:
- AES-256-GCM Encryption: Industry-standard authenticated encryption
- Backward Compatibility: Gracefully handles plaintext legacy tokens during migration
- Idempotent: Safe to run encryption multiple times on the same data
- Error Handling: Comprehensive logging and error reporting
2. Prisma Middleware
Location: /lib/database/account-encryption-middleware.ts
This middleware automatically encrypts tokens on write operations and decrypts them on read operations, making the encryption transparent to application code.
Supported Operations:
create- Encrypts tokens when creating new Account recordsupdate- Encrypts tokens when updating Account recordsupsert- Encrypts tokens for both create and update pathscreateMany/updateMany- Encrypts tokens in batch operationsfindUnique/findFirst/findMany- Decrypts tokens on read
Registration:
The middleware is automatically registered in /lib/database.ts:
import { registerAccountEncryption } from './database/account-encryption-middleware';
if (typeof window === 'undefined') {
registerAccountEncryption(basePrisma);
}
3. Migration Script
Location: /scripts/migrations/encrypt-oauth-tokens.ts
This script encrypts existing plaintext tokens in the database.
Usage:
# Dry run (no changes)
npx tsx scripts/migrations/encrypt-oauth-tokens.ts
# Execute migration
npx tsx scripts/migrations/encrypt-oauth-tokens.ts --execute
Features:
- Dry-run Mode: Preview changes before executing
- Idempotent: Safe to run multiple times (skips already-encrypted tokens)
- Comprehensive Reporting: Shows detailed statistics of the migration
- Error Handling: Continues processing even if individual records fail
Security Features
Encryption Format
Tokens are encrypted using AES-256-GCM with the following format:
iv:authTag:encryptedContent
All components are base64-encoded and separated by colons.
Key Derivation
Encryption uses PBKDF2 key derivation with:
- Algorithm: SHA-256
- Iterations: 100,000 (high security)
- Key Length: 256 bits
- Salt: Configurable via
CREDENTIALS_ENCRYPTION_SALTenvironment variable
Environment Variables
Required:
CREDENTIALS_ENCRYPTION_KEY- Master encryption key (must be at least 32 characters)
Optional:
CREDENTIALS_ENCRYPTION_SALT- Custom salt for key derivation (defaults to 'petunia-encryption-salt')
Key Validation
The encryption system validates keys to prevent weak or test values:
- Minimum length: 32 characters
- Rejects common weak patterns (e.g., "password", "test", "demo")
- Rejects repeated characters
- Validates in production mode (warnings in development)
Migration Process
Prerequisites
-
Ensure
CREDENTIALS_ENCRYPTION_KEYis set in your environment:# Generate a secure key (example) openssl rand -base64 32 -
Set the key in your environment:
export CREDENTIALS_ENCRYPTION_KEY="your-secure-key-here"
Migration Steps
-
Dry Run - Test the migration without making changes:
npx tsx scripts/migrations/encrypt-oauth-tokens.ts -
Review Output - Check the migration report:
📊 Migration Summary ═══════════════════════════════════════════════════════════════════ Total Account records: 150 Already encrypted: 0 Newly encrypted: 150 Failed: 0 Skipped: 0 -
Execute - Run the actual migration:
npx tsx scripts/migrations/encrypt-oauth-tokens.ts --execute -
Verify - Check the results and ensure all tokens are encrypted
Rollback
If you need to rollback:
- The migration script does not modify the database structure, only data
- You can restore from a database backup taken before the migration
- The decryption functions handle both encrypted and plaintext tokens during the transition
Backward Compatibility
The implementation includes backward compatibility features:
- Plaintext Token Support: Decryption functions detect plaintext tokens and return them as-is
- Gradual Migration: Old plaintext tokens continue to work while migration is in progress
- Idempotent Encryption: Re-running encryption on already-encrypted data has no effect
Testing
Unit Tests
Location: /tests/lib/utils/oauth-token-encryption.test.ts
Run tests:
npm test -- tests/lib/utils/oauth-token-encryption.test.ts
Test Coverage:
- Encryption/decryption of access tokens
- Encryption/decryption of refresh tokens
- Backward compatibility with plaintext tokens
- Round-trip encryption/decryption
- Error handling (empty tokens, invalid formats)
- Idempotent encryption (safeEncryptToken)
Integration Tests
The middleware is automatically tested through any tests that interact with the Account model via Prisma.
Monitoring
The encryption utilities include comprehensive logging:
- Info: Migration progress, encryption operations
- Debug: Individual token encryption/decryption events
- Warn: Legacy plaintext tokens encountered
- Error: Encryption/decryption failures
All logs are sent through the application's logging infrastructure and can be monitored in production.
Performance Considerations
PBKDF2 Key Caching
The encryption utilities implement key caching to avoid repeated PBKDF2 derivations:
- Cache TTL: 5 minutes
- Cache Size: Limited to 10 entries (LRU)
- Impact: Reduces encryption overhead from ~50ms to <5ms per operation
Database Performance
- Middleware Overhead: Minimal (<1ms per token)
- Migration Performance: Processes tokens in sequence to avoid overwhelming the database
- Index Usage: Uses existing Account table indexes (no additional indexes required)
Compliance
This implementation supports:
- GDPR: Encryption of personal data at rest
- PCI DSS: Secure storage of authentication credentials
- SOC 2: Encryption controls for sensitive data
Future Enhancements
Potential improvements:
- Key Rotation: Implement automated key rotation support
- Tenant Isolation: Use HKDF-derived per-tenant keys (currently uses shared key)
- Hardware Security Modules (HSM): Support for HSM-backed encryption keys
- Audit Logging: Enhanced audit trail for token access
Troubleshooting
Common Issues
Issue: CREDENTIALS_ENCRYPTION_KEY not set in production environment
- Solution: Set the environment variable in your production deployment
Issue: Tokens appear as gibberish in database queries
- Expected: Tokens are encrypted. Use the application code to decrypt them.
Issue: Migration fails with "Failed to encrypt token"
- Check: Verify the encryption key is set correctly
- Check: Ensure database connection is stable
- Action: Re-run the migration (it's idempotent)
Issue: Performance degradation after enabling encryption
- Check: Verify key caching is working (check debug logs)
- Action: Monitor encryption metrics in production
Related Documentation
- Encryption Utilities - Core encryption infrastructure
- Database Middleware - Prisma middleware implementation
- Migration Scripts - Other data migration scripts
Questions?
For questions or issues, please:
- Check this documentation
- Review the code comments in the implementation files
- Contact the security team for compliance-related questions