feat(phase-2): workstream 1 — HashiCorp Vault credential storage
Vault is optional — server falls back to bcrypt (Phase 1 behaviour) when VAULT_ADDR is not set. Full coexistence: existing bcrypt credentials continue to work until rotated. Changes: - src/vault/VaultClient.ts — wraps node-vault KV v2; writeSecret, readSecret, verifySecret (constant-time), deleteSecret - src/db/migrations/005_add_vault_path.sql — vault_path column on credentials - CredentialRepository — createWithVaultPath, updateVaultPath methods - CredentialService — routes generate/rotate through Vault when configured; bcrypt path unchanged - OAuth2Service — verifies via Vault when vaultPath set, bcrypt otherwise - src/app.ts — createVaultClientFromEnv() wired into service layer - ICredentialRow — vaultPath field added - docs/devops/environment-variables.md — VAULT_ADDR, VAULT_TOKEN, VAULT_MOUNT - docs/devops/vault-setup.md — dev quickstart, production config, migration guide - tests: 33/33 unit tests pass (VaultClient + CredentialService Vault path) - node-vault + @types/node-vault installed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
14
src/app.ts
14
src/app.ts
@@ -33,6 +33,7 @@ import { createCredentialsRouter } from './routes/credentials.js';
|
||||
import { createAuditRouter } from './routes/audit.js';
|
||||
|
||||
import { errorHandler } from './middleware/errorHandler.js';
|
||||
import { createVaultClientFromEnv } from './vault/VaultClient.js';
|
||||
import { RedisClientType } from 'redis';
|
||||
|
||||
/**
|
||||
@@ -86,12 +87,22 @@ export async function createApp(): Promise<Application> {
|
||||
const tokenRepo = new TokenRepository(pool, redis as RedisClientType);
|
||||
const auditRepo = new AuditRepository(pool);
|
||||
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
// Optional integrations
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
// Vault is optional. When VAULT_ADDR + VAULT_TOKEN are set, new credentials
|
||||
// are stored in Vault KV v2. When not set, bcrypt is used (Phase 1 behaviour).
|
||||
const vaultClient = createVaultClientFromEnv();
|
||||
if (vaultClient !== null) {
|
||||
console.log('[AgentIdP] Vault integration enabled — new credentials will use Vault KV v2');
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
// Service layer
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
const auditService = new AuditService(auditRepo);
|
||||
const agentService = new AgentService(agentRepo, credentialRepo, auditService);
|
||||
const credentialService = new CredentialService(credentialRepo, agentRepo, auditService);
|
||||
const credentialService = new CredentialService(credentialRepo, agentRepo, auditService, vaultClient);
|
||||
|
||||
const privateKey = process.env['JWT_PRIVATE_KEY'];
|
||||
const publicKey = process.env['JWT_PUBLIC_KEY'];
|
||||
@@ -106,6 +117,7 @@ export async function createApp(): Promise<Application> {
|
||||
auditService,
|
||||
privateKey,
|
||||
publicKey,
|
||||
vaultClient,
|
||||
);
|
||||
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user