package store import ( "encoding/json" "fmt" "os" "path/filepath" "time" ) type TokenSet struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` TokenType string `json:"token_type"` Expiry time.Time `json:"expiry"` Scopes []string `json:"scopes,omitempty"` Account string `json:"account,omitempty"` } func (t *TokenSet) IsExpired() bool { if t.Expiry.IsZero() { return false } return time.Now().After(t.Expiry) } type TokenStore struct { path string } func NewTokenStore(path string) *TokenStore { return &TokenStore{path: path} } func (s *TokenStore) Save(token *TokenSet) error { if err := os.MkdirAll(filepath.Dir(s.path), 0700); err != nil { return fmt.Errorf("create token dir: %w", err) } data, err := json.MarshalIndent(token, "", " ") if err != nil { return fmt.Errorf("marshal token: %w", err) } tmp := s.path + ".tmp" if err := os.WriteFile(tmp, data, 0600); err != nil { return fmt.Errorf("write token file: %w", err) } if err := os.Rename(tmp, s.path); err != nil { return fmt.Errorf("rename token file: %w", err) } return nil } func (s *TokenStore) Load() (*TokenSet, error) { data, err := os.ReadFile(s.path) if err != nil { if os.IsNotExist(err) { return nil, nil } return nil, fmt.Errorf("read token file: %w", err) } var token TokenSet if err := json.Unmarshal(data, &token); err != nil { return nil, fmt.Errorf("unmarshal token: %w", err) } return &token, nil } func (s *TokenStore) Delete() error { if err := os.Remove(s.path); err != nil && !os.IsNotExist(err) { return fmt.Errorf("delete token file: %w", err) } return nil } func (s *TokenStore) Exists() bool { _, err := os.Stat(s.path) return err == nil }