Documentation
¶
Index ¶
- Constants
- Variables
- func EffectiveRefreshLead(configured, lifetime time.Duration) time.Duration
- func IsNoBrokerForProvider(err error) bool
- func IsRefreshInvalidGrant(err error) bool
- func MarshalSecurityContext(ctx context.Context) (string, error)
- func NewRefreshInvalidGrantError(cause error) error
- func RefreshJitter(key Key, lead time.Duration) time.Duration
- func ShouldRefresh(now, expiresAt time.Time, configured, lifetime time.Duration) bool
- type Broker
- type BrokerRegistry
- type InstanceID
- type Key
- type Manager
- type ManagerOption
- type OAuthToken
- type Provider
- type RefreshInvalidGrantError
- type RoutingBroker
- type SecurityData
- type TokenStore
- type WorkspaceAliasMatcher
Constants ¶
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 ¶
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
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
IsNoBrokerForProvider reports whether err marks missing broker routing.
func IsRefreshInvalidGrant ¶ added in v0.1.22
IsRefreshInvalidGrant reports whether err carries invalid_grant classification from the OAuth refresh broker.
func MarshalSecurityContext ¶
MarshalSecurityContext serializes the auth tokens from context into a JSON string suitable for storing in run.SecurityContext.
func NewRefreshInvalidGrantError ¶ added in v0.1.22
NewRefreshInvalidGrantError wraps a parsed invalid_grant response.
func RefreshJitter ¶ added in v0.1.29
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
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
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 ¶
EnsureTokens checks if tokens in context are fresh; if not, refreshes from cache or via Broker, and returns updated context.
func (*Manager) Invalidate ¶
Invalidate removes cached tokens for a key.
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.
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
WorkspaceAliasMatcher reports whether a provider value is the workspace identity provider or one of its trusted legacy aliases.