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:
SentryAgent.ai Developer
2026-03-30 09:54:26 +00:00
parent 3d1fff15f6
commit 5e465e596a
13 changed files with 2221 additions and 13 deletions

View File

@@ -200,6 +200,11 @@ export interface ITokenResponse {
token_type: 'Bearer';
expires_in: number;
scope: string;
/**
* OIDC ID token — included when the `openid` scope is requested.
* Signed RS256 or ES256 JWT containing agent identity claims.
*/
id_token?: string;
}
/** OAuth 2.0 error response (RFC 6749 §5.2). */

152
src/types/oidc.ts Normal file
View File

@@ -0,0 +1,152 @@
/**
* OIDC type definitions for SentryAgent.ai AgentIdP.
* Covers ID token claims, JWKS response, discovery document, and agent-info response.
*/
// ============================================================================
// JWKS Key and Response
// ============================================================================
/**
* A single JSON Web Key as returned in the JWKS endpoint.
* Supports both RSA (RS256) and EC P-256 (ES256) keys.
*/
export interface IJWKSKey {
/** Key ID — matches the `kid` header in signed JWTs. */
kid: string;
/** Key type: "RSA" or "EC". */
kty: string;
/** Intended use: always "sig" for signing keys. */
use: string;
/** Algorithm: "RS256" or "ES256". */
alg: string;
/** RSA: Base64url-encoded modulus. */
n?: string;
/** RSA: Base64url-encoded public exponent. */
e?: string;
/** EC: Curve name, e.g. "P-256". */
crv?: string;
/** EC: Base64url-encoded x coordinate. */
x?: string;
/** EC: Base64url-encoded y coordinate. */
y?: string;
}
/**
* JWKS (JSON Web Key Set) response returned by the `/.well-known/jwks.json` endpoint.
*/
export interface IJWKSResponse {
/** Array of JSON Web Keys. Includes all non-expired keys (for rotation grace period). */
keys: IJWKSKey[];
}
// ============================================================================
// OIDC Key record (database row)
// ============================================================================
/**
* Represents a row in the `oidc_keys` table.
*/
export interface IOIDCKey {
/** UUID primary key. */
id: string;
/** Key identifier (e.g. "key-20260330-001"). Used as JWT `kid` header. */
kid: string;
/** Signing algorithm: "RS256" or "ES256". */
algorithm: string;
/** Public key in JWK format as stored in DB. */
public_key_jwk: IJWKSKey;
/** Vault KV2 path where the private key is stored, or "dev:no-vault" in dev mode. */
vault_key_path: string;
/** True if this is the active signing key. Only one key is current at a time. */
is_current: boolean;
/** Timestamp when this key was generated. */
created_at: Date;
/** Timestamp when all tokens signed with this key will have expired. */
expires_at: Date;
}
// ============================================================================
// ID Token Claims
// ============================================================================
/**
* Claims payload of an OIDC ID token.
* Conforms to OpenID Connect Core 1.0 §2 with additional agent-specific claims.
*/
export interface IIDTokenClaims {
/** Issuer — the OIDC provider URL. */
iss: string;
/** Subject — the agent UUID. */
sub: string;
/** Audience — the client_id that requested the token. */
aud: string;
/** Issued-at time (Unix seconds). */
iat: number;
/** Expiry time (Unix seconds). */
exp: number;
/** Nonce — if provided in the original request, echoed here for replay protection. */
nonce?: string;
/** Functional classification of the agent. */
agent_type: string;
/** Target deployment environment of the agent. */
deployment_env: string;
/** Organization UUID the agent belongs to. */
organization_id: string;
/** W3C DID identifier for the agent, if one has been generated. */
did?: string;
}
// ============================================================================
// OIDC Discovery Document
// ============================================================================
/**
* OpenID Connect Discovery 1.0 document returned by `/.well-known/openid-configuration`.
* All standard fields are included; `authorization_endpoint` is a stub (not implemented in Phase 3).
*/
export interface IOIDCDiscoveryDocument {
/** OIDC Issuer URL. Must match the `iss` claim in ID tokens. */
issuer: string;
/** Authorization endpoint (stub — not implemented in Phase 3). */
authorization_endpoint: string;
/** Token endpoint for the client_credentials grant. */
token_endpoint: string;
/** JWKS endpoint for ID token verification public keys. */
jwks_uri: string;
/** Supported response types. */
response_types_supported: string[];
/** Supported subject types. */
subject_types_supported: string[];
/** Supported ID token signing algorithms. */
id_token_signing_alg_values_supported: string[];
/** Supported OAuth 2.0 scopes. */
scopes_supported: string[];
/** Claims that may appear in ID tokens or the agent-info response. */
claims_supported: string[];
/** Supported grant types. */
grant_types_supported: string[];
}
// ============================================================================
// Agent Info Response
// ============================================================================
/**
* Response body for the `GET /agent-info` endpoint.
* Returns agent identity claims for the authenticated agent, similar to OIDC UserInfo.
*/
export interface IAgentInfoResponse {
/** Agent UUID (subject). */
sub: string;
/** Functional classification of the agent. */
agent_type: string;
/** Target deployment environment of the agent. */
deployment_env: string;
/** Organization UUID the agent belongs to. */
organization_id: string;
/** W3C DID identifier, if one has been generated. */
did?: string;
/** The OAuth 2.0 scope associated with the Bearer token used to call this endpoint. */
scope: string;
}