Skip to content

Commit 300d509

Browse files
committed
Document TokenStore requirements and add ErrNoToken sentinel error
1 parent 35199e0 commit 300d509

File tree

2 files changed

+32
-18
lines changed

2 files changed

+32
-18
lines changed

client/transport/oauth.go

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import (
1414
"time"
1515
)
1616

17+
// ErrNoToken is returned when no token is available in the token store
18+
var ErrNoToken = errors.New("no token available")
19+
1720
// OAuthConfig holds the OAuth configuration for the client
1821
type OAuthConfig struct {
1922
// ClientID is the OAuth client ID
@@ -33,11 +36,25 @@ type OAuthConfig struct {
3336
PKCEEnabled bool
3437
}
3538

36-
// TokenStore is an interface for storing and retrieving OAuth tokens
39+
// TokenStore is an interface for storing and retrieving OAuth tokens.
40+
//
41+
// Implementations must:
42+
// - Honor context cancellation and deadlines, returning context.Canceled
43+
// or context.DeadlineExceeded as appropriate
44+
// - Return ErrNoToken (or a sentinel error that wraps it) when no token
45+
// is available, rather than conflating this with other operational errors
46+
// - Properly propagate all other errors (database failures, I/O errors, etc.)
47+
// - Check ctx.Done() before performing operations and return ctx.Err() if cancelled
3748
type TokenStore interface {
38-
// GetToken returns the current token
49+
// GetToken returns the current token.
50+
// Returns ErrNoToken if no token is available.
51+
// Returns context.Canceled or context.DeadlineExceeded if ctx is cancelled.
52+
// Returns other errors for operational failures (I/O, database, etc.).
3953
GetToken(ctx context.Context) (*Token, error)
40-
// SaveToken saves a token
54+
55+
// SaveToken saves a token.
56+
// Returns context.Canceled or context.DeadlineExceeded if ctx is cancelled.
57+
// Returns other errors for operational failures (I/O, database, etc.).
4158
SaveToken(ctx context.Context, token *Token) error
4259
}
4360

@@ -76,20 +93,23 @@ func NewMemoryTokenStore() *MemoryTokenStore {
7693
return &MemoryTokenStore{}
7794
}
7895

79-
// GetToken returns the current token
96+
// GetToken returns the current token.
97+
// Returns ErrNoToken if no token is available.
98+
// Returns context.Canceled or context.DeadlineExceeded if ctx is cancelled.
8099
func (s *MemoryTokenStore) GetToken(ctx context.Context) (*Token, error) {
81100
if err := ctx.Err(); err != nil {
82101
return nil, err
83102
}
84103
s.mu.RLock()
85104
defer s.mu.RUnlock()
86105
if s.token == nil {
87-
return nil, errors.New("no token available")
106+
return nil, ErrNoToken
88107
}
89108
return s.token, nil
90109
}
91110

92-
// SaveToken saves a token
111+
// SaveToken saves a token.
112+
// Returns context.Canceled or context.DeadlineExceeded if ctx is cancelled.
93113
func (s *MemoryTokenStore) SaveToken(ctx context.Context, token *Token) error {
94114
if err := ctx.Err(); err != nil {
95115
return err
@@ -157,10 +177,8 @@ func (h *OAuthHandler) GetAuthorizationHeader(ctx context.Context) (string, erro
157177
// getValidToken returns a valid token, refreshing if necessary
158178
func (h *OAuthHandler) getValidToken(ctx context.Context) (*Token, error) {
159179
token, err := h.config.TokenStore.GetToken(ctx)
160-
if err != nil {
161-
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
162-
return nil, err
163-
}
180+
if err != nil && !errors.Is(err, ErrNoToken) {
181+
return nil, err
164182
}
165183
if err == nil && !token.IsExpired() && token.AccessToken != "" {
166184
return token, nil
@@ -229,12 +247,8 @@ func (h *OAuthHandler) refreshToken(ctx context.Context, refreshToken string) (*
229247
}
230248

231249
// If no new refresh token is provided, keep the old one
232-
oldToken, oldErr := h.config.TokenStore.GetToken(ctx)
233-
if oldErr != nil && (errors.Is(oldErr, context.Canceled) || errors.Is(oldErr, context.DeadlineExceeded)) {
234-
return nil, oldErr
235-
}
236-
if tokenResp.RefreshToken == "" && oldToken != nil {
237-
tokenResp.RefreshToken = oldToken.RefreshToken
250+
if tokenResp.RefreshToken == "" {
251+
tokenResp.RefreshToken = refreshToken
238252
}
239253

240254
// Save the token

client/transport/oauth_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ func TestMemoryTokenStore(t *testing.T) {
5858

5959
// Test getting token from empty store
6060
_, err := store.GetToken(ctx)
61-
if err == nil {
62-
t.Errorf("Expected error when getting token from empty store")
61+
if !errors.Is(err, ErrNoToken) {
62+
t.Errorf("Expected ErrNoToken when getting token from empty store, got %v", err)
6363
}
6464

6565
// Create a test token

0 commit comments

Comments
 (0)