Skip to content

Commit 7863289

Browse files
authored
Merge 65f70ba into 27c5619
2 parents 27c5619 + 65f70ba commit 7863289

3 files changed

Lines changed: 66 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# v1.3.1
2+
## Fixes
3+
- Reuse OAuth2 token source to prevent unnecessary token fetches for each request.
4+
15
# v1.3.0
26

37
## Features

auth_providers/auth_oauth.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2024 Keyfactor
1+
// Copyright 2026 Keyfactor
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import (
2222
"net/http"
2323
"os"
2424
"strings"
25+
"sync"
2526
"time"
2627

2728
"golang.org/x/oauth2"
@@ -115,6 +116,10 @@ type CommandConfigOauth struct {
115116

116117
// TokenURL is the token URL for OAuth authentication
117118
TokenURL string `json:"token_url,omitempty" yaml:"token_url,omitempty"`
119+
120+
// unexported: lazily initialized, shared across GetHttpClient() calls
121+
tokenSource oauth2.TokenSource
122+
tsMu sync.Mutex
118123
}
119124

120125
// NewOAuthAuthenticatorBuilder creates a new CommandConfigOauth instance.
@@ -222,7 +227,14 @@ func (b *CommandConfigOauth) GetHttpClient() (*http.Client, error) {
222227
}
223228

224229
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{Transport: baseTransport})
225-
tokenSource := config.TokenSource(ctx)
230+
231+
// Lazily initialize the token source and cache it
232+
b.tsMu.Lock()
233+
if b.tokenSource == nil {
234+
b.tokenSource = config.TokenSource(ctx)
235+
}
236+
tokenSource := b.tokenSource
237+
b.tsMu.Unlock()
226238

227239
client = http.Client{
228240
Transport: &oauth2Transport{

auth_providers/auth_oauth_test.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2024 Keyfactor
1+
// Copyright 2026 Keyfactor
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -16,9 +16,11 @@ package auth_providers_test
1616

1717
import (
1818
"crypto/tls"
19+
"encoding/json"
1920
"encoding/pem"
2021
"fmt"
2122
"net/http"
23+
"net/http/httptest"
2224
"net/url"
2325
"os"
2426
"path/filepath"
@@ -568,3 +570,48 @@ func DownloadCertificate(input string, outputPath string) error {
568570
fmt.Printf("Certificate chain saved to: %s\n", outputFile)
569571
return nil
570572
}
573+
574+
func TestCommandConfigOauth_TokenSourceIsReused(t *testing.T) {
575+
tokenRequestCount := 0
576+
577+
// Fake IdP token endpoint
578+
tokenServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
579+
tokenRequestCount++
580+
w.Header().Set("Content-Type", "application/json")
581+
json.NewEncoder(w).Encode(map[string]interface{}{
582+
"access_token": "shared-test-token",
583+
"token_type": "Bearer",
584+
"expires_in": 3600,
585+
})
586+
}))
587+
defer tokenServer.Close()
588+
589+
// Fake API endpoint (just needs to accept requests)
590+
apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
591+
w.WriteHeader(http.StatusOK)
592+
}))
593+
defer apiServer.Close()
594+
595+
config := &auth_providers.CommandConfigOauth{
596+
ClientID: "test-client-id",
597+
ClientSecret: "test-client-secret",
598+
TokenURL: tokenServer.URL + "/token",
599+
}
600+
601+
// Get multiple clients from the same config
602+
const numClients = 3
603+
for i := 0; i < numClients; i++ {
604+
client, err := config.GetHttpClient()
605+
if err != nil {
606+
t.Fatalf("GetHttpClient() call %d failed: %v", i+1, err)
607+
}
608+
_, err = client.Get(apiServer.URL)
609+
if err != nil {
610+
t.Fatalf("request %d failed: %v", i+1, err)
611+
}
612+
}
613+
614+
if tokenRequestCount != 1 {
615+
t.Errorf("expected token endpoint to be called once, got %d — token source is not being reused across GetHttpClient() calls", tokenRequestCount)
616+
}
617+
}

0 commit comments

Comments
 (0)