feat(phase-3): workstream 3 — OpenID Connect (OIDC) Provider
Implements full OIDC layer on top of the existing OAuth 2.0 token service: - Migration 014: oidc_keys table (RSA/EC key pairs, is_current flag, expires_at for rotation grace period) - OIDCKeyService: key generation (RS256/ES256), Vault storage, JWKS with Redis cache, key rotation with grace period, pruneExpiredKeys - IDTokenService: buildIDTokenClaims (agent claims, nonce, DID), signIDToken (kid in JWT header), verifyIDToken (alg:none rejected, RS256/ES256 only) - OIDCController: discovery document, JWKS (Cache-Control), /agent-info - OIDC routes mounted at / — /.well-known/openid-configuration, /.well-known/jwks.json, /agent-info - OAuth2Service: id_token appended to token response when openid scope requested - 473 unit tests passing (100% OIDCKeyService stmts, 95.91% IDTokenService stmts) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
14
src/app.ts
14
src/app.ts
@@ -24,6 +24,8 @@ import { CredentialService } from './services/CredentialService.js';
|
||||
import { OAuth2Service } from './services/OAuth2Service.js';
|
||||
import { OrgService } from './services/OrgService.js';
|
||||
import { DIDService } from './services/DIDService.js';
|
||||
import { OIDCKeyService } from './services/OIDCKeyService.js';
|
||||
import { IDTokenService } from './services/IDTokenService.js';
|
||||
|
||||
import { AgentController } from './controllers/AgentController.js';
|
||||
import { TokenController } from './controllers/TokenController.js';
|
||||
@@ -31,6 +33,7 @@ import { CredentialController } from './controllers/CredentialController.js';
|
||||
import { AuditController } from './controllers/AuditController.js';
|
||||
import { OrgController } from './controllers/OrgController.js';
|
||||
import { DIDController } from './controllers/DIDController.js';
|
||||
import { OIDCController } from './controllers/OIDCController.js';
|
||||
|
||||
import { createAgentsRouter } from './routes/agents.js';
|
||||
import { createTokenRouter } from './routes/token.js';
|
||||
@@ -40,6 +43,7 @@ import { createHealthRouter } from './routes/health.js';
|
||||
import { createMetricsRouter } from './routes/metrics.js';
|
||||
import { createOrgsRouter } from './routes/organizations.js';
|
||||
import { createDIDRouter } from './routes/did.js';
|
||||
import { createOIDCRouter } from './routes/oidc.js';
|
||||
|
||||
import { errorHandler } from './middleware/errorHandler.js';
|
||||
import { createOpaMiddleware } from './middleware/opa.js';
|
||||
@@ -132,6 +136,11 @@ export async function createApp(): Promise<Application> {
|
||||
throw new Error('JWT_PRIVATE_KEY and JWT_PUBLIC_KEY environment variables are required');
|
||||
}
|
||||
|
||||
// OIDC services — initialised after DB pool is ready
|
||||
const oidcKeyService = new OIDCKeyService(pool, redis as RedisClientType);
|
||||
await oidcKeyService.ensureCurrentKey();
|
||||
const idTokenService = new IDTokenService(oidcKeyService);
|
||||
|
||||
const oauth2Service = new OAuth2Service(
|
||||
tokenRepo,
|
||||
credentialRepo,
|
||||
@@ -140,6 +149,7 @@ export async function createApp(): Promise<Application> {
|
||||
privateKey,
|
||||
publicKey,
|
||||
vaultClient,
|
||||
idTokenService,
|
||||
);
|
||||
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
@@ -156,6 +166,7 @@ export async function createApp(): Promise<Application> {
|
||||
const auditController = new AuditController(auditService);
|
||||
const orgController = new OrgController(orgService);
|
||||
const didController = new DIDController(didService, agentRepo);
|
||||
const oidcController = new OIDCController(oidcKeyService, agentRepo);
|
||||
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
// Org context middleware — sets PostgreSQL session variable app.organization_id
|
||||
@@ -180,6 +191,9 @@ export async function createApp(): Promise<Application> {
|
||||
void didController.getInstanceDIDDocument(req, res, next);
|
||||
});
|
||||
|
||||
// OIDC well-known endpoints and agent-info — mounted at root so /.well-known/* paths resolve
|
||||
app.use('/', createOIDCRouter(oidcController, authMiddleware));
|
||||
|
||||
app.use(`${API_BASE}/agents`, createAgentsRouter(agentController, opaMiddleware));
|
||||
app.use(`${API_BASE}`, createDIDRouter(didController, authMiddleware, opaMiddleware));
|
||||
app.use(
|
||||
|
||||
Reference in New Issue
Block a user