Skip to content

Commit aa534ec

Browse files
committed
add logger and leeway
1 parent 8a61667 commit aa534ec

1 file changed

Lines changed: 36 additions & 20 deletions

File tree

internal/auth/generic/generic.go

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/MicahParks/keyfunc/v3"
2929
"github.com/golang-jwt/jwt/v5"
3030
"github.com/googleapis/genai-toolbox/internal/auth"
31+
"github.com/googleapis/genai-toolbox/internal/util"
3132
)
3233

3334
const AuthServiceType string = "generic"
@@ -71,6 +72,22 @@ func (cfg Config) Initialize() (auth.AuthService, error) {
7172
return a, nil
7273
}
7374

75+
func newSecureHTTPClient() *http.Client {
76+
return &http.Client{
77+
Timeout: 10 * time.Second,
78+
Transport: &http.Transport{
79+
ForceAttemptHTTP2: true,
80+
MaxIdleConns: 10,
81+
IdleConnTimeout: 90 * time.Second,
82+
TLSHandshakeTimeout: 5 * time.Second,
83+
ExpectContinueTimeout: 1 * time.Second,
84+
},
85+
CheckRedirect: func(req *http.Request, via []*http.Request) error {
86+
return http.ErrUseLastResponse
87+
},
88+
}
89+
}
90+
7491
func discoverJWKSURL(AuthorizationServer string) (string, error) {
7592
u, err := url.Parse(AuthorizationServer)
7693
if err != nil {
@@ -86,20 +103,7 @@ func discoverJWKSURL(AuthorizationServer string) (string, error) {
86103
}
87104

88105
// HTTP Client
89-
client := &http.Client{
90-
Timeout: 10 * time.Second,
91-
Transport: &http.Transport{
92-
ForceAttemptHTTP2: true,
93-
MaxIdleConns: 10,
94-
IdleConnTimeout: 90 * time.Second,
95-
TLSHandshakeTimeout: 5 * time.Second,
96-
ExpectContinueTimeout: 1 * time.Second,
97-
},
98-
// Prevent redirect loops or redirects to internal sites
99-
CheckRedirect: func(req *http.Request, via []*http.Request) error {
100-
return http.ErrUseLastResponse
101-
},
102-
}
106+
client := newSecureHTTPClient()
103107

104108
resp, err := client.Get(oidcConfigURL)
105109
if err != nil {
@@ -295,7 +299,15 @@ func (a AuthService) validateJwtToken(ctx context.Context, tokenStr string) erro
295299
}
296300

297301
func (a AuthService) validateOpaqueToken(ctx context.Context, tokenStr string) error {
298-
introspectionURL := a.AuthorizationServer + "/introspect"
302+
logger, err := util.LoggerFromContext(ctx)
303+
if err != nil {
304+
return fmt.Errorf("failed to get logger from context: %w", err)
305+
}
306+
307+
introspectionURL, err := url.JoinPath(a.AuthorizationServer, "introspect")
308+
if err != nil {
309+
return fmt.Errorf("failed to construct introspection URL: %w", err)
310+
}
299311

300312
data := url.Values{}
301313
data.Set("token", tokenStr)
@@ -308,17 +320,17 @@ func (a AuthService) validateOpaqueToken(ctx context.Context, tokenStr string) e
308320
req.Header.Set("Accept", "application/json")
309321

310322
// Send request to auth server's introspection endpoint
311-
client := &http.Client{
312-
Timeout: 10 * time.Second,
313-
}
323+
client := newSecureHTTPClient()
314324

315325
resp, err := client.Do(req)
316326
if err != nil {
327+
logger.ErrorContext(ctx, "failed to call introspection endpoint: %v", err)
317328
return &MCPAuthError{Code: http.StatusInternalServerError, Message: fmt.Sprintf("failed to call introspection endpoint: %v", err), ScopesRequired: a.ScopesRequired}
318329
}
319330
defer resp.Body.Close()
320331

321332
if resp.StatusCode != http.StatusOK {
333+
logger.WarnContext(ctx, "introspection failed with status: %d", resp.StatusCode)
322334
return &MCPAuthError{Code: http.StatusUnauthorized, Message: fmt.Sprintf("introspection failed with status: %d", resp.StatusCode), ScopesRequired: a.ScopesRequired}
323335
}
324336

@@ -339,16 +351,20 @@ func (a AuthService) validateOpaqueToken(ctx context.Context, tokenStr string) e
339351
}
340352

341353
if !introspectResp.Active {
354+
logger.InfoContext(ctx, "token is not active")
342355
return &MCPAuthError{Code: http.StatusUnauthorized, Message: "token is not active", ScopesRequired: a.ScopesRequired}
343356
}
344357

345358
// Verify audience (client_id)
346359
if a.Audience != "" && introspectResp.ClientId != a.Audience {
360+
logger.WarnContext(ctx, "audience validation failed: expected %s, got %s", a.Audience, introspectResp.ClientId)
347361
return &MCPAuthError{Code: http.StatusUnauthorized, Message: "audience validation failed", ScopesRequired: a.ScopesRequired}
348362
}
349363

350-
// Verify expiration
351-
if introspectResp.Exp > 0 && time.Now().Unix() > introspectResp.Exp {
364+
// Verify expiration (with 1 minute leeway) to account for potential time difference between Toolbox and the auth server
365+
const leeway = 60
366+
if introspectResp.Exp > 0 && time.Now().Unix() > (introspectResp.Exp + leeway) {
367+
logger.WarnContext(ctx, "token has expired: exp=%d, now=%d", introspectResp.Exp, time.Now().Unix())
352368
return &MCPAuthError{Code: http.StatusUnauthorized, Message: "token has expired", ScopesRequired: a.ScopesRequired}
353369
}
354370

0 commit comments

Comments
 (0)