Skip to content

Proxy command does not handle OAuth token refresh, causing authentication failures after token expiry #707

Closed
@JAORMX

Description

@JAORMX

Problem

The thv proxy command with remote authentication (--remote-auth) does not handle OAuth token refresh, causing the proxy to fail once the initial access token expires (typically after 1 hour).

Root Cause

In cmd/thv/app/proxy.go:

  1. OAuth flow is performed once at startup (line 171)
  2. Only the token string is extracted - the RefreshToken and oauth2.Token object are discarded (lines 410-414)
  3. Static token middleware - createTokenInjectionMiddleware uses a fixed token that never gets refreshed (line 503)
  4. No expiry detection - no mechanism exists to detect token expiry or perform refresh
// Lines 410-414 in proxy.go - only token string is kept
if tokenResult.IDToken != "" {
    return tokenResult.IDToken, nil
}
return tokenResult.AccessToken, nil
// Lines 503-511 - static token injection
func createTokenInjectionMiddleware(accessToken string) types.Middleware {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))
            next.ServeHTTP(w, r)
        })
    }
}

Impact

  • Long-running proxy instances fail after token expiration (typically 1 hour)
  • Users experience authentication failures without clear indication of the cause
  • Manual restart required to re-authenticate
  • Poor user experience for production deployments

Current Behavior

  1. User starts proxy with --remote-auth
  2. OAuth flow completes successfully
  3. Proxy works fine initially
  4. After ~1 hour, token expires
  5. All requests start failing with 401 Unauthorized
  6. No automatic recovery - manual restart needed

Expected Behavior

The proxy should automatically refresh tokens when they expire, maintaining continuous authentication.

Potential Solutions

Option 1: Use oauth2.Client with automatic refresh

// Store the complete oauth2.Token and oauth2.Config
// Use oauth2.Config.Client(ctx, token) which handles refresh automatically
client := oauthConfig.Client(ctx, token)

Option 2: Implement refresh logic in middleware

// Check token expiry in middleware
// Refresh token when needed
// Update Authorization header with new token

Option 3: Background token refresh goroutine

// Start a goroutine that periodically checks and refreshes tokens
// Update the middleware's token atomically

Files Affected

Additional Context

The OAuth flow implementation in pkg/auth/oauth/flow.go already properly handles and returns refresh tokens in the TokenResult struct (lines 68-69), but this information is not utilized by the proxy command.

// TokenResult already includes refresh token
type TokenResult struct {
    AccessToken  string
    RefreshToken string  // This is available but unused!
    TokenType    string
    Expiry       time.Time
    Claims       jwt.MapClaims
    IDToken      string
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions