Thanks to visit codestin.com
Credit goes to github.com

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
19 changes: 13 additions & 6 deletions coderd/externalauth/externalauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ type Config struct {
RefreshRetryMaxBackoff time.Duration
// RefreshRetryTimeout overrides the total budget for retrying a transient
Comment thread
jscottmiller marked this conversation as resolved.
// refresh failure across all attempts. A zero value applies
// defaultRefreshRetryTimeout.
// defaultRefreshRetryTimeout. A negative value disables transient-failure
// retries entirely, so exactly one refresh attempt is made.
RefreshRetryTimeout time.Duration
}

Expand Down Expand Up @@ -389,9 +390,9 @@ validate:

// refreshTokenWithRetry exchanges the refresh token for a new access token,
// retrying with exponential backoff on transient failures. Permanent
// failures (as classified by isFailedRefresh) and the no-op case where no
// refresh token is set bypass the retry loop so a doomed refresh is not
// repeatedly attempted.
// failures (as classified by isFailedRefresh), the no-op case where no
// refresh token is set, and a negative RefreshRetryTimeout all bypass the
// retry loop so a doomed or unwanted refresh is not repeatedly attempted.
func (c *Config) refreshTokenWithRetry(ctx context.Context, existingToken *oauth2.Token) (*oauth2.Token, error) {
// Without a refresh token the oauth2 library short-circuits with
// "token expired and refresh token is not set". No retry can recover
Expand All @@ -400,6 +401,12 @@ func (c *Config) refreshTokenWithRetry(ctx context.Context, existingToken *oauth
return c.TokenSource(ctx, existingToken).Token()
}

// A negative RefreshRetryTimeout disables retries entirely, so make a
// single attempt and return.
if c.RefreshRetryTimeout < 0 {
return c.TokenSource(ctx, existingToken).Token()
}

initial := c.RefreshRetryInitialBackoff
if initial <= 0 {
initial = defaultRefreshRetryInitialBackoff
Expand All @@ -409,7 +416,7 @@ func (c *Config) refreshTokenWithRetry(ctx context.Context, existingToken *oauth
maximum = defaultRefreshRetryMaxBackoff
}
total := c.RefreshRetryTimeout
if total <= 0 {
if total == 0 {
total = defaultRefreshRetryTimeout
}

Expand All @@ -430,7 +437,7 @@ func (c *Config) refreshTokenWithRetry(ctx context.Context, existingToken *oauth
// retry.Wait selects between time.After(delay) and ctx.Done(); when
// delay is zero and the context is already canceled the two cases
// race nondeterministically, which would cause an unwanted extra
// refresh attempt with a near-zero budget (notably in tests).
// refresh attempt with a near-zero budget.
if retryCtx.Err() != nil {
return token, err
}
Expand Down
14 changes: 8 additions & 6 deletions coderd/externalauth/externalauth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,10 @@ func TestRefreshToken(t *testing.T) {
// refresh attempts should ever happen. An invalid refresh token does
// not magically become valid at some point in the future.
//
// Internal retries are disabled in this subtest via RefreshRetryTimeout
// so each RefreshToken call results in exactly one IDP refresh attempt.
// The RefreshTokenWithBackoff subtest covers the retry-with-backoff path.
// Internal retries are disabled in this subtest via a negative
// RefreshRetryTimeout so each RefreshToken call results in exactly one
// IDP refresh attempt. The RefreshTokenWithBackoff subtest covers the
// retry-with-backoff path.
t.Run("RefreshRetries", func(t *testing.T) {
t.Parallel()

Expand All @@ -182,9 +183,10 @@ func TestRefreshToken(t *testing.T) {
}),
},
ExternalAuthOpt: func(cfg *externalauth.Config) {
// Disable transient-error retries so the assertion below
// (1 IDP call per RefreshToken) holds.
cfg.RefreshRetryTimeout = time.Nanosecond
// Negative timeout disables retries (1 IDP call per RefreshToken).
// A tiny positive timeout is unreliable on coarse-clock platforms
// (Windows).
cfg.RefreshRetryTimeout = -1
},
})

Expand Down
Loading