Files
openwork/packages/enterprise-mcp-client/SECURITY.md
T

5.5 KiB

Security, validation, and expiration contract

  • Document status: active reference implementation
  • Last code-and-document review: 2026-07-12
  • Review by: 2026-10-12, or sooner when the MCP SDK/protocol baseline changes Stable protocol evidence baseline: MCP 2025-11-25; provider support must still be verified against the deployed ServiceNow or Microsoft surface.

This document is intentionally time-bounded. A reviewer must re-check the live SDK, MCP specification, provider documentation, and Den adapter before changing the review date. A date alone is not evidence of provider conformance.

Required composition contracts

Boundary Package requirement Den responsibility
Network A fetch port is required; all requests share cancellation/deadline signals. Enforce protocol, credentials, DNS/IP policy, redirects, downgrade rules, cross-origin secret stripping, proxy/TLS posture, response limits, and body cancellation.
Clock Absolute epoch-millisecond values; finite/non-negative validation. Supply deployment time; monitor clock skew operationally.
Tenant and actor No tenant/member model in the package. Construct an adapter already scoped to exactly one organization, connection, and shared/member credential owner.
Client registration Opaque revision, source, and expiry. Concurrent DCR is first-writer-wins. Encrypt the secret, omit secrets from JSON metadata, serialize competing writers, and reject a losing client.
Authorization Signed id, verifier, expiry, optional client revision, opaque transaction revision. Store only a non-reversible state key plus encrypted verifier; cap transactions and enforce single-use.
Credentials Validated OAuth token wire value plus absolute expiry and opaque revision. Encrypt tokens and atomically persist/consume under connection and identity locks.
Diagnostics Secret-free phase events; observer failures cannot affect behavior. Add safe classification, correlation reference, authorized support evidence, retention, and audit.

Expiration rules

Record Validation Expired behavior
Operation lifecycle now < commitExpiresAt before and immediately before transaction commit; abort signal remains active. Reject/roll back with MCP_LIFECYCLE_DEADLINE; never commit after a reported timeout.
OAuth authorization/PKCE Absolute expiry, signed state id, transaction revision, client revision; Den state verification is an additional outer check. Delete the transaction and require Connect to restart. It cannot be reused.
Access token Persist absolute expiry computed from expires_in; reject invalid/negative lifetimes. With a refresh token, allow the bounded SDK refresh path. Without one, invalidate and require reconnect.
Refresh token Opaque; provider rotation is authoritative. Preserve the previous refresh token only when a successful refresh response validly omits a replacement. Invalid grant is reconnect-required, not generic retry.
OAuth client/client secret Absolute client_secret_expires_at when declared; 0 means no declared expiry per DCR convention. Invalidate and require client renewal/re-registration. Do not silently use an expired secret.
Documentation/provider assumptions Last review and Review by above. Revalidate against primary sources and a nonproduction tenant; never promote a stale preview/patch assumption.

The default clock-skew allowance is 30 seconds and is configurable only at client construction. It is not an environment read inside the package.

Validation and failure ownership

Validation happens before secrets cross a boundary whenever possible:

  • configuration: non-empty ids, HTTP(S) URLs, no URL credentials/fragments, bounded OAuth state/code, positive timeouts and TTLs;
  • persistence: OAuth wire schemas, opaque revisions, finite expirations, matching authorization/client revisions, correct credential identity;
  • protocol: initialize first, bounded complete pagination, tool schemas, provider isError, close within deadline;
  • adapter: tenant/member scope, encrypted columns, same-row transaction locks, safe public origin, and guarded outbound requests.

Stable package codes distinguish missing/expired authorization, changed/expired client registration, expired credentials, invalid persistence, and lifecycle deadline. Den maps those codes to a safe phase, owner, remediation, and diagnostic reference. Raw provider messages are not the administrator contract.

Known evidence boundary

The deterministic OAuth/MCP tests prove protocol and port semantics. They do not prove a real customer's ServiceNow ACL/domain policy, Microsoft consent or Conditional Access, provider licensing/preview availability, egress allowlist, or mutation behavior. Those remain explicit nonproduction conformance gates.

Pre-existing records written by another implementation may contain historical provider metadata in the generic JSON column. This package never writes OAuth client secrets or registration access tokens there. A separate reviewed data cleanup is required before claiming that historical rows were rewritten; this PR does not silently mutate every existing record at startup.

Pending PKCE data is intentionally not cross-mode compatible: current mode has one unbound verifier, while enterprise mode has a versioned collection of state-bound transactions. Feature-flag rollout/rollback must drain or expire in-flight browser authorizations first. The adapter fails closed instead of guessing a transaction identity for an old verifier.