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:
@@ -8,6 +8,7 @@ import { CredentialRepository } from '../repositories/CredentialRepository.js';
|
||||
import { AgentRepository } from '../repositories/AgentRepository.js';
|
||||
import { AuditService } from './AuditService.js';
|
||||
import { VaultClient } from '../vault/VaultClient.js';
|
||||
import { IDTokenService } from './IDTokenService.js';
|
||||
import {
|
||||
ITokenPayload,
|
||||
ITokenResponse,
|
||||
@@ -46,6 +47,8 @@ export class OAuth2Service {
|
||||
* @param privateKey - PEM-encoded RSA private key for signing tokens.
|
||||
* @param publicKey - PEM-encoded RSA public key for verifying tokens.
|
||||
* @param vaultClient - Optional VaultClient for Phase 2 credential verification.
|
||||
* @param idTokenService - Optional IDTokenService; when provided and `openid` scope
|
||||
* is requested, an OIDC ID token is appended to the token response.
|
||||
*/
|
||||
constructor(
|
||||
private readonly tokenRepository: TokenRepository,
|
||||
@@ -55,6 +58,7 @@ export class OAuth2Service {
|
||||
private readonly privateKey: string,
|
||||
private readonly publicKey: string,
|
||||
private readonly vaultClient: VaultClient | null = null,
|
||||
private readonly idTokenService: IDTokenService | null = null,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -207,12 +211,21 @@ export class OAuth2Service {
|
||||
// Instrument: count successful token issuances
|
||||
tokensIssuedTotal.inc({ scope });
|
||||
|
||||
return {
|
||||
const tokenResponse: ITokenResponse = {
|
||||
access_token: accessToken,
|
||||
token_type: 'Bearer',
|
||||
expires_in: expiresIn,
|
||||
scope,
|
||||
};
|
||||
|
||||
// OIDC: append id_token when the `openid` scope was requested and IDTokenService is wired
|
||||
const scopeList = scope.split(' ');
|
||||
if (scopeList.includes('openid') && this.idTokenService !== null) {
|
||||
const claims = await this.idTokenService.buildIDTokenClaims(agent, clientId, scope);
|
||||
tokenResponse.id_token = await this.idTokenService.signIDToken(claims);
|
||||
}
|
||||
|
||||
return tokenResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user