Reduce MCP OAuth debug detail
This commit is contained in:
@@ -93,37 +93,6 @@ type MCPOAuthFlowErrorReason =
|
|||||||
|
|
||||||
const MAX_LOCK_RETRIES = 5
|
const MAX_LOCK_RETRIES = 5
|
||||||
|
|
||||||
/**
|
|
||||||
* OAuth query parameters that should be redacted from logs.
|
|
||||||
* These contain sensitive values that could enable CSRF or session fixation attacks.
|
|
||||||
*/
|
|
||||||
const SENSITIVE_OAUTH_PARAMS = [
|
|
||||||
'state',
|
|
||||||
'nonce',
|
|
||||||
'code_challenge',
|
|
||||||
'code_verifier',
|
|
||||||
'code',
|
|
||||||
]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redacts sensitive OAuth query parameters from a URL for safe logging.
|
|
||||||
* Prevents exposure of state, nonce, code_challenge, code_verifier, and authorization codes.
|
|
||||||
*/
|
|
||||||
function redactSensitiveUrlParams(url: string): string {
|
|
||||||
try {
|
|
||||||
const parsedUrl = new URL(url)
|
|
||||||
for (const param of SENSITIVE_OAUTH_PARAMS) {
|
|
||||||
if (parsedUrl.searchParams.has(param)) {
|
|
||||||
parsedUrl.searchParams.set(param, '[REDACTED]')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return parsedUrl.toString()
|
|
||||||
} catch {
|
|
||||||
// Return as-is if not a valid URL
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function summarizeHeadersForDebug(
|
function summarizeHeadersForDebug(
|
||||||
headers: Record<string, string> | undefined,
|
headers: Record<string, string> | undefined,
|
||||||
): {
|
): {
|
||||||
@@ -1899,29 +1868,18 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
|
|||||||
|
|
||||||
// Extract and store scopes from the authorization URL for later use in token exchange
|
// Extract and store scopes from the authorization URL for later use in token exchange
|
||||||
const scopes = authorizationUrl.searchParams.get('scope')
|
const scopes = authorizationUrl.searchParams.get('scope')
|
||||||
logMCPDebug(
|
|
||||||
this.serverName,
|
|
||||||
`Authorization URL: ${redactSensitiveUrlParams(authorizationUrl.toString())}`,
|
|
||||||
)
|
|
||||||
logMCPDebug(this.serverName, `Scopes in URL: ${scopes || 'NOT FOUND'}`)
|
|
||||||
|
|
||||||
if (scopes) {
|
if (scopes) {
|
||||||
this._scopes = scopes
|
this._scopes = scopes
|
||||||
logMCPDebug(
|
logMCPDebug(this.serverName, 'Captured scopes from authorization URL')
|
||||||
this.serverName,
|
|
||||||
`Captured scopes from authorization URL: ${scopes}`,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
// If no scope in URL, try to get it from metadata
|
// If no scope in URL, try to get it from metadata
|
||||||
const metadataScope = getScopeFromMetadata(this._metadata)
|
const metadataScope = getScopeFromMetadata(this._metadata)
|
||||||
if (metadataScope) {
|
if (metadataScope) {
|
||||||
this._scopes = metadataScope
|
this._scopes = metadataScope
|
||||||
logMCPDebug(
|
logMCPDebug(this.serverName, 'Using scopes from metadata')
|
||||||
this.serverName,
|
|
||||||
`Using scopes from metadata: ${metadataScope}`,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
logMCPDebug(this.serverName, `No scopes available from URL or metadata`)
|
logMCPDebug(this.serverName, 'No scopes available from URL or metadata')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1939,7 +1897,7 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
|
|||||||
if (existing) {
|
if (existing) {
|
||||||
existing.stepUpScope = this._scopes
|
existing.stepUpScope = this._scopes
|
||||||
storage.update(existingData)
|
storage.update(existingData)
|
||||||
logMCPDebug(this.serverName, `Persisted step-up scope: ${this._scopes}`)
|
logMCPDebug(this.serverName, 'Persisted step-up scope')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1960,8 +1918,6 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logMCPDebug(this.serverName, `Redirecting to authorization URL`)
|
logMCPDebug(this.serverName, `Redirecting to authorization URL`)
|
||||||
const redactedUrl = redactSensitiveUrlParams(urlString)
|
|
||||||
logMCPDebug(this.serverName, `Authorization URL: ${redactedUrl}`)
|
|
||||||
|
|
||||||
// Notify the UI about the authorization URL BEFORE opening the browser,
|
// Notify the UI about the authorization URL BEFORE opening the browser,
|
||||||
// so users can see the URL as a fallback if the browser fails to open
|
// so users can see the URL as a fallback if the browser fails to open
|
||||||
@@ -1970,7 +1926,7 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.skipBrowserOpen) {
|
if (!this.skipBrowserOpen) {
|
||||||
logMCPDebug(this.serverName, `Opening authorization URL: ${redactedUrl}`)
|
logMCPDebug(this.serverName, 'Opening authorization URL')
|
||||||
|
|
||||||
const success = await openBrowser(urlString)
|
const success = await openBrowser(urlString)
|
||||||
if (!success) {
|
if (!success) {
|
||||||
@@ -1982,7 +1938,7 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
|
|||||||
} else {
|
} else {
|
||||||
logMCPDebug(
|
logMCPDebug(
|
||||||
this.serverName,
|
this.serverName,
|
||||||
`Skipping browser open (skipBrowserOpen=true). URL: ${redactedUrl}`,
|
'Skipping browser open (skipBrowserOpen=true)',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2035,7 +1991,7 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
storage.update(existingData)
|
storage.update(existingData)
|
||||||
logMCPDebug(this.serverName, `Invalidated credentials (scope: ${scope})`)
|
logMCPDebug(this.serverName, `Invalidated credentials (${scope})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveDiscoveryState(state: OAuthDiscoveryState): Promise<void> {
|
async saveDiscoveryState(state: OAuthDiscoveryState): Promise<void> {
|
||||||
@@ -2043,10 +1999,7 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
|
|||||||
const existingData = storage.read() || {}
|
const existingData = storage.read() || {}
|
||||||
const serverKey = getServerKey(this.serverName, this.serverConfig)
|
const serverKey = getServerKey(this.serverName, this.serverConfig)
|
||||||
|
|
||||||
logMCPDebug(
|
logMCPDebug(this.serverName, 'Saving discovery state')
|
||||||
this.serverName,
|
|
||||||
`Saving discovery state (authServer: ${state.authorizationServerUrl})`,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Persist only the URLs, NOT the full metadata blobs.
|
// Persist only the URLs, NOT the full metadata blobs.
|
||||||
// authorizationServerMetadata alone is ~1.5-2KB per MCP server (every
|
// authorizationServerMetadata alone is ~1.5-2KB per MCP server (every
|
||||||
@@ -2085,10 +2038,7 @@ export class ClaudeAuthProvider implements OAuthClientProvider {
|
|||||||
|
|
||||||
const cached = data?.mcpOAuth?.[serverKey]?.discoveryState
|
const cached = data?.mcpOAuth?.[serverKey]?.discoveryState
|
||||||
if (cached?.authorizationServerUrl) {
|
if (cached?.authorizationServerUrl) {
|
||||||
logMCPDebug(
|
logMCPDebug(this.serverName, 'Returning cached discovery state')
|
||||||
this.serverName,
|
|
||||||
`Returning cached discovery state (authServer: ${cached.authorizationServerUrl})`,
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
authorizationServerUrl: cached.authorizationServerUrl,
|
authorizationServerUrl: cached.authorizationServerUrl,
|
||||||
|
|||||||
Reference in New Issue
Block a user