Skip to content

Commit 0dc7c05

Browse files
Refactor OAuth token persistence and fix Resource/Audience conflation
Extract TokenPersistenceManager to pkg/auth/remote to eliminate the repeated nil-check + fetch-cached-token + create-token-source pattern shared by three callers. Generalize RegistryOAuthConfig to OAuthConfig in pkg/config, adding a Resource field (RFC 8707) and an injectable configUpdater callback so callers can supply their own persistence logic. Fix a bug where Audience (provider-specific, e.g. Auth0) was passed where Resource (RFC 8707 resource indicator) was expected: Resource now flows to CreateOAuthConfigFromOIDC and Audience is routed into OAuthParams["audience"] for authorization URL parameters. Add field-level doc comments to OAuthConfig clarifying the distinction between Audience and Resource. Fix %w error wrapping in tryRestoreFromCache and tryRestoreFromCachedTokens. Convert configUpdaterFunc from a type alias to a named type. Add unit tests covering: FetchRefreshToken direct paths, the Resource-vs-Audience split in buildOAuthFlowConfig (regression guard), configUpdater callback invocation, endpoint-override logic, wrapWithPersistence persistence callbacks, and resolveClientCredentials priority logic. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 9824210 commit 0dc7c05

File tree

13 files changed

+989
-98
lines changed

13 files changed

+989
-98
lines changed

pkg/auth/remote/doc.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
// SPDX-FileCopyrightText: Copyright 2025 Stacklok, Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
// Package remote provides authentication handling for remote MCP servers.
4+
// Package remote provides authentication handling for remote MCP servers,
5+
// as well as general-purpose OAuth token source utilities used across the codebase.
56
//
6-
// This package implements OAuth/OIDC-based authentication with automatic
7-
// discovery support for remote MCP servers. It handles:
7+
// # Remote MCP server authentication
8+
//
9+
// Handler.Authenticate() is the main entry point: it takes a remote URL
10+
// and performs all necessary discovery and authentication steps, including:
811
// - OAuth issuer discovery (RFC 8414)
912
// - Protected resource metadata (RFC 9728)
1013
// - OAuth flow execution (PKCE-based)
1114
// - Token source creation for HTTP transports
1215
//
13-
// The main entry point is Handler.Authenticate() which takes a remote URL
14-
// and performs all necessary discovery and authentication steps.
15-
//
1616
// Configuration is defined in pkg/runner.RemoteAuthConfig as part of the
1717
// runner's RunConfig structure.
18+
//
19+
// # General-purpose token source utilities
20+
//
21+
// These types and functions are also used outside of remote MCP auth (e.g. registry auth):
22+
// - PersistingTokenSource / NewPersistingTokenSource — wraps an oauth2.TokenSource
23+
// and invokes a TokenPersister callback whenever tokens are refreshed.
24+
// - CreateTokenSourceFromCached — restores an oauth2.TokenSource from a cached
25+
// refresh token without requiring a new interactive flow.
26+
// - TokenPersistenceManager / NewTokenPersistenceManager — retrieves a cached
27+
// refresh token from a secrets provider and creates a token source from it.
1828
package remote

pkg/auth/remote/handler.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -237,14 +237,9 @@ func (h *Handler) tryRestoreFromCachedTokens(
237237
scopes []string,
238238
authServerInfo *discovery.AuthServerInfo,
239239
) (oauth2.TokenSource, error) {
240-
// Resolve the refresh token from the secret manager
241-
if h.secretProvider == nil {
242-
return nil, fmt.Errorf("secret provider not configured, cannot restore cached tokens")
243-
}
244-
245-
refreshToken, err := h.secretProvider.GetSecret(ctx, h.config.CachedRefreshTokenRef)
240+
mgr, err := NewTokenPersistenceManager(h.secretProvider)
246241
if err != nil {
247-
return nil, fmt.Errorf("failed to retrieve cached refresh token: %w", err)
242+
return nil, fmt.Errorf("secret provider not configured, cannot restore cached tokens: %w", err)
248243
}
249244

250245
// Resolve client credentials - prefer cached DCR credentials over config
@@ -284,12 +279,16 @@ func (h *Handler) tryRestoreFromCachedTokens(
284279

285280
// Create token source from cached refresh token.
286281
// Passes resource for RFC 8707 compliance when configured.
287-
baseSource := CreateTokenSourceFromCached(
282+
baseSource, err := mgr.RestoreFromCache(
283+
ctx,
284+
h.config.CachedRefreshTokenRef,
288285
oauth2Config,
289-
refreshToken,
290286
h.config.CachedTokenExpiry,
291287
h.config.Resource,
292288
)
289+
if err != nil {
290+
return nil, err
291+
}
293292

294293
// Try to get a token to verify the cached tokens are valid
295294
// This will trigger a refresh since we don't have an access token

0 commit comments

Comments
 (0)