Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 30 additions & 8 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"time"

"github.com/DataDog/datadog-api-client-go/v2/api/datadog"
"github.com/DataDog/pup/pkg/auth/dcr"
"github.com/DataDog/pup/pkg/auth/storage"
"github.com/DataDog/pup/pkg/config"
"github.com/DataDog/pup/pkg/useragent"
Expand All @@ -25,6 +26,12 @@ type Client struct {
api *datadog.APIClient
}

// Test hooks — overridden in tests to inject fakes
var (
getStorageFunc = func() (storage.Storage, error) { return storage.GetStorage(nil) }
newDCRClientFunc = func(site string) *dcr.Client { return dcr.NewClient(site) }
)

// New creates a new Datadog API client
// Authentication priority:
// 1. OAuth2 tokens (if available and valid)
Expand All @@ -45,16 +52,31 @@ func NewWithOptions(cfg *config.Config, forceAPIKeys bool) (*Client, error) {

if !forceAPIKeys {
// Try OAuth2 tokens first (preferred method)
store, err := storage.GetStorage(nil)
store, err := getStorageFunc()
if err == nil {
tokens, err := store.LoadTokens(cfg.Site)
if err == nil && tokens != nil && !tokens.IsExpired() {
// Use OAuth2 Bearer token authentication
ctx = context.WithValue(
context.Background(),
datadog.ContextAccessToken,
tokens.AccessToken,
)
if err == nil && tokens != nil {
// Auto-refresh: if token is expired but refresh token is available, refresh it
if tokens.IsExpired() && tokens.RefreshToken != "" {
creds, credsErr := store.LoadClientCredentials(cfg.Site)
if credsErr == nil && creds != nil {
dcrClient := newDCRClientFunc(cfg.Site)
newTokens, refreshErr := dcrClient.RefreshToken(tokens.RefreshToken, creds)
if refreshErr == nil {
_ = store.SaveTokens(cfg.Site, newTokens)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Surface refresh token save failures

When refresh succeeds, the code ignores store.SaveTokens errors and continues with the in-memory token. This creates a latent auth regression whenever keychain/file writes fail (e.g., locked keychain, permission issues): the current command appears to work, but the next process reloads stale tokens and may fail to refresh (especially if the refresh token rotated), forcing unexpected re-login or auth errors. This path was introduced by the new auto-refresh flow, so persistence failures should be handled explicitly instead of being silently dropped.

Useful? React with 👍 / 👎.

tokens = newTokens
}
}
}

if !tokens.IsExpired() {
// Use OAuth2 Bearer token authentication
ctx = context.WithValue(
context.Background(),
datadog.ContextAccessToken,
tokens.AccessToken,
)
}
}
}
}
Expand Down
Loading