auth

package
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Mar 16, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DeviceCodeEndpoint  = "/api/auth/device/code"  //nolint:gosec // not a credential
	DeviceTokenEndpoint = "/api/auth/device/token" //nolint:gosec // not a credential
	UserInfoEndpoint    = "/oauth2/userinfo"
)

Device Flow endpoints (RFC 8628)

View Source
const (
	TokenEndpoint  = "/oauth2/token" //nolint:gosec // not a credential
	RevokeEndpoint = "/oauth2/revoke"
)

OAuth 2.0 endpoints

View Source
const ClientID = "ox"

ClientID is the OAuth client identifier for the ox CLI

Variables

View Source
var DefaultScopes = []string{"user:profile", "sageox:write"}

DefaultScopes are the OAuth scopes requested during authentication

Functions

func GetAuthFilePath

func GetAuthFilePath() (string, error)

GetAuthFilePath returns the path to the auth token file.

Path Resolution (via internal/paths package):

Default:           ~/.sageox/config/auth.json
With OX_XDG_ENABLE: $XDG_CONFIG_HOME/sageox/auth.json

See internal/paths/doc.go for architecture rationale.

func GetLoggedInEndpoints

func GetLoggedInEndpoints() []string

GetLoggedInEndpoints returns all endpoints with valid (non-expired) tokens. Returns nil if no valid tokens exist.

func GetUserID added in v0.2.0

func GetUserID(ep string) string

GetUserID returns the authenticated user's unique ID for the given endpoint. Returns empty string if not authenticated or on error.

func IsAuthRequired

func IsAuthRequired() bool

IsAuthRequired checks if authentication is required based on the FEATURE_AUTH environment variable

func IsAuthenticated

func IsAuthenticated() (bool, error)

IsAuthenticated checks if a valid non-expired token exists for the current endpoint

func IsAuthenticatedForEndpoint

func IsAuthenticatedForEndpoint(endpoint string) (bool, error)

IsAuthenticatedForEndpoint checks if a valid non-expired token exists for a specific endpoint

func IsCloudEnabled

func IsCloudEnabled() bool

IsCloudEnabled checks if cloud features are enabled based on the FEATURE_CLOUD environment variable

func IsMemoryEnabled added in v0.3.0

func IsMemoryEnabled() bool

IsMemoryEnabled checks if memory features (ox memory, ox agent <id> distill) are enabled. Memory is experimental and not included in the default experience. Set FEATURE_MEMORY=true to enable.

func IsPostMVPEnabled

func IsPostMVPEnabled() bool

IsPostMVPEnabled checks if post-MVP features (ox completion) are enabled. These features are for power users and not included in the MVP release. Set FEATURE_POST_MVP=true to enable.

func IsValidServerSessionID

func IsValidServerSessionID(id string) bool

IsValidServerSessionID validates that the session ID has the correct format (starts with "oxsid_" and contains a valid ULID). Works for both client-generated and server-generated session IDs.

func ListEndpoints

func ListEndpoints() ([]string, error)

ListEndpoints returns all endpoints that have stored tokens

func Login

func Login(ctx context.Context, deviceCode *DeviceCodeResponse, statusCallback func(string)) error

Login polls for the device authorization and completes the login process

func NewServerSessionID

func NewServerSessionID() string

NewServerSessionID generates a new server session ID in the format: oxsid_<ulid>. This is used as fallback when server doesn't provide a session ID (offline mode).

func RemoveToken

func RemoveToken() error

RemoveToken deletes the authentication token for the current API endpoint

func RemoveTokenForEndpoint

func RemoveTokenForEndpoint(ep string) error

RemoveTokenForEndpoint deletes the authentication token for a specific API endpoint

func RequireAuth

func RequireAuth() error

RequireAuth checks if authentication is required and if the user is authenticated Returns an error if authentication is required but user is not authenticated

func RevokeToken

func RevokeToken() (bool, error)

RevokeToken revokes token and removes from local storage.

Best-effort server revocation:

  • Prefers revoking refresh_token (more secure, invalidates both tokens)
  • Falls back to access_token if refresh_token missing
  • Always removes local token regardless of server response

Returns:

  • true if server revocation succeeded (or no token to revoke)
  • false if server revocation failed
  • error if local token removal fails

Local token is always removed regardless of return value (unless removal itself fails).

func SaveToken

func SaveToken(token *StoredToken) error

SaveToken saves the authentication token for the current API endpoint

func SaveTokenForEndpoint

func SaveTokenForEndpoint(ep string, token *StoredToken) error

SaveTokenForEndpoint saves the authentication token for a specific API endpoint

func ServerSessionFromResponse

func ServerSessionFromResponse(serverSessionID string) string

ServerSessionFromResponse validates and returns a server-provided session ID. If the server ID is empty or invalid, generates a new client-side session ID as fallback for offline/degraded operation.

Design decision: Client fallback for offline Rationale: Graceful degradation - offline agents still get sessions; server-side preferred when available for security benefits.

Types

type APIError

type APIError struct {
	StatusCode int
	Message    string
}

APIError is raised when API returns an error response

func (*APIError) Error

func (e *APIError) Error() string

type APIResponse

type APIResponse struct {
	StatusCode int
	Data       interface{} // parsed JSON or nil
	Headers    http.Header
}

APIResponse represents a response from an authenticated API request

func AuthenticatedRequest

func AuthenticatedRequest(ctx context.Context, method, url string, data interface{}) (*APIResponse, error)

AuthenticatedRequest makes an authenticated HTTP request to the API.

Adds Bearer token to Authorization header. If request returns 401, attempts token refresh and retries once.

Args:

  • ctx: Context for cancellation and timeout
  • method: HTTP method (GET, POST, PUT, DELETE, etc.)
  • url: Full URL to request
  • data: Optional JSON body (will be serialized)

Returns:

  • APIResponse with status, data, headers

Errors:

  • AuthenticationError: If not logged in
  • TokenRefreshError: If token refresh fails
  • APIError: If API returns error after retry

func (*APIResponse) Ok

func (r *APIResponse) Ok() bool

Ok returns true if the status code is 2xx

type AuthClient

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

AuthClient provides authentication operations with configurable storage location. Use NewAuthClient() for production or NewAuthClientWithDir() for testing.

func NewAuthClient

func NewAuthClient() *AuthClient

NewAuthClient creates an AuthClient using the default config directory and endpoint.

func NewAuthClientWithDir

func NewAuthClientWithDir(configDir string) *AuthClient

NewAuthClientWithDir creates an AuthClient with a custom config directory. Primarily used for testing to isolate each test's auth storage.

func (*AuthClient) AuthenticatedRequest

func (c *AuthClient) AuthenticatedRequest(ctx context.Context, method, url string, data interface{}) (*APIResponse, error)

AuthenticatedRequest makes an authenticated HTTP request using this client's token storage. This method enables test isolation by using the client's configDir for token storage.

func (*AuthClient) Endpoint

func (c *AuthClient) Endpoint() string

Endpoint returns the configured API endpoint.

func (*AuthClient) EnsureValidToken

func (c *AuthClient) EnsureValidToken(bufferSeconds int) (*StoredToken, error)

EnsureValidToken ensures we have a valid token, refreshing proactively if needed.

func (*AuthClient) GetAuthFilePath

func (c *AuthClient) GetAuthFilePath() (string, error)

GetAuthFilePath returns the path to the auth token file for this client.

func (*AuthClient) GetToken

func (c *AuthClient) GetToken() (*StoredToken, error)

GetToken loads the authentication token for this client's endpoint

func (*AuthClient) GetTokenForEndpoint

func (c *AuthClient) GetTokenForEndpoint(ep string) (*StoredToken, error)

GetTokenForEndpoint loads the authentication token for a specific API endpoint

func (*AuthClient) Handle401Error

func (c *AuthClient) Handle401Error(token *StoredToken) (*StoredToken, error)

Handle401Error handles 401 Unauthorized by attempting token refresh.

func (*AuthClient) IsAuthenticated

func (c *AuthClient) IsAuthenticated() (bool, error)

IsAuthenticated checks if a valid non-expired token exists for this client's endpoint

func (*AuthClient) IsAuthenticatedForEndpoint

func (c *AuthClient) IsAuthenticatedForEndpoint(ep string) (bool, error)

IsAuthenticatedForEndpoint checks if a valid non-expired token exists for a specific endpoint

func (*AuthClient) Login

func (c *AuthClient) Login(ctx context.Context, deviceCode *DeviceCodeResponse, statusCallback func(string)) error

Login polls for the device authorization and completes the login process.

func (*AuthClient) RemoveToken

func (c *AuthClient) RemoveToken() error

RemoveToken deletes the authentication token for this client's endpoint

func (*AuthClient) RemoveTokenForEndpoint

func (c *AuthClient) RemoveTokenForEndpoint(ep string) error

RemoveTokenForEndpoint deletes the authentication token for a specific API endpoint

func (*AuthClient) RequestDeviceCode

func (c *AuthClient) RequestDeviceCode() (*DeviceCodeResponse, error)

RequestDeviceCode initiates the device authorization flow.

func (*AuthClient) RevokeToken

func (c *AuthClient) RevokeToken() (bool, error)

RevokeToken revokes token and removes from local storage for this client.

Best-effort server revocation:

  • Prefers revoking refresh_token (more secure, invalidates both tokens)
  • Falls back to access_token if refresh_token missing
  • Always removes local token regardless of server response

Returns:

  • true if server revocation succeeded (or no token to revoke)
  • false if server revocation failed
  • error if local token removal fails

Local token is always removed regardless of return value (unless removal itself fails).

func (*AuthClient) SaveToken

func (c *AuthClient) SaveToken(token *StoredToken) error

SaveToken saves the authentication token for this client's endpoint

func (*AuthClient) SaveTokenForEndpoint

func (c *AuthClient) SaveTokenForEndpoint(ep string, token *StoredToken) error

SaveTokenForEndpoint saves the authentication token for a specific API endpoint

func (*AuthClient) WithEndpoint

func (c *AuthClient) WithEndpoint(ep string) *AuthClient

WithEndpoint returns a new AuthClient using the specified API endpoint. Trailing slashes are stripped to prevent double slashes in URL paths.

type AuthStore

type AuthStore struct {
	// Tokens maps API endpoint URLs to their authentication tokens
	// e.g., "https://api.sageox.ai/" -> token, "https://sageox.walmart.com/" -> token
	Tokens map[string]*StoredToken `json:"tokens"`
}

AuthStore holds tokens for multiple API endpoints

type AuthenticationError

type AuthenticationError struct {
	Message string
}

AuthenticationError is raised when authentication is required but user is not logged in

func (*AuthenticationError) Error

func (e *AuthenticationError) Error() string

type DeviceAuthError

type DeviceAuthError struct {
	Message string
}

DeviceAuthError represents a device authorization failure

func (*DeviceAuthError) Error

func (e *DeviceAuthError) Error() string

type DeviceAuthPendingError

type DeviceAuthPendingError struct{}

DeviceAuthPendingError indicates the user hasn't authorized yet (not an error, used for control flow)

func (*DeviceAuthPendingError) Error

func (e *DeviceAuthPendingError) Error() string

type DeviceAuthSlowDownError

type DeviceAuthSlowDownError struct{}

DeviceAuthSlowDownError indicates the server requested slower polling

func (*DeviceAuthSlowDownError) Error

func (e *DeviceAuthSlowDownError) Error() string

type DeviceCodeRequest

type DeviceCodeRequest struct {
	ClientID string `json:"client_id"`
	Scope    string `json:"scope"`
}

DeviceCodeRequest represents the request body for device code endpoint

type DeviceCodeResponse

type DeviceCodeResponse struct {
	DeviceCode              string `json:"device_code"`
	UserCode                string `json:"user_code"`
	VerificationURI         string `json:"verification_uri"`
	VerificationURIComplete string `json:"verification_uri_complete"`
	ExpiresIn               int    `json:"expires_in"`
	Interval                int    `json:"interval"`
}

DeviceCodeResponse represents the response from the device code endpoint

func RequestDeviceCode

func RequestDeviceCode() (*DeviceCodeResponse, error)

RequestDeviceCode initiates the device authorization flow

type ErrorResponse

type ErrorResponse struct {
	Error            string `json:"error"`
	ErrorDescription string `json:"error_description"`
}

ErrorResponse represents an error response from the API

type JWTExchangeResponse

type JWTExchangeResponse struct {
	AccessToken string `json:"access_token"`
	TokenType   string `json:"token_type"`
	ExpiresIn   int    `json:"expires_in"`
}

JWTExchangeResponse represents the response from JWT exchange endpoint

type LoginResult

type LoginResult struct {
	Success  bool
	UserInfo *UserInfo
	Error    string
}

LoginResult contains the result of a login attempt

func LoginWithResult

func LoginWithResult(timeout time.Duration, onStatus func(string)) *LoginResult

Login performs the full OAuth Device Authorization Flow with LoginResult return type This is a wrapper around the existing Login function that returns LoginResult instead of error for simpler handling in CLI code

type StoredToken

type StoredToken struct {
	AccessToken  string    `json:"access_token"`
	RefreshToken string    `json:"refresh_token"`
	ExpiresAt    time.Time `json:"expires_at"`
	TokenType    string    `json:"token_type"`
	Scope        string    `json:"scope"`
	UserInfo     UserInfo  `json:"user_info"`
}

StoredToken represents the authentication token stored locally

func EnsureValidToken

func EnsureValidToken(bufferSeconds int) (*StoredToken, error)

EnsureValidToken ensures we have a valid token, refreshing proactively if needed.

Proactive refresh: If token expires within bufferSeconds (default 300 = 5 min), refresh it now to avoid expiration during a request.

Returns:

  • Valid StoredToken, or nil if not authenticated

Raises:

  • TokenRefreshError if refresh fails

func GetToken

func GetToken() (*StoredToken, error)

GetToken loads the authentication token for the current API endpoint

func GetTokenForEndpoint

func GetTokenForEndpoint(ep string) (*StoredToken, error)

GetTokenForEndpoint loads the authentication token for a specific API endpoint

func Handle401Error

func Handle401Error(token *StoredToken) (*StoredToken, error)

Handle401Error handles 401 Unauthorized by attempting token refresh.

Reactive refresh: Called when a request returns 401, attempt refresh and return new token for retry.

Args:

  • token: The current (possibly expired) token

Returns:

  • New valid StoredToken after refresh

Raises:

  • TokenRefreshError if refresh fails (user must re-authenticate)

func (*StoredToken) IsExpired

func (t *StoredToken) IsExpired(bufferSeconds int) bool

IsExpired checks if the token is expired or will expire within the buffer seconds

type TokenRefreshError

type TokenRefreshError struct {
	Message string
	Err     error
}

TokenRefreshError represents an error that occurred during token refresh

func (*TokenRefreshError) Error

func (e *TokenRefreshError) Error() string

func (*TokenRefreshError) Unwrap

func (e *TokenRefreshError) Unwrap() error

type TokenRequest

type TokenRequest struct {
	GrantType  string `json:"grant_type"`
	DeviceCode string `json:"device_code"`
	ClientID   string `json:"client_id"`
}

TokenRequest represents the request body for device token endpoint

type TokenResponse

type TokenResponse struct {
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
	TokenType    string `json:"token_type"`
	ExpiresIn    int    `json:"expires_in"`
	Scope        string `json:"scope"`
}

TokenResponse represents the response from the token endpoint

type UserInfo

type UserInfo struct {
	UserID string `json:"user_id"`
	Email  string `json:"email"`
	Name   string `json:"name"`
}

UserInfo contains user information from the authentication provider

Jump to

Keyboard shortcuts

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