package auth import ( "encoding/json" "fmt" "os" "time" ) // TokenSet is persisted at tokens.json mode 0600. type TokenSet struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token,omitempty"` TokenType string `json:"token_type,omitempty"` Expiry time.Time `json:"expiry,omitempty"` ClientID string `json:"client_id,omitempty"` ClientSecret string `json:"client_secret,omitempty"` AuthURL string `json:"auth_url,omitempty"` TokenURL string `json:"token_url,omitempty"` RedirectURL string `json:"redirect_url,omitempty"` AccountID string `json:"account_id,omitempty"` } // WriteTokens stores OAuth tokens with mode 0600. func WriteTokens(path, access, refresh string) error { return WriteTokenSet(path, TokenSet{AccessToken: access, RefreshToken: refresh, TokenType: "Bearer"}) } // WriteTokenSet writes the full token record. func WriteTokenSet(path string, t TokenSet) error { b, err := json.MarshalIndent(t, "", " ") if err != nil { return fmt.Errorf("marshal tokens: %w", err) } if err := os.WriteFile(path, append(b, '\n'), 0o600); err != nil { return fmt.Errorf("write tokens: %w", err) } return nil } // ReadTokens loads tokens.json. func ReadTokens(path string) (TokenSet, error) { b, err := os.ReadFile(path) if err != nil { return TokenSet{}, err } var t TokenSet if err := json.Unmarshal(b, &t); err != nil { return TokenSet{}, fmt.Errorf("parse tokens: %w", err) } if t.AccessToken == "" && t.RefreshToken == "" { return TokenSet{}, fmt.Errorf("tokens: missing access_token") } return t, nil }