token

package
v1.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 26, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package token provides secure storage and retrieval of Linear API access tokens. It handles token persistence across sessions with proper encryption and file permissions to protect sensitive authentication credentials.

The package is designed to work seamlessly with the OAuth flow, storing tokens after successful authentication and providing them for API requests.

Token Storage

Tokens are stored in a platform-specific secure location (XDG standard):

~/.config/linear/token

The token file has restricted permissions (0600) to prevent unauthorized access. Only the owner can read or write the token file.

Security Features

  • File permissions restricted to owner only (0600)
  • Token validation before storage
  • Automatic directory creation with proper permissions
  • Safe file operations with atomic writes
  • Clear error messages for permission issues

Usage

Save a token after OAuth authentication:

storage := token.NewFileStorage()
err := storage.SaveToken("your-access-token")
if err != nil {
    log.Fatal(err)
}

Retrieve a stored token:

token, err := storage.GetToken()
if err != nil {
    if err == token.ErrTokenNotFound {
        // Handle missing token - user needs to authenticate
    }
    log.Fatal(err)
}

Clear stored token on logout:

err := storage.ClearToken()
if err != nil {
    log.Fatal(err)
}

Error Handling

The package defines specific errors for common scenarios:

  • ErrTokenNotFound: No token file exists (user not authenticated)
  • ErrTokenEmpty: Token file exists but is empty
  • Permission errors: Token file has incorrect permissions
  • I/O errors: File system issues during read/write operations

Token Lifecycle

1. Token Creation: OAuth flow generates access token 2. Token Storage: SaveToken() stores with secure permissions 3. Token Usage: GetToken() retrieves for API requests 4. Token Refresh: OAuth refresh flow updates stored token 5. Token Removal: ClearToken() removes on logout

Best Practices

  • Always check for ErrTokenNotFound to detect unauthenticated state
  • Handle token refresh proactively before expiration
  • Clear tokens on logout to maintain security
  • Never log or display token values
  • Validate tokens before storage to prevent corruption

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FormatAuthHeader added in v1.2.0

func FormatAuthHeader(token string) string

FormatAuthHeader formats a token for use in an Authorization header. Linear personal API keys (lin_api_*) must be sent without a Bearer prefix. OAuth access tokens are sent with "Bearer ".

func GetDefaultTokenPath

func GetDefaultTokenPath() string

GetDefaultTokenPath returns the default path for storing tokens.

Token location strategy: - Primary: ~/.config/linear/token (XDG standard) - Fallback: .config/linear/token (current directory)

Why home directory: Tokens are user-specific credentials. Storing them in the home directory ensures they're not accidentally committed to version control and are isolated per user on multi-user systems.

func IsExpired added in v1.1.0

func IsExpired(data *TokenData) bool

IsExpired checks if the token has expired. Returns false for tokens without expiration (legacy tokens or long-lived tokens).

func LoadTokenFromEnv added in v1.6.0

func LoadTokenFromEnv() string

LoadTokenFromEnv loads a Linear token from environment variables. Uses LINEAR_API_KEY as the canonical env var for direct API-key auth.

func LoadTokenWithFallback

func LoadTokenWithFallback() string

LoadTokenWithFallback loads token from default location with env var fallback

func NeedsRefresh added in v1.1.0

func NeedsRefresh(data *TokenData, buffer time.Duration) bool

NeedsRefresh checks if the token should be proactively refreshed. Returns true if the token will expire within the buffer duration. Returns false for tokens without expiration.

func SanitizeToken added in v1.2.0

func SanitizeToken(token string) string

SanitizeToken removes invalid HTTP header characters from a token. Removes all whitespace characters: spaces, tabs, newlines, carriage returns. This prevents "invalid header field value" errors when setting Authorization headers.

func ValidateToken added in v1.2.0

func ValidateToken(token string) error

ValidateToken checks if a token contains invalid characters. Returns an error if the token contains characters that would cause HTTP header issues.

Types

type NoRefreshTokenError added in v1.1.0

type NoRefreshTokenError struct{}

Error types for specific refresh failure scenarios

func (*NoRefreshTokenError) Error added in v1.1.0

func (e *NoRefreshTokenError) Error() string

type OAuthRefresher added in v1.1.0

type OAuthRefresher interface {
	RefreshAccessToken(refreshToken string) (*TokenData, error)
}

OAuthRefresher defines the interface for refreshing OAuth tokens. This allows the refresher to be independent of the oauth package implementation, avoiding circular dependencies.

type RefreshFailedError added in v1.1.0

type RefreshFailedError struct {
	Reason error
}

func (*RefreshFailedError) Error added in v1.1.0

func (e *RefreshFailedError) Error() string

type Refresher added in v1.1.0

type Refresher struct {
	// contains filtered or unexported fields
}

Refresher handles thread-safe automatic token refresh. It implements double-checked locking to prevent thundering herd on concurrent 401s.

func NewRefresher added in v1.1.0

func NewRefresher(storage *Storage, oauthHandler OAuthRefresher) *Refresher

NewRefresher creates a new token refresher with default settings.

func (*Refresher) GetValidToken added in v1.1.0

func (r *Refresher) GetValidToken() (string, error)

GetValidToken returns a valid access token, proactively refreshing if needed. This should be called before making API requests to ensure the token is fresh.

func (*Refresher) RefreshIfNeeded added in v1.1.0

func (r *Refresher) RefreshIfNeeded(originalToken string) (string, error)

RefreshIfNeeded handles reactive refresh when a 401 error occurs. Uses double-checked locking to prevent multiple concurrent refresh attempts.

Parameters:

  • originalToken: The token that was used in the failed request

Returns:

  • New access token if refresh succeeded
  • Error if refresh failed or no refresh token available

type RefreshingProvider added in v1.1.0

type RefreshingProvider struct {
	// contains filtered or unexported fields
}

RefreshingProvider provides tokens with automatic refresh capability. Used for new OAuth apps (created after Oct 1, 2025) with 24-hour token expiration.

func NewRefreshingProvider added in v1.1.0

func NewRefreshingProvider(refresher *Refresher) *RefreshingProvider

NewRefreshingProvider creates a provider that automatically refreshes tokens

func (*RefreshingProvider) GetToken added in v1.1.0

func (p *RefreshingProvider) GetToken() (string, error)

GetToken returns a valid token, proactively refreshing if expiring soon

func (*RefreshingProvider) RefreshIfNeeded added in v1.1.0

func (p *RefreshingProvider) RefreshIfNeeded(failedToken string) (string, error)

RefreshIfNeeded reactively refreshes the token when a 401 occurs

type SessionExpiredError added in v1.1.0

type SessionExpiredError struct{}

func (*SessionExpiredError) Error added in v1.1.0

func (e *SessionExpiredError) Error() string

type StaticProvider added in v1.1.0

type StaticProvider struct {
	// contains filtered or unexported fields
}

StaticProvider provides a static token without refresh capability. Used for: - Legacy OAuth apps with long-lived tokens (10 year expiration) - Environment variable tokens - API tokens without OAuth refresh

func NewStaticProvider added in v1.1.0

func NewStaticProvider(token string) *StaticProvider

NewStaticProvider creates a provider for non-refreshable tokens

func (*StaticProvider) GetToken added in v1.1.0

func (p *StaticProvider) GetToken() (string, error)

GetToken returns the static token (sanitized for safety)

func (*StaticProvider) RefreshIfNeeded added in v1.1.0

func (p *StaticProvider) RefreshIfNeeded(failedToken string) (string, error)

RefreshIfNeeded always fails for static tokens

type Storage

type Storage struct {
	// contains filtered or unexported fields
}

Storage handles token storage and retrieval with secure file permissions. Tokens are sensitive credentials that must be protected from unauthorized access.

func NewStorage

func NewStorage(tokenPath string) *Storage

NewStorage creates a new token storage instance

func (*Storage) DeleteToken

func (s *Storage) DeleteToken() error

DeleteToken removes the token file

func (*Storage) LoadToken

func (s *Storage) LoadToken() (string, error)

LoadToken loads a token from the file system

func (*Storage) LoadTokenData added in v1.1.0

func (s *Storage) LoadTokenData() (*TokenData, error)

LoadTokenData loads structured token data with backward compatibility.

Behavior: - If file contains JSON: parse and return TokenData - If file contains plain string: treat as legacy access token (no refresh) - Returns error only on file read failure or invalid JSON

This enables seamless migration from legacy token format to new format.

func (*Storage) SaveToken

func (s *Storage) SaveToken(token string) error

SaveToken saves a token to the file system with secure permissions.

Security measures: - Directory created with 0700 (owner only access) - Token file created with 0600 (owner read/write only) - Atomic write operation to prevent partial token storage

Why these permissions: OAuth tokens are bearer tokens - anyone with the token can act as the authenticated user. Restricting file permissions prevents other users on the system from reading the token.

func (*Storage) SaveTokenData added in v1.1.0

func (s *Storage) SaveTokenData(data *TokenData) error

SaveTokenData saves structured token data as JSON with secure permissions. This replaces the legacy plain-string token format and enables automatic refresh.

func (*Storage) TokenExists deprecated

func (s *Storage) TokenExists() bool

TokenExists checks if a token file exists. Returns false for file not found and logs errors for other issues like permission denied.

Why log but not fail: This maintains backward compatibility while still alerting developers to potential issues. Permission errors might indicate security problems that should be investigated.

Deprecated: Use TokenExistsWithError for better error handling.

func (*Storage) TokenExistsWithError

func (s *Storage) TokenExistsWithError() (bool, error)

TokenExistsWithError checks if a token file exists and returns detailed error information. Returns (false, nil) if file doesn't exist - this is not an error condition. Returns (false, error) if there's an actual error like permission denied.

Why distinguish between "not found" and other errors: - File not found is expected when user hasn't authenticated yet - Permission errors indicate a security or configuration problem - This allows callers to handle these cases differently

type TokenData added in v1.1.0

type TokenData struct {
	AccessToken  string    `json:"access_token"`
	RefreshToken string    `json:"refresh_token,omitempty"`
	TokenType    string    `json:"token_type"`
	ExpiresAt    time.Time `json:"expires_at,omitempty"`
	Scope        string    `json:"scope"`
	AuthMode     string    `json:"auth_mode,omitempty"` // "user" or "agent" - determines how "me" resolves
}

TokenData represents structured OAuth token information with expiration tracking. Supports both refresh-capable tokens (new OAuth apps created after Oct 1, 2025) and legacy tokens without expiration.

type TokenProvider added in v1.1.0

type TokenProvider interface {
	// GetToken returns a valid access token, refreshing if needed (proactive refresh)
	GetToken() (string, error)

	// RefreshIfNeeded attempts to refresh the token when a 401 error occurs (reactive refresh)
	// Takes the token that failed as a parameter to enable double-checked locking
	RefreshIfNeeded(failedToken string) (string, error)
}

TokenProvider provides access to authentication tokens with optional refresh capability. This interface abstracts token management for the Linear API client.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL