token

package
v0.1.32 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// DefaultRefreshLead is the default lead for every registry and inline
	// provider, including the workspace provider.
	DefaultRefreshLead = 15 * time.Minute
	// MinUsefulRefreshLead is the minimum effective lead; below it a refresh
	// would race the token's own expiry.
	MinUsefulRefreshLead = 30 * time.Second
)

Refresh policy shared by request-time resolution and the background refresh watcher. Both must call the same ShouldRefresh decision; they must not maintain different thresholds.

Variables

View Source
var ErrNoBrokerForProvider = errors.New("token: no refresh broker registered for provider")

ErrNoBrokerForProvider marks a refresh request routed to a provider without a registered broker. It is a routing/configuration condition — never invalid_grant — so callers must preserve the stored credential (cooldown, skip) and must never delete or modify the token row because of it.

Functions

func EffectiveRefreshLead added in v0.1.29

func EffectiveRefreshLead(configured, lifetime time.Duration) time.Duration

EffectiveRefreshLead computes the refresh lead applied to one token:

effectiveRefreshLead = min(configuredRefreshLead, originalTokenLifetime * 20%)
minimum useful lead  = 30 seconds

configured <= 0 selects DefaultRefreshLead. lifetime <= 0 means the original token lifetime is unavailable: the configured lead is used unchanged and the caller must enforce a per-token refresh cooldown instead.

func IsNoBrokerForProvider added in v0.1.29

func IsNoBrokerForProvider(err error) bool

IsNoBrokerForProvider reports whether err marks missing broker routing.

func IsRefreshInvalidGrant added in v0.1.22

func IsRefreshInvalidGrant(err error) bool

IsRefreshInvalidGrant reports whether err carries invalid_grant classification from the OAuth refresh broker.

func MarshalSecurityContext

func MarshalSecurityContext(ctx context.Context) (string, error)

MarshalSecurityContext serializes the auth tokens from context into a JSON string suitable for storing in run.SecurityContext.

func NewRefreshInvalidGrantError added in v0.1.22

func NewRefreshInvalidGrantError(cause error) error

NewRefreshInvalidGrantError wraps a parsed invalid_grant response.

func RefreshJitter added in v0.1.29

func RefreshJitter(key Key, lead time.Duration) time.Duration

RefreshJitter derives a small deterministic per-key offset (up to 10% of lead) so multiple pods and users do not refresh simultaneously. Determinism keeps request-time and background decisions for the same key identical.

func ShouldRefresh added in v0.1.29

func ShouldRefresh(now, expiresAt time.Time, configured, lifetime time.Duration) bool

ShouldRefresh reports whether a token expiring at expiresAt should refresh now under the provider's configured lead and the token's original lifetime. Tokens without a known expiry never trigger refresh.

Types

type Broker

type Broker interface {
	// Refresh uses a refresh token to obtain new access/ID tokens.
	Refresh(ctx context.Context, key Key, refreshToken string) (*scyauth.Token, error)
	// Exchange converts an authorization code to tokens (for OOB/scheduled flows).
	Exchange(ctx context.Context, key Key, code string) (*scyauth.Token, error)
}

Broker handles token refresh and exchange operations. When nil on Manager, the manager operates in cache-only mode.

type BrokerRegistry added in v0.1.29

type BrokerRegistry interface {
	Broker(ctx context.Context, provider string) (Broker, bool)
}

BrokerRegistry resolves refresh brokers by token key provider (workspace provider name or delegated storage key).

type InstanceID

type InstanceID string

InstanceID uniquely identifies a running process instance (hostname:pid:uuid). The UUID suffix handles container recycling where hostname+PID may be reused.

func NewInstanceID

func NewInstanceID() InstanceID

NewInstanceID creates a new InstanceID for the current process.

type Key

type Key struct {
	Subject  string // user identifier (from EffectiveUserID)
	Provider string // oauth provider name (e.g. "google", "default")
}

Key identifies a token set for a user+provider pair.

type Manager

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

Manager is the default in-process Provider implementation. It layers an in-memory cache over an optional persistent TokenStore and uses an optional Broker for refresh/exchange.

func NewManager

func NewManager(opts ...ManagerOption) *Manager

NewManager creates a new token Manager. When a TokenStore is provided and no explicit InstanceID is set, distributed refresh coordination is automatically enabled with an auto-generated InstanceID. To explicitly disable distributed mode, use WithInstanceID("").

func (*Manager) EnsureTokens

func (m *Manager) EnsureTokens(ctx context.Context, key Key) (context.Context, error)

EnsureTokens checks if tokens in context are fresh; if not, refreshes from cache or via Broker, and returns updated context.

func (*Manager) Invalidate

func (m *Manager) Invalidate(ctx context.Context, key Key) error

Invalidate removes cached tokens for a key.

func (*Manager) Store

func (m *Manager) Store(ctx context.Context, key Key, tok *scyauth.Token) error

Store persists tokens for later retrieval.

type ManagerOption

type ManagerOption func(*Manager)

ManagerOption configures a Manager.

func WithBroker

func WithBroker(b Broker) ManagerOption

WithBroker sets the token broker for refresh/exchange.

func WithInstanceID

func WithInstanceID(id InstanceID) ManagerOption

WithInstanceID sets the instance identity for distributed refresh coordination. Pass a non-empty InstanceID to enable, or "" to explicitly disable auto-detection.

func WithLeaseTTL

func WithLeaseTTL(d time.Duration) ManagerOption

WithLeaseTTL sets the distributed refresh lease duration (default 30s).

func WithMinTTL

func WithMinTTL(d time.Duration) ManagerOption

WithMinTTL sets the minimum remaining TTL before a refresh is triggered.

func WithMissTTL added in v0.1.8

func WithMissTTL(d time.Duration) ManagerOption

WithMissTTL sets how long a missing-token result is negatively cached. During this window EnsureTokens skips store lookups and does not re-log the miss.

func WithTokenStore

func WithTokenStore(s TokenStore) ManagerOption

WithTokenStore sets the persistent token store.

type OAuthToken

type OAuthToken struct {
	Username     string
	Provider     string
	AccessToken  string
	IDToken      string
	RefreshToken string
	ExpiresAt    time.Time

	Issuer      string
	Resource    string
	Scopes      []string
	TokenType   string
	Subject     string
	ProviderRef string
	ClientRef   string
	// IDTokenExpiresAt mirrors the verified ID-token exp; ExpiresAt remains
	// the access-token expiry for compatibility.
	IDTokenExpiresAt time.Time
	// IssuedAt records when the token set was obtained; the refresh policy
	// derives the original selected-token lifetime from it.
	IssuedAt time.Time
}

OAuthToken represents a stored OAuth token set for a user/provider pair. This mirrors service/auth.OAuthToken to avoid import cycles. The metadata fields are optional for legacy workspace rows; conversions and refresh/CAS paths must preserve them when present.

func (*OAuthToken) MergeMetadataFrom added in v0.1.29

func (t *OAuthToken) MergeMetadataFrom(prior *OAuthToken)

MergeMetadataFrom copies missing metadata fields from prior. Populated fields on the receiver win (e.g. an authoritative refreshed scope set).

type Provider

type Provider interface {
	// EnsureTokens checks if tokens in context are fresh; if not, refreshes
	// from cache or via Broker, and returns updated context.
	EnsureTokens(ctx context.Context, key Key) (context.Context, error)

	// Store persists tokens for later retrieval (called by auth middleware on login/callback).
	Store(ctx context.Context, key Key, tok *scyauth.Token) error

	// Invalidate removes cached tokens for a key (called on logout).
	Invalidate(ctx context.Context, key Key) error
}

Provider supplies fresh tokens for a user+provider pair.

type RefreshInvalidGrantError added in v0.1.22

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

RefreshInvalidGrantError classifies a parsed invalid_grant response from the OAuth token endpoint without coupling the token manager to service/auth.

func (*RefreshInvalidGrantError) Error added in v0.1.22

func (e *RefreshInvalidGrantError) Error() string

func (*RefreshInvalidGrantError) Unwrap added in v0.1.22

func (e *RefreshInvalidGrantError) Unwrap() error

type RoutingBroker added in v0.1.29

type RoutingBroker struct {
	Workspace        Broker
	Registry         BrokerRegistry
	IsWorkspaceAlias WorkspaceAliasMatcher
}

RoutingBroker implements Broker by routing Refresh/Exchange to the broker registered for token.Key.Provider. There is exactly one token.Manager: the router lets it serve every provider while keeping leases, singleflight, miss caches and retry caches shared.

Routing rules:

  • Workspace aliases go to the workspace broker.
  • Other providers are resolved through the registry.
  • Unknown providers fail with ErrNoBrokerForProvider; they are never sent to the workspace broker and never treated as invalid_grant.

func (*RoutingBroker) Exchange added in v0.1.29

func (r *RoutingBroker) Exchange(ctx context.Context, key Key, code string) (*scyauth.Token, error)

Exchange routes the code exchange to the provider's broker.

func (*RoutingBroker) Refresh added in v0.1.29

func (r *RoutingBroker) Refresh(ctx context.Context, key Key, refreshToken string) (*scyauth.Token, error)

Refresh routes the refresh to the provider's broker.

type SecurityData

type SecurityData struct {
	AccessToken  string    `json:"accessToken,omitempty"`
	IDToken      string    `json:"idToken,omitempty"`
	RefreshToken string    `json:"refreshToken,omitempty"`
	ExpiresAt    time.Time `json:"expiresAt,omitempty"`
	Subject      string    `json:"subject,omitempty"`
	Provider     string    `json:"provider,omitempty"`
}

SecurityData is the JSON-serializable auth state saved to run.SecurityContext.

func RestoreSecurityContext

func RestoreSecurityContext(ctx context.Context, data string) (context.Context, *SecurityData, error)

RestoreSecurityContext deserializes auth state from a run.SecurityContext string and injects tokens into the context.

type TokenStore

type TokenStore interface {
	Get(ctx context.Context, username, provider string) (*OAuthToken, error)
	Put(ctx context.Context, token *OAuthToken) error
	Delete(ctx context.Context, username, provider string) error

	// TryAcquireRefreshLease atomically attempts to acquire a distributed lease
	// for refreshing the token identified by (username, provider). Returns the
	// current version and whether the lease was acquired.
	TryAcquireRefreshLease(ctx context.Context, username, provider, owner string, ttl time.Duration) (version int64, acquired bool, err error)

	// ReleaseRefreshLease releases a previously acquired lease (e.g. on failure).
	ReleaseRefreshLease(ctx context.Context, username, provider, owner string) error

	// CASPut atomically updates the token only if the current version matches
	// expectedVersion and the lease is held by owner. Returns whether the swap succeeded.
	CASPut(ctx context.Context, token *OAuthToken, expectedVersion int64, owner string) (swapped bool, err error)
}

TokenStore abstracts encrypted OAuth token persistence. This mirrors service/auth.TokenStore to avoid import cycles. Implementations from service/auth satisfy this interface.

type WorkspaceAliasMatcher added in v0.1.29

type WorkspaceAliasMatcher func(provider string) bool

WorkspaceAliasMatcher reports whether a provider value is the workspace identity provider or one of its trusted legacy aliases.

Jump to

Keyboard shortcuts

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