feat(phase-3): workstream 4 — AGNTCY Federation

Implements cross-IdP token verification for the AGNTCY ecosystem:

- Migration 015: federation_partners table (issuer, jwks_uri,
  allowed_organizations JSONB, status, expires_at)
- FederationService: registerPartner (JWKS validation at registration),
  listPartners, getPartner, updatePartner, deletePartner,
  verifyFederatedToken (alg:none rejected, RS256/ES256 only,
  allowedOrganizations filter, expiry enforcement)
- JWKS caching in Redis (TTL: FEDERATION_JWKS_CACHE_TTL_SECONDS);
  cache invalidated on partner delete and jwks_uri change
- FederationController + routes: 5 admin:orgs endpoints +
  POST /federation/verify (agents:read)
- OPA policy: 5 federation admin endpoint → admin:orgs mappings
- 499 unit tests passing; 94.69% statement coverage on FederationService

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
SentryAgent.ai Developer
2026-03-30 10:13:49 +00:00
parent 5e465e596a
commit 03b5de300c
12 changed files with 2092 additions and 13 deletions

151
src/types/federation.ts Normal file
View File

@@ -0,0 +1,151 @@
/**
* Federation type definitions for SentryAgent.ai AgentIdP.
* Covers federation partner management and cross-IdP token verification.
*/
// ============================================================================
// Federation Partner Status
// ============================================================================
/**
* Lifecycle status of a federation partner.
* - 'active' — partner is trusted; tokens accepted.
* - 'suspended' — partner is temporarily disabled; tokens rejected.
* - 'expired' — partner's expires_at has passed; tokens rejected.
*/
export type FederationPartnerStatus = 'active' | 'suspended' | 'expired';
// ============================================================================
// Federation Partner (database row)
// ============================================================================
/**
* Represents a row in the `federation_partners` table.
* A federation partner is an external IdP whose tokens this IdP trusts.
*/
export interface IFederationPartner {
/** UUID primary key. */
id: string;
/** Human-readable partner name. */
name: string;
/** Issuer URL — must match the `iss` claim in federated tokens. */
issuer: string;
/** URL of the partner's JWKS endpoint. */
jwks_uri: string;
/**
* Allowlist of organization_id values accepted from this partner.
* An empty array means all organizations are accepted.
*/
allowed_organizations: string[];
/** Current lifecycle status. */
status: FederationPartnerStatus;
/** Timestamp when this partner was registered. */
created_at: Date;
/** Timestamp of the last update. */
updated_at: Date;
/** Optional expiry timestamp. NULL means the partner never expires. */
expires_at: Date | null;
}
// ============================================================================
// Request / Response shapes
// ============================================================================
/**
* Request body for registering a new federation partner.
* POST /api/v1/federation/trust
*/
export interface ICreatePartnerRequest {
/** Human-readable partner name. */
name: string;
/** Issuer URL of the external IdP. */
issuer: string;
/** URL of the partner's JWKS endpoint (must be reachable at registration time). */
jwks_uri: string;
/**
* Optional allowlist of organization_id values to accept from this partner.
* Defaults to empty array (all organizations accepted).
*/
allowed_organizations?: string[];
/** Optional ISO 8601 date-time string after which the partner will be expired. */
expires_at?: string;
}
/**
* Request body for updating an existing federation partner.
* PATCH /api/v1/federation/partners/:id
* All fields are optional; only provided fields are updated.
*/
export interface IUpdatePartnerRequest {
/** Updated human-readable partner name. */
name?: string;
/** Updated JWKS endpoint URL. Cache will be invalidated if this changes. */
jwks_uri?: string;
/** Updated organization allowlist. */
allowed_organizations?: string[];
/** Updated lifecycle status. */
status?: FederationPartnerStatus;
/**
* Updated expiry timestamp.
* Pass an ISO 8601 string to set a new expiry, or null to clear it.
*/
expires_at?: string | null;
}
/**
* Request body for verifying a federated token.
* POST /api/v1/federation/verify
*/
export interface IFederationVerifyRequest {
/** The JWT token string to verify. */
token: string;
/**
* Optional issuer hint. If provided, the token's `iss` claim must match this value exactly.
* Useful to prevent issuer confusion when multiple partners share JWKS.
*/
expected_issuer?: string;
}
/**
* Result returned by a successful federated token verification.
*/
export interface IFederationVerifyResult {
/** Whether the token is valid (true when verification passed). */
valid: boolean;
/** The issuer (`iss` claim) of the verified token. */
issuer: string;
/** The subject (`sub` claim) of the verified token. */
subject: string;
/**
* The `organization_id` claim from the token, if present.
* Populated when the external IdP includes this standard SentryAgent claim.
*/
organization_id?: string;
/** All claims from the verified token payload. */
claims: Record<string, unknown>;
}
/**
* Decoded claims payload of a federated JWT token.
* Matches the standard JWT claims plus optional SentryAgent extensions.
* Used internally by FederationService during token verification.
*/
export interface IFederatedTokenClaims {
/** Issuer — identifies the external IdP. */
iss: string;
/** Subject — identifies the agent in the external IdP. */
sub: string;
/** Audience — the intended recipient(s) of the token. */
aud: string | string[];
/** Issued-at time (Unix seconds). */
iat: number;
/** Expiry time (Unix seconds). */
exp: number;
/**
* Optional organization_id claim.
* When present, used to enforce the partner's allowed_organizations filter.
*/
organization_id?: string;
/** Additional claims from the token payload. */
[key: string]: unknown;
}