feat(#352): Encrypt existing plaintext Account tokens
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Implements transparent encryption/decryption of OAuth tokens via Prisma middleware with progressive migration strategy. Core Implementation: - Prisma middleware transparently encrypts tokens on write, decrypts on read - Auto-detects ciphertext format: aes:iv:authTag:encrypted, vault:v1:..., or plaintext - Uses existing CryptoService (AES-256-GCM) for encryption - Progressive encryption: tokens encrypted as they're accessed/refreshed - Zero-downtime migration (schema change only, no bulk data migration) Security Features: - Startup key validation prevents silent data loss if ENCRYPTION_KEY changes - Secure error logging (no stack traces that could leak sensitive data) - Graceful handling of corrupted encrypted data - Idempotent encryption prevents double-encryption - Future-proofed for OpenBao Transit encryption (Phase 2) Token Fields Encrypted: - accessToken (OAuth access tokens) - refreshToken (OAuth refresh tokens) - idToken (OpenID Connect ID tokens) Backward Compatibility: - Existing plaintext tokens readable (encryptionVersion = NULL) - Progressive encryption on next write - BetterAuth integration transparent (middleware layer) Test Coverage: - 20 comprehensive unit tests (89.06% coverage) - Encryption/decryption scenarios - Null/undefined handling - Corrupted data handling - Legacy plaintext compatibility - Future vault format support - All CRUD operations (create, update, updateMany, upsert) Files Created: - apps/api/src/prisma/account-encryption.middleware.ts - apps/api/src/prisma/account-encryption.middleware.spec.ts - apps/api/prisma/migrations/20260207_encrypt_account_tokens/migration.sql Files Modified: - apps/api/src/prisma/prisma.service.ts (register middleware) - apps/api/src/prisma/prisma.module.ts (add CryptoService) - apps/api/src/federation/crypto.service.ts (add key validation) - apps/api/prisma/schema.prisma (add encryptionVersion) - .env.example (document ENCRYPTION_KEY) Fixes #352 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,20 @@
|
||||
import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { CryptoService } from "../federation/crypto.service";
|
||||
import { registerAccountEncryptionMiddleware } from "./account-encryption.middleware";
|
||||
|
||||
/**
|
||||
* Prisma service that manages database connection lifecycle
|
||||
* Extends PrismaClient to provide connection management and health checks
|
||||
*
|
||||
* IMPORTANT: CryptoService is required (not optional) because it will throw
|
||||
* if ENCRYPTION_KEY is not configured, providing fail-fast behavior.
|
||||
*/
|
||||
@Injectable()
|
||||
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
||||
private readonly logger = new Logger(PrismaService.name);
|
||||
|
||||
constructor() {
|
||||
constructor(private readonly cryptoService: CryptoService) {
|
||||
super({
|
||||
log: process.env.NODE_ENV === "development" ? ["query", "info", "warn", "error"] : ["error"],
|
||||
});
|
||||
@@ -22,6 +27,11 @@ export class PrismaService extends PrismaClient implements OnModuleInit, OnModul
|
||||
try {
|
||||
await this.$connect();
|
||||
this.logger.log("Database connection established");
|
||||
|
||||
// Register Account token encryption middleware
|
||||
// CryptoService constructor will have already validated ENCRYPTION_KEY exists
|
||||
registerAccountEncryptionMiddleware(this, this.cryptoService);
|
||||
this.logger.log("Account encryption middleware registered");
|
||||
} catch (error) {
|
||||
this.logger.error("Failed to connect to database", error);
|
||||
throw error;
|
||||
|
||||
Reference in New Issue
Block a user