Skip to content

oauth flow updates#34

Merged
j4ys0n merged 2 commits intomainfrom
external-oauth
Mar 23, 2026
Merged

oauth flow updates#34
j4ys0n merged 2 commits intomainfrom
external-oauth

Conversation

@j4ys0n
Copy link
Copy Markdown
Contributor

@j4ys0n j4ys0n commented Mar 23, 2026

No description provided.

Copilot AI review requested due to automatic review settings March 23, 2026 21:17
@j4ys0n
Copy link
Copy Markdown
Contributor Author

j4ys0n commented Mar 23, 2026

Automated review 🤖

Summary of Changes
Version bump to 1.11.1 with OAuth flow enhancements: introduces compatibility fallback rules for Webflow transport/resource URI mismatches, adds single-flight deduplication for OAuth token refresh and user connection establishment, and implements runtime state invalidation when authoritative resource URIs change—ensuring stale sessions/tokens are cleared on URI updates.

Key Changes & Positives

  • resolveCompatibilityFallbackExternalOAuthResourceUri adds Webflow-specific fallback (/mcp/sse) to handle legacy transport/resource splits 🟢
  • Single-flight deduplication in MCPService.connectUserToServer and McpOAuthTokens.refreshTokenRecord prevents duplicate concurrent refresh/connection attempts, reducing load and race conditions 🟢
  • Explicit refresh token suppression in McpOAuthClientProvider.tokens() prevents SDK-side re-refresh on 401s, aligning refresh ownership with MissionSquad’s logic 🟢
  • Comprehensive logging in oauthTokens.ts with redacted sensitive fields improves debuggability without exposing secrets 🟢

Potential Issues & Recommendations

  1. Issue / Risk: invalidateExternalOAuthRuntimeState tears down all active connections and deletes tokens/sessions for a server when its resource URI changes, potentially disrupting multiple users simultaneously.
    Impact: High-impact outage risk during backfill or template updates if not coordinated with deployments.
    Recommendation: Add rate-limiting or staggered invalidation (e.g., per-user cooldown) and surface invalidation triggers in health metrics.
    Status: 🟡 Needs review

  2. Issue / Risk: resolveCompatibilityFallbackExternalOAuthResourceUri hardcodes Webflow logic; future providers require code changes and redeploy.
    Impact: Reduced extensibility; each new compatibility case increases maintenance burden.
    Recommendation: Consider externalizing rules via config (e.g., DB table or env var) to support dynamic addition without code changes.
    Status: 🟡 Needs review

  3. Issue / Risk: shouldBackfillExternalOAuthResourceUri checks runtimeTemplate?.resourceUri !== server.oauthTemplate.resourceUri, but runtimeTemplate may be undefined if oauthTemplate is missing—though guarded earlier, the condition relies on implicit ordering.
    Impact: Potential missed backfill if oauthTemplate is partially malformed.
    Recommendation: Add explicit null guard before comparison or refactor to early-return on missing template.
    Status: 🟡 Needs review

Language/Framework Checks

  • TypeScript: ExternalOAuthResourceCompatibilityRule interface and resolveCompatibilityFallbackExternalOAuthResourceUri use strict URL parsing and safe fallbacks (no unsafe casts)
  • McpOAuthClientProvider.tokens() intentionally omits refresh_token from snapshot to prevent SDK auto-refresh—comment explains rationale clearly
  • Logging utilities (truncateSensitiveValue, summarizeUrlForLog) sanitize sensitive fields while preserving diagnostic utility

Security & Privacy

  • Token redaction in logs (truncateSensitiveValue) limits exposure of client secrets, refresh tokens, and code verifiers
  • Explicit refresh_token: undefined in provider snapshot prevents accidental leakage to SDK layers that might trigger unauthorized refreshes
  • invalidateExternalOAuthRuntimeState clears tokens/sessions before reconnect, reducing window for replay attacks post-URI change

Build/CI & Ops

  • Version bump to 1.11.1 signals non-breaking bug fixes; no API contract changes (routes, params, data formats unchanged)
  • Logging volume increase in oauthTokens.ts may impact storage costs—monitor log ingestion rates post-deployment

Tests

  • New tests validate:
    • Compatibility fallback logic (resolveCompatibilityFallbackExternalOAuthResourceUri)
    • OAuth provider suppresses refresh token in snapshots
    • Single-flight deduplication for refresh and connection attempts
    • Runtime normalization applies fallback for issuer-override templates
    • Backfill invalidates runtime state on resource URI change
  • Ensure test/mcp-external-auth.spec.ts runs with mocked fetch and MongoDB to avoid flaky network/db dependencies

Approval Recommendation
Approve with caveats

  • Add mitigation plan for high-impact invalidation (rate limiting/staggering) before production rollout
  • Document Webflow-specific fallback rule in README or admin docs for future maintainers
  • Verify log volume impact in staging environment prior to merge

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the external MCP OAuth flow to better handle provider-specific resource URI quirks, reduce duplicate concurrent work (refresh/connect), and adjust how OAuth tokens/resources are surfaced to the MCP SDK.

Changes:

  • Add a compatibility fallback mechanism for external OAuth resourceUri resolution (notably Webflow /mcp/sse) and apply it across validation/runtime normalization/backfill.
  • Introduce single-flight behavior for OAuth refreshes (per server/user) and for per-user server connection establishment.
  • Adjust OAuth provider behavior to avoid returning refresh tokens to the SDK helper (preventing SDK-owned refresh loops) and add extensive OAuth-related logging.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
test/mcp-external-auth.spec.ts Adds/updates tests for compatibility resource fallback, provider snapshot behavior, and single-flight refresh/connect behavior.
src/services/oauthTokens.ts Adds token record logging, single-flight refresh implementation, resource URL validation, and provider snapshot changes.
src/services/mcp.ts Adds compatibility fallback resource resolution, resource-uri backfill invalidation logic, and single-flight user connection establishment.
package.json Bumps package version to 1.11.1.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/services/oauthTokens.ts Outdated
Comment on lines +304 to +316
log({
level: 'info',
msg: `[oauth:${input.username}:${input.serverName}] Starting OAuth refresh ${JSON.stringify({
tokenEndpoint: summarizeUrlForLog(input.tokenEndpoint),
resource: summarizeUrlForLog(input.resource),
clientId: truncateSensitiveValue(existing.clientId),
refreshToken: truncateSensitiveValue(existing.refreshToken),
tokenEndpointAuthMethod: existing.tokenEndpointAuthMethod,
registrationMode: existing.registrationMode,
expiresAt: existing.expiresAt?.toISOString(),
scopes: existing.scopes
})}`
})
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several new info logs include truncated refreshToken values. Even partial refresh tokens are sensitive credentials and can become a security liability since log() prints to stdout in production. Prefer logging only booleans/metadata (e.g., hasRefreshToken, token endpoint origin/path) and omit refresh token material entirely.

Copilot uses AI. Check for mistakes.
log({
level: 'info',
msg: `[oauth:${username}:${serverName}] Saved PKCE code verifier ${JSON.stringify({
codeVerifier: truncateSensitiveValue(codeVerifier)
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new logs emit truncated PKCE codeVerifier values. Code verifiers are secrets for active OAuth sessions; logging them (even truncated) can enable replay/credential stuffing if logs are accessed. Recommend removing the verifier from logs and only logging non-sensitive flags (e.g., hasCodeVerifier: true).

Suggested change
codeVerifier: truncateSensitiveValue(codeVerifier)
hasCodeVerifier: true

Copilot uses AI. Check for mistakes.
const parsed = new URL(value)
return `${parsed.origin}${parsed.pathname}`
} catch {
return value
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

summarizeUrlForLog() returns the original string on URL parse failure, which can re-introduce query strings/fragments (e.g., ?token=...) into logs. Since these values may come from external configuration, consider returning a safe placeholder (like <invalid-url>) or stripping everything after ?/# even in the fallback path.

Suggested change
return value
// Fallback: strip query strings and fragments to avoid logging sensitive data
const queryIndex = value.indexOf('?')
const fragmentIndex = value.indexOf('#')
let endIndex = value.length
if (queryIndex !== -1) {
endIndex = Math.min(endIndex, queryIndex)
}
if (fragmentIndex !== -1) {
endIndex = Math.min(endIndex, fragmentIndex)
}
const sanitized = value.slice(0, endIndex)
return sanitized || '<invalid-url>'

Copilot uses AI. Check for mistakes.
Comment thread src/services/mcp.ts
Comment on lines +1753 to 1756
if (oauthTemplate.resourceUri !== resolveCompatibilityFallbackExternalOAuthResourceUri(transportUrl)) {
throw new McpValidationError(
'oauthTemplate.resourceUri must equal the canonicalized transport url in issuer-override discovery mode'
'oauthTemplate.resourceUri must equal the resolved fallback resource uri in issuer-override discovery mode'
)
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated validation error text references a "resolved fallback resource uri", but doesn’t tell the caller what value was expected vs what was provided. Including the expected resolved URI (and the received one) in the error message would make misconfigurations much easier to diagnose, especially since canonicalization/compat rules can change the expected value.

Copilot uses AI. Check for mistakes.
Comment on lines +395 to +409
global.fetch = jest.fn(async () =>
new Response(
JSON.stringify({
access_token: 'fresh-access',
refresh_token: 'fresh-refresh',
token_type: 'Bearer',
expires_in: 3600
}),
{
status: 200,
headers: { 'content-type': 'application/json' }
}
)
) as typeof global.fetch

Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test replaces global.fetch but never restores it afterward, which can leak the mocked fetch into later test blocks in this file (and make future additions flaky). Consider restoring global.fetch in an afterEach for this describe (or use jest.spyOn(global, 'fetch') and mockRestore() / finally cleanup in the test).

Copilot uses AI. Check for mistakes.
@j4ys0n
Copy link
Copy Markdown
Contributor Author

j4ys0n commented Mar 23, 2026

Automated review 🤖

Summary of Changes
Bumps version to 1.11.1, adds ENABLE_OAUTH_LOGGING environment flag, introduces Webflow transport/resource URI compatibility fallback logic, enforces single-flight OAuth refresh and user connection establishment, and invalidates runtime OAuth state (tokens, sessions, installs) when authoritative resource URI changes—ensuring consistency after config updates or backfills.

Key Changes & Positives

  • Single-flight OAuth refresh (src/services/oauthTokens.ts, refreshTokenRecord) prevents duplicate refresh calls for same user/server, reducing token endpoint load and race conditions 🟢
  • Webflow compatibility fallback (src/services/mcp.ts, resolveCompatibilityFallbackExternalOAuthResourceUri) maps /mcp paths to /sse for known Webflow endpoints, resolving mismatched resource URIs 🟢
  • OAuth state invalidation on resource URI change (src/services/mcp.ts, invalidateExternalOAuthRuntimeState) ensures stale connections/tokens are torn down when authoritative resource changes, preventing auth drift 🟢
  • Single-flight user connection (src/services/mcp.ts, connectUserToServer) avoids concurrent connection attempts per user/server, improving reliability 🟢

Potential Issues & Recommendations

  1. Issue / Risk: resolveCompatibilityFallbackExternalOAuthResourceUri hardcodes Webflow-specific logic; future providers require code changes and redeploy

    • Impact: Reduces extensibility; new OAuth providers with similar transport/resource splits cannot be configured dynamically
    • Recommendation: Move compatibility rules to a configurable list (e.g., DB or config file) or add a generic resourceUriOverride field in server config
    • Status: 🟡 Needs review
  2. Issue / Risk: oauthLogInfo logs sensitive token metadata (e.g., clientId, refreshToken truncated but still visible) when ENABLE_OAUTH_LOGGING=true

    • Impact: Potential exposure of PII/sensitive identifiers in logs if logging infrastructure lacks access controls
    • Recommendation: Add explicit opt-in for sensitive fields or mask all values beyond origin/path in production by default
    • Status: 🟡 Needs review
  3. Issue / Risk: invalidateExternalOAuthRuntimeState tears down all user connections and deletes all tokens/sessions for a server—even if only one user’s resource URI changed

    • Impact: Unnecessary disruption for other users when server-wide config change is unlikely
    • Recommendation: Consider per-user invalidation or defer to user-level reauth triggers unless resource URI is globally authoritative
    • Status: 🟡 Needs review

Language/Framework Checks

  • resolveCompatibilityFallbackExternalOAuthResourceUri correctly handles URL parsing and normalization (src/services/mcp.ts, lines 450–465)
  • oauthLogInfo guards logging behind env.ENABLE_OAUTH_LOGGING, avoiding unnecessary overhead in production (src/services/mcp.ts, src/services/oauthTokens.ts)
  • McpOAuthClientProvider.tokens() intentionally omits refresh_token from returned snapshot to prevent SDK auto-refresh conflicts (src/services/oauthTokens.ts, line 639–640)
  • shouldBackfillExternalOAuthResourceUri now checks runtimeTemplate?.resourceUri !== server.oauthTemplate.resourceUri to detect stale URIs (src/services/mcp.ts, line 1403–1406)

Security & Privacy

  • Truncation helpers (truncateSensitiveValue, summarizeUrlForLog) reduce exposure of full secrets/URLs in logs (src/services/oauthTokens.ts, lines 70–92)
  • validateResourceURL in McpOAuthClientProvider enforces use of configured compatibility resource URI, preventing accidental misrouting (src/services/oauthTokens.ts, lines 522–530)

Build/CI & Ops

  • Version bump to 1.11.1 indicates patch release; no breaking API changes detected in diff
  • New ENABLE_OAUTH_LOGGING flag requires ops to explicitly enable verbose OAuth tracing; safe default (false)

Tests

  • Adds tests for Webflow compatibility fallback (test/mcp-external-auth.spec.ts, lines 86–93)
  • Validates single-flight OAuth refresh and user connection (test/mcp-external-auth.spec.ts, lines 317–425)
  • Confirms McpOAuthClientProvider suppresses SDK-owned refresh by omitting refresh_token (test/mcp-external-auth.spec.ts, lines 304–315)
  • Verifies runtime normalization applies fallback for issuer-override (test/mcp-external-auth.spec.ts, lines 694–723)
  • Ensures resource URI backfill invalidates runtime state (test/mcp-external-auth.spec.ts, lines 725–785)

Approval Recommendation
Approve with caveats

  • Address extensibility of compatibility rules (recommend dynamic config over hardcoded Webflow logic)
  • Review log sanitization strategy for sensitive fields under ENABLE_OAUTH_LOGGING
  • Clarify whether per-user invalidation is needed or if server-wide teardown is acceptable for resource URI changes

@j4ys0n j4ys0n merged commit 3746fdc into main Mar 23, 2026
1 check passed
@j4ys0n j4ys0n deleted the external-oauth branch March 23, 2026 22:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants