package client import ( "context" "encoding/json" "fmt" "os" "path/filepath" "time" "golang.org/x/oauth2" ) // TokenCache handles token persistence type TokenCache struct { filePath string } // NewTokenCache creates a new token cache func NewTokenCache(filePath string) *TokenCache { return &TokenCache{filePath: filePath} } // Load loads a token from cache func (tc *TokenCache) Load() (*oauth2.Token, error) { // Check if file exists if _, err := os.Stat(tc.filePath); os.IsNotExist(err) { return nil, nil // No cached token } // Read file data, err := os.ReadFile(tc.filePath) if err != nil { return nil, fmt.Errorf("failed to read token cache: %w", err) } // Unmarshal token var token oauth2.Token if err := json.Unmarshal(data, &token); err != nil { return nil, fmt.Errorf("failed to unmarshal token: %w", err) } // Check if token is expired if token.Expiry.Before(time.Now()) { return nil, nil // Token expired } return &token, nil } // Save saves a token to cache func (tc *TokenCache) Save(token *oauth2.Token) error { // Create directory if it doesn't exist dir := filepath.Dir(tc.filePath) if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("failed to create token cache directory: %w", err) } // Marshal token data, err := json.MarshalIndent(token, "", " ") if err != nil { return fmt.Errorf("failed to marshal token: %w", err) } // Write to file with restricted permissions if err := os.WriteFile(tc.filePath, data, 0600); err != nil { return fmt.Errorf("failed to write token cache: %w", err) } return nil } // Authenticate authenticates with Zwift using username and password func Authenticate(ctx context.Context, username, password string, tokenCache *TokenCache) (*oauth2.Token, error) { // Try to load cached token first if tokenCache != nil { token, err := tokenCache.Load() if err == nil && token != nil && token.Valid() { return token, nil } } // Authenticate with username/password // Note: The gravl library handles the actual OAuth2 flow // We'll use the credentials directly with the client // For now, we'll return nil as the gravl client handles authentication // This function serves as a placeholder for explicit token management return nil, fmt.Errorf("authentication handled by gravl client") }