Documentation
¶
Overview ¶
Package auth provides authentication handler interfaces and utilities for scafctl. Auth handlers manage identity verification, credential storage, and token acquisition. They are separate from providers - providers are stateless execution primitives, while auth handlers manage state (cached tokens, refresh tokens) across invocations.
Index ¶
- Constants
- Variables
- func FingerprintHash(identity string) string
- func HasCapability(capabilities []Capability, capability Capability) bool
- func HasHandler(ctx context.Context, name string) bool
- func IsAlreadyAuthenticated(err error) bool
- func IsAuthenticationFailed(err error) bool
- func IsCapabilityNotSupported(err error) bool
- func IsConsentRequired(err error) bool
- func IsFlowNotSupported(err error) bool
- func IsGrantInvalid(err error) bool
- func IsHandlerNotFound(err error) bool
- func IsInvalidScope(err error) bool
- func IsNotAuthenticated(err error) bool
- func IsTimeout(err error) bool
- func IsTokenExpired(err error) bool
- func IsUserCancelled(err error) bool
- func ListHandlers(ctx context.Context) []string
- func WithRegistry(ctx context.Context, registry *Registry) context.Context
- type CacheEntry
- type CachedToken
- type CachedTokenInfo
- type Capability
- type Claims
- type CredentialDetector
- type Error
- type Flow
- type FlowDetectionResult
- type GroupsProvider
- type Handler
- type IdentityType
- type LoginOptions
- type MockHandler
- func (m *MockHandler) Capabilities() []Capability
- func (m *MockHandler) DisplayName() string
- func (m *MockHandler) GetToken(_ context.Context, opts TokenOptions) (*Token, error)
- func (m *MockHandler) InjectAuth(_ context.Context, req *http.Request, opts TokenOptions) error
- func (m *MockHandler) ListCachedTokens(_ context.Context) ([]*CachedTokenInfo, error)
- func (m *MockHandler) Login(_ context.Context, opts LoginOptions) (*Result, error)
- func (m *MockHandler) Logout(_ context.Context) error
- func (m *MockHandler) Name() string
- func (m *MockHandler) PurgeExpiredTokens(_ context.Context) (int, error)
- func (m *MockHandler) Reset()
- func (m *MockHandler) SetAuthenticated(claims *Claims)
- func (m *MockHandler) SetNotAuthenticated()
- func (m *MockHandler) SetToken(token *Token)
- func (m *MockHandler) SetTokenError(err error)
- func (m *MockHandler) Status(_ context.Context) (*Status, error)
- func (m *MockHandler) SupportedFlows() []Flow
- type PreLoginAction
- type PreLoginResult
- type Registry
- func (r *Registry) All() map[string]Handler
- func (r *Registry) Count() int
- func (r *Registry) Get(name string) (Handler, error)
- func (r *Registry) Has(name string) bool
- func (r *Registry) List() []string
- func (r *Registry) Register(handler Handler) error
- func (r *Registry) Unregister(name string) error
- type Result
- type Status
- type Token
- type TokenAcquireFunc
- type TokenCache
- func (c *TokenCache) CacheKey(flow Flow, fingerprint, scope string) string
- func (c *TokenCache) Clear(ctx context.Context) error
- func (c *TokenCache) Delete(ctx context.Context, flow Flow, fingerprint, scope string) error
- func (c *TokenCache) Get(ctx context.Context, flow Flow, fingerprint, scope string) (*Token, error)
- func (c *TokenCache) ListCachedEntries(ctx context.Context) ([]CacheEntry, error)
- func (c *TokenCache) ParseKey(key string) (Flow, string, string, bool)
- func (c *TokenCache) Prefix() string
- func (c *TokenCache) PurgeExpired(ctx context.Context) (int, error)
- func (c *TokenCache) Set(ctx context.Context, flow Flow, fingerprint, scope string, token *Token) error
- type TokenCacheAccessor
- type TokenLister
- type TokenOptions
- type TokenPurger
Constants ¶
const DefaultMinValidFor = 60 * time.Second
DefaultMinValidFor is the default minimum validity duration for tokens.
Variables ¶
var ( ErrNotAuthenticated = errors.New("not authenticated") ErrAuthenticationFailed = errors.New("authentication failed") ErrTokenExpired = errors.New("credentials expired") ErrConsentRequired = errors.New("consent required: please login with the required scope") ErrInvalidScope = errors.New("invalid scope: scope cannot be empty") ErrHandlerNotFound = errors.New("auth handler not found") ErrFlowNotSupported = errors.New("authentication flow not supported") ErrUserCancelled = errors.New("authentication cancelled by user") ErrTimeout = errors.New("authentication timed out") ErrAlreadyAuthenticated = errors.New("already authenticated") ErrGrantInvalid = errors.New("invalid grant: the refresh token is invalid or has been revoked") ErrCapabilityNotSupported = errors.New("capability not supported by this auth handler") )
Sentinel errors for the auth package.
var ( // ErrNilHandler indicates a nil handler was passed to Register. ErrNilHandler = errors.New("cannot register nil handler") // ErrEmptyHandlerName indicates a handler with an empty name was passed to Register. ErrEmptyHandlerName = errors.New("handler name cannot be empty") // ErrHandlerAlreadyRegistered indicates a handler with the same name is already registered. ErrHandlerAlreadyRegistered = errors.New("auth handler already registered") )
Sentinel errors for the registry.
var ErrOpaqueToken = fmt.Errorf("token is not a decodable JWT")
ErrOpaqueToken is returned when a token is not a decodable JWT (e.g., encrypted or opaque).
Functions ¶
func FingerprintHash ¶ added in v0.6.0
FingerprintHash computes a short, deterministic hash from an identity string for use as a cache-key segment. It prevents cross-config cache collisions by partitioning cache entries per unique configuration identity (e.g., clientID+tenantID).
Returns "_" for an empty identity, indicating that no config-specific partitioning is required (e.g., the metadata-server flow where the identity is implicit).
func HasCapability ¶ added in v0.5.0
func HasCapability(capabilities []Capability, capability Capability) bool
HasCapability checks if a set of capabilities includes the specified capability.
func HasHandler ¶
HasHandler checks if an auth handler exists in the context's registry.
func IsAlreadyAuthenticated ¶ added in v0.6.0
IsAlreadyAuthenticated returns true if the error indicates the user is already authenticated.
func IsAuthenticationFailed ¶ added in v0.6.0
IsAuthenticationFailed returns true if the error indicates authentication failed.
func IsCapabilityNotSupported ¶ added in v0.5.0
IsCapabilityNotSupported returns true if the error indicates a capability is not supported.
func IsConsentRequired ¶ added in v0.4.0
IsConsentRequired returns true if the error indicates consent is required for the requested scope.
func IsFlowNotSupported ¶ added in v0.6.0
IsFlowNotSupported returns true if the error indicates the flow is not supported.
func IsGrantInvalid ¶ added in v0.6.0
IsGrantInvalid returns true if the error indicates the grant (refresh token) is invalid or revoked.
func IsHandlerNotFound ¶
IsHandlerNotFound returns true if the error indicates the handler was not found.
func IsInvalidScope ¶ added in v0.6.0
IsInvalidScope returns true if the error indicates the scope is invalid.
func IsNotAuthenticated ¶
IsNotAuthenticated returns true if the error indicates the user is not authenticated.
func IsTokenExpired ¶
IsTokenExpired returns true if the error indicates the token has expired.
func IsUserCancelled ¶
IsUserCancelled returns true if the error indicates the user cancelled.
func ListHandlers ¶
ListHandlers lists all auth handlers in the context's registry.
Types ¶
type CacheEntry ¶ added in v0.6.0
type CacheEntry struct {
Flow Flow `json:"flow" yaml:"flow" doc:"Authentication flow for this cache entry"`
Fingerprint string `json:"fingerprint" yaml:"fingerprint" doc:"Identity fingerprint for cache partitioning" maxLength:"128"`
Scope string `json:"scope" yaml:"scope" doc:"OAuth scope for this cache entry" maxLength:"1024"`
}
CacheEntry represents a unique cache slot identified by flow, fingerprint, and scope.
type CachedToken ¶ added in v0.6.0
type CachedToken struct {
AccessToken string `json:"accessToken" yaml:"accessToken" doc:"The cached access token" maxLength:"65536"` //nolint:gosec // G117: not a hardcoded credential, stores runtime token data
TokenType string `json:"tokenType" yaml:"tokenType" doc:"Token type, typically Bearer" example:"Bearer" maxLength:"64"`
ExpiresAt time.Time `json:"expiresAt" yaml:"expiresAt" doc:"Time the token expires"`
Scope string `json:"scope" yaml:"scope" doc:"OAuth scope the token was issued for" maxLength:"1024"`
Flow Flow `` /* 131-byte string literal not displayed */
Fingerprint string `json:"fingerprint,omitempty" yaml:"fingerprint,omitempty" doc:"Truncated SHA-256 hash of the config identity" maxLength:"128"`
CachedAt time.Time `json:"cachedAt" yaml:"cachedAt" doc:"Time the token was written to cache"`
SessionID string `json:"sessionId,omitempty" yaml:"sessionId,omitempty" doc:"Stable identifier of the authentication session" maxLength:"128"`
}
CachedToken is the structure stored on disk for each cached token.
type CachedTokenInfo ¶ added in v0.5.0
type CachedTokenInfo struct {
// Handler is the name of the auth handler that owns this token.
Handler string `json:"handler" yaml:"handler" doc:"Name of the auth handler that owns this token" example:"entra" maxLength:"64"`
// TokenKind is either "refresh" or "access".
TokenKind string `json:"tokenKind" yaml:"tokenKind" doc:"Token kind: refresh or access" example:"access" maxLength:"16"`
// Scope is the OAuth scope associated with the token.
// Empty for refresh tokens that were not scope-specific.
Scope string `json:"scope,omitempty" yaml:"scope,omitempty" doc:"OAuth scope associated with the token" maxLength:"1024"`
// TokenType is the token type, e.g. "Bearer".
TokenType string `json:"tokenType,omitempty" yaml:"tokenType,omitempty" doc:"Token type, typically Bearer" example:"Bearer" maxLength:"64"`
// Flow is the authentication flow that produced the token.
Flow Flow `` /* 130-byte string literal not displayed */
// Fingerprint is the truncated SHA-256 hash of the config identity
// (e.g., clientID+tenantID) that produced this token. The value "_"
// means no config-specific partitioning applies.
Fingerprint string `json:"fingerprint,omitempty" yaml:"fingerprint,omitempty" doc:"Truncated SHA-256 hash of the config identity" maxLength:"128"`
// ExpiresAt is when the token expires.
ExpiresAt time.Time `json:"expiresAt,omitempty" yaml:"expiresAt,omitempty" doc:"Time the token expires"`
// CachedAt is when the token was written to the on-disk cache.
CachedAt time.Time `json:"cachedAt,omitempty" yaml:"cachedAt,omitempty" doc:"Time the token was written to the on-disk cache"`
// IsExpired indicates whether the token is past its expiry time.
IsExpired bool `json:"isExpired" yaml:"isExpired" doc:"Whether the token is past its expiry time"`
// SessionID is the stable identifier of the authentication session that
// produced this token. Present on both refresh and access entries, allowing
// callers to trace which login session minted a given access token.
SessionID string `json:"sessionId,omitempty" yaml:"sessionId,omitempty" doc:"Stable identifier of the authentication session" maxLength:"128"`
}
CachedTokenInfo holds display metadata for a cached token (refresh or access). The actual token value is intentionally omitted — use 'auth token' to retrieve it.
func (*CachedTokenInfo) TimeUntilExpiry ¶ added in v0.5.0
func (c *CachedTokenInfo) TimeUntilExpiry() time.Duration
TimeUntilExpiry returns the duration until this cached token expires. Returns 0 if the token is already expired or has no expiry set.
type Capability ¶ added in v0.5.0
type Capability string
Capability represents a feature or behavior that an auth handler supports. Capabilities allow CLI commands to dynamically adapt their flags and validation based on what each handler supports, enabling plugin-loaded handlers to work without hardcoded knowledge of their features.
const ( // CapScopesOnLogin indicates the handler supports specifying OAuth scopes at login time. // Both GitHub (device code) and Entra (device code/SP/WI) support this. CapScopesOnLogin Capability = "scopes_on_login" // CapScopesOnTokenRequest indicates the handler supports specifying per-request scopes // when acquiring tokens. Entra supports this (different resource scopes per request), // but GitHub does not (scopes are fixed at login time). CapScopesOnTokenRequest Capability = "scopes_on_token_request" // CapTenantID indicates the handler supports a tenant ID parameter. // Entra uses this for Azure AD tenant selection. CapTenantID Capability = "tenant_id" // CapHostname indicates the handler supports a hostname parameter. // GitHub uses this for GitHub Enterprise Server (GHES) support. CapHostname Capability = "hostname" // CapFederatedToken indicates the handler supports federated token input. // Entra uses this for workload identity (Kubernetes) authentication. CapFederatedToken Capability = "federated_token" // CapCallbackPort indicates the handler supports binding the OAuth callback // server to a specific port via --callback-port. Handlers that use the // authorization code + PKCE flow (Entra, GCP) advertise this capability. CapCallbackPort Capability = "callback_port" )
type Claims ¶
type Claims struct {
Issuer string `` /* 130-byte string literal not displayed */
Subject string `json:"subject,omitempty" yaml:"subject,omitempty" doc:"Subject identifier" example:"user@example.com" maxLength:"512"`
TenantID string `` /* 139-byte string literal not displayed */
ObjectID string `` /* 147-byte string literal not displayed */
ClientID string `` /* 142-byte string literal not displayed */
Email string `json:"email,omitempty" yaml:"email,omitempty" doc:"Email address of the identity" example:"user@example.com" maxLength:"320"`
Name string `json:"name,omitempty" yaml:"name,omitempty" doc:"Display name of the identity" example:"Jane Doe" maxLength:"256"`
Username string `json:"username,omitempty" yaml:"username,omitempty" doc:"Username or login name" example:"janedoe" maxLength:"256"`
IssuedAt time.Time `json:"issuedAt,omitempty" yaml:"issuedAt,omitempty" doc:"Time the token was issued"`
ExpiresAt time.Time `json:"expiresAt,omitempty" yaml:"expiresAt,omitempty" doc:"Time the token expires"`
}
Claims represents normalized identity claims from any auth handler.
func ParseJWTClaims ¶ added in v0.6.0
ParseJWTClaims decodes a JWT token string (without signature verification) and extracts normalized identity claims. This works for both ID tokens and access tokens that are standard three-part JWTs.
Returns ErrOpaqueToken if the token is not a decodable JWT (e.g., encrypted or opaque tokens issued by some providers for first-party resources).
func (*Claims) DisplayIdentity ¶
DisplayIdentity returns the best available identity string for display.
type CredentialDetector ¶ added in v0.6.0
type CredentialDetector struct {
// HasCredentials returns true if this credential type is available.
HasCredentials func() bool
// Flow is the auth flow to use when these credentials are detected.
Flow Flow
// Description is a human-readable message shown when this credential is detected.
Description string
}
CredentialDetector checks for available credentials in the environment.
type Error ¶
type Error struct {
Handler string `json:"handler" yaml:"handler" doc:"Name of the auth handler" example:"entra" maxLength:"64"`
Operation string `json:"operation" yaml:"operation" doc:"Operation that failed" example:"login" maxLength:"64"`
Cause error `json:"-" yaml:"-"`
}
Error wraps authentication errors with additional context.
type Flow ¶
type Flow string
Flow represents an authentication flow type.
const ( // FlowDeviceCode is the OAuth 2.0 device authorization flow. FlowDeviceCode Flow = "device_code" // FlowInteractive is an interactive browser-based flow. FlowInteractive Flow = "interactive" // FlowServicePrincipal is authentication using service principal credentials. FlowServicePrincipal Flow = "service_principal" // FlowWorkloadIdentity is authentication using Azure Workload Identity (Kubernetes). FlowWorkloadIdentity Flow = "workload_identity" // FlowPAT is authentication using a personal access token from environment variables. FlowPAT Flow = "pat" // FlowMetadata is authentication using cloud metadata server (e.g., GCE, Cloud Run). FlowMetadata Flow = "metadata" // FlowGcloudADC is authentication using gcloud's Application Default Credentials file. FlowGcloudADC Flow = "gcloud_adc" // FlowGitHubApp is authentication using a GitHub App installation token. // A JWT is minted from the App's private key and exchanged for a short-lived // installation access token. FlowGitHubApp Flow = "github_app" )
type FlowDetectionResult ¶ added in v0.6.0
type FlowDetectionResult struct {
// Flow is the selected authentication flow.
Flow Flow
// Description is a human-readable message about what was detected,
// or empty if the default flow was used.
Description string
}
FlowDetectionResult contains the auto-detected flow and a description.
func DetectFlow ¶ added in v0.6.0
func DetectFlow(explicitFlow Flow, detectors []CredentialDetector, defaultFlow Flow) FlowDetectionResult
DetectFlow determines the best authentication flow given an explicit preference, a prioritized list of credential detectors, and a default fallback flow.
Resolution order:
- If explicitFlow is non-empty, use it.
- Check detectors in order; use the first one with available credentials.
- Fall back to defaultFlow.
type GroupsProvider ¶ added in v0.5.0
type GroupsProvider interface {
// GetGroups returns the ObjectIDs of all groups the authenticated user belongs to.
// Implementations must handle pagination transparently, so all memberships —
// including those beyond any per-token cap (e.g. the 200-group JWT limit for
// Microsoft Entra) — are returned.
GetGroups(ctx context.Context) ([]string, error)
}
GroupsProvider is an optional interface that auth handlers can implement to return the authenticated user's group memberships as a slice of ObjectIDs.
Handlers that do not implement this interface do not support group membership queries. Callers should type-assert to this interface before invoking GetGroups.
type Handler ¶
type Handler interface {
// Name returns the unique identifier for this auth handler.
Name() string
// DisplayName returns a human-readable name for display purposes.
DisplayName() string
// Login initiates the authentication flow.
// For interactive flows (like device code), this blocks until completion or timeout.
// Returns the authenticated identity claims on success.
Login(ctx context.Context, opts LoginOptions) (*Result, error)
// Logout clears stored credentials for this handler.
Logout(ctx context.Context) error
// Status returns the current authentication status.
// Returns a status with Authenticated=false if not logged in.
Status(ctx context.Context) (*Status, error)
// GetToken returns a valid access token for the specified options.
// Uses cached tokens when available and valid for the requested duration,
// otherwise refreshes from the identity provider.
// Returns ErrNotAuthenticated if user is not logged in.
// Returns ErrTokenExpired if refresh token has expired (re-login required).
GetToken(ctx context.Context, opts TokenOptions) (*Token, error)
// InjectAuth adds authentication to an HTTP request.
// This is the primary method used by providers (like HTTP) to authenticate requests.
// Automatically handles token acquisition/refresh as needed.
InjectAuth(ctx context.Context, req *http.Request, opts TokenOptions) error
// SupportedFlows returns the authentication flows this handler supports.
SupportedFlows() []Flow
// Capabilities returns the set of capabilities this handler supports.
// Commands use capabilities to dynamically determine which flags and
// validation rules apply for a given handler.
Capabilities() []Capability
}
Handler defines the interface for authentication handlers. Auth handlers manage identity verification, credential storage, and token acquisition.
type IdentityType ¶
type IdentityType string
IdentityType represents the type of authenticated identity.
const ( // IdentityTypeUser represents a user identity (e.g., device code flow). IdentityTypeUser IdentityType = "user" // IdentityTypeServicePrincipal represents a service principal identity. IdentityTypeServicePrincipal IdentityType = "service-principal" // IdentityTypeWorkloadIdentity represents a workload identity (Kubernetes federated). IdentityTypeWorkloadIdentity IdentityType = "workload-identity" )
type LoginOptions ¶
type LoginOptions struct {
TenantID string `json:"tenantId,omitempty" yaml:"tenantId,omitempty" doc:"Azure AD tenant ID override" maxLength:"128"`
Scopes []string `json:"scopes,omitempty" yaml:"scopes,omitempty" doc:"OAuth scopes to request" maxItems:"20"`
Flow Flow `json:"flow,omitempty" yaml:"flow,omitempty" doc:"Authentication flow to use" example:"device_code" maxLength:"64"`
Timeout time.Duration `json:"timeout,omitempty" yaml:"timeout,omitempty" doc:"Maximum time to wait for authentication"`
CallbackPort int `` /* 130-byte string literal not displayed */
DeviceCodeCallback func(userCode, verificationURI, message string) `json:"-" yaml:"-"`
}
LoginOptions configures the login process.
type MockHandler ¶
type MockHandler struct {
NameValue string
DisplayNameValue string
FlowsValue []Flow
CapabilitiesValue []Capability
LoginResult *Result
LoginErr error
LogoutErr error
StatusResult *Status
StatusErr error
GetTokenResult *Token
GetTokenErr error
InjectAuthErr error
ListCachedTokensResult []*CachedTokenInfo
ListCachedTokensErr error
PurgeExpiredResult int
PurgeExpiredErr error
LoginCalls []LoginOptions
LogoutCalls int
StatusCalls int
GetTokenCalls []TokenOptions
InjectAuthCalls []TokenOptions
PurgeExpiredCalls int
// contains filtered or unexported fields
}
MockHandler implements Handler for testing.
func NewMockHandler ¶
func NewMockHandler(name string) *MockHandler
NewMockHandler creates a new mock auth handler with default values.
func (*MockHandler) Capabilities ¶ added in v0.5.0
func (m *MockHandler) Capabilities() []Capability
func (*MockHandler) DisplayName ¶
func (m *MockHandler) DisplayName() string
func (*MockHandler) GetToken ¶
func (m *MockHandler) GetToken(_ context.Context, opts TokenOptions) (*Token, error)
func (*MockHandler) InjectAuth ¶
func (m *MockHandler) InjectAuth(_ context.Context, req *http.Request, opts TokenOptions) error
func (*MockHandler) ListCachedTokens ¶ added in v0.5.0
func (m *MockHandler) ListCachedTokens(_ context.Context) ([]*CachedTokenInfo, error)
func (*MockHandler) Login ¶
func (m *MockHandler) Login(_ context.Context, opts LoginOptions) (*Result, error)
func (*MockHandler) Name ¶
func (m *MockHandler) Name() string
func (*MockHandler) PurgeExpiredTokens ¶ added in v0.5.0
func (m *MockHandler) PurgeExpiredTokens(_ context.Context) (int, error)
func (*MockHandler) Reset ¶
func (m *MockHandler) Reset()
func (*MockHandler) SetAuthenticated ¶
func (m *MockHandler) SetAuthenticated(claims *Claims)
func (*MockHandler) SetNotAuthenticated ¶
func (m *MockHandler) SetNotAuthenticated()
func (*MockHandler) SetToken ¶
func (m *MockHandler) SetToken(token *Token)
func (*MockHandler) SetTokenError ¶
func (m *MockHandler) SetTokenError(err error)
func (*MockHandler) SupportedFlows ¶
func (m *MockHandler) SupportedFlows() []Flow
type PreLoginAction ¶ added in v0.6.0
type PreLoginAction int
PreLoginAction describes what the caller should do after a pre-login check.
const ( // PreLoginProceed means the login should proceed normally. PreLoginProceed PreLoginAction = iota // PreLoginSkip means the user is already authenticated and login should be skipped. PreLoginSkip // PreLoginAlreadyAuthenticated means the user is already authenticated but login was not skipped. PreLoginAlreadyAuthenticated )
type PreLoginResult ¶ added in v0.6.0
type PreLoginResult struct {
// Action indicates what the caller should do.
Action PreLoginAction
// Identity is the display name of the authenticated identity (if any).
Identity string
}
PreLoginResult contains the result of a pre-login check.
func PreLoginCheck ¶ added in v0.6.0
func PreLoginCheck(ctx context.Context, handler Handler, flow Flow, force, skipIfAuthenticated bool, skipCheckFlows ...Flow) (*PreLoginResult, error)
PreLoginCheck checks whether a handler is already authenticated and determines the appropriate action. If force is true, the handler is logged out first and PreLoginProceed is returned. If the flow is in skipCheckFlows, no check is performed.
Parameters:
- handler: the auth handler to check
- flow: the selected auth flow
- force: if true, force logout and proceed
- skipIfAuthenticated: if true and already authenticated, return PreLoginSkip
- skipCheckFlows: flows for which no auth-status check is needed (e.g., FlowPAT)
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry manages registered auth handlers.
func RegistryFromContext ¶
RegistryFromContext retrieves the auth registry from the context.
func (*Registry) Unregister ¶
Unregister removes an auth handler from the registry.
type Result ¶
type Result struct {
Claims *Claims `json:"claims,omitempty" yaml:"claims,omitempty" doc:"Identity claims from authentication"`
ExpiresAt time.Time `json:"expiresAt,omitempty" yaml:"expiresAt,omitempty" doc:"Time the authentication expires"`
}
Result contains the result of a successful authentication.
type Status ¶
type Status struct {
Authenticated bool `json:"authenticated" yaml:"authenticated" doc:"Whether the user is currently authenticated"`
Claims *Claims `json:"claims,omitempty" yaml:"claims,omitempty" doc:"Identity claims from the current session"`
ExpiresAt time.Time `json:"expiresAt,omitempty" yaml:"expiresAt,omitempty" doc:"Time the authentication expires"`
LastRefresh time.Time `json:"lastRefresh,omitempty" yaml:"lastRefresh,omitempty" doc:"Time the token was last refreshed"`
TenantID string `json:"tenantId,omitempty" yaml:"tenantId,omitempty" doc:"Azure AD tenant ID" maxLength:"128"`
IdentityType IdentityType `` /* 126-byte string literal not displayed */
ClientID string `` /* 131-byte string literal not displayed */
TokenFile string `` /* 131-byte string literal not displayed */
Scopes []string `json:"scopes,omitempty" yaml:"scopes,omitempty" doc:"Scopes granted during login" maxItems:"20"`
}
Status represents the current authentication state.
type Token ¶
type Token struct {
AccessToken string `json:"accessToken" yaml:"accessToken" doc:"The access token value"` //nolint:gosec // G117: not a hardcoded credential, stores runtime token data
TokenType string `json:"tokenType" yaml:"tokenType" doc:"Token type, typically Bearer" example:"Bearer" maxLength:"64"`
ExpiresAt time.Time `json:"expiresAt" yaml:"expiresAt" doc:"Time the token expires"`
Scope string `json:"scope,omitempty" yaml:"scope,omitempty" doc:"OAuth scope the token was issued for" maxLength:"1024"`
// CachedAt records when this token was written to the on-disk cache.
// Zero value means the token was not loaded from the cache.
CachedAt time.Time `json:"cachedAt,omitempty" yaml:"cachedAt,omitempty" doc:"Time the token was written to the on-disk cache"`
// Flow is the authentication flow that produced this token (e.g. "device_code",
// "service_principal", "workload_identity"). Empty string means unknown.
Flow Flow `` /* 131-byte string literal not displayed */
// SessionID is a stable identifier linking this access token back to the
// authentication session (login) that produced it. Generated once at login
// time and preserved across refresh-token rotations.
SessionID string `json:"sessionId,omitempty" yaml:"sessionId,omitempty" doc:"Stable identifier of the authentication session" maxLength:"128"`
}
Token represents a short-lived access token.
func GetCachedOrAcquireToken ¶ added in v0.6.0
func GetCachedOrAcquireToken[T any]( ctx context.Context, cache *TokenCache, opts TokenOptions, flow Flow, cacheKey string, getCreds func() (T, error), isCredsNil func(T) bool, getFingerprint func(T) string, acquireToken TokenAcquireFunc[T], logPrefix string, ) (*Token, error)
GetCachedOrAcquireToken is a generic helper that handles the common pattern of: 1. Retrieving credentials 2. Checking the cache (unless ForceRefresh) 3. Acquiring a new token if needed 4. Caching the new token
Parameters:
- ctx: context for cancellation and logging
- cache: the token cache to read/write from
- opts: token acquisition options (scope, min validity, force refresh flags)
- flow: the authentication flow type for cache partitioning
- cacheKey: the cache lookup key (typically opts.Scope; GitHub uses a fixed key)
- getCreds: retrieves credentials; returns (T, error) — return a nil error if the credential source doesn't produce errors
- isCredsNil: returns true when the credential value indicates "not configured"
- getFingerprint: computes a cache-partitioning fingerprint from the credentials
- acquireToken: mints a fresh token using the credentials and scope
- logPrefix: short label for log messages (e.g. "SP", "WI", "pat")
func (*Token) IsValidFor ¶
IsValidFor returns true if the token will be valid for at least the specified duration.
func (*Token) TimeUntilExpiry ¶
TimeUntilExpiry returns the duration until the token expires. Returns 0 if the token is already expired or invalid.
type TokenAcquireFunc ¶ added in v0.6.0
TokenAcquireFunc is a function that acquires a token given credentials and a scope (or cache key).
type TokenCache ¶ added in v0.6.0
type TokenCache struct {
// contains filtered or unexported fields
}
TokenCache provides disk-based caching for access tokens via pkg/secrets. Tokens are stored encrypted and survive process restarts. Each (flow, fingerprint, scope) tuple has its own secret key for atomic updates and fault isolation. The fingerprint prevents cross-config cache contamination when users switch between different authentication configurations (e.g., different tenant IDs, client IDs, WIF audiences).
TokenCache is shared by all auth handlers (entra, gcp, github). Each handler provides its own key prefix at construction time to namespace its entries.
func NewTokenCache ¶ added in v0.6.0
func NewTokenCache(secretStore secrets.Store, prefix string) *TokenCache
NewTokenCache creates a new disk-based token cache. The prefix parameter namespaces cache entries per handler (e.g., "scafctl.auth.entra.token.").
func (*TokenCache) CacheKey ¶ added in v0.6.0
func (c *TokenCache) CacheKey(flow Flow, fingerprint, scope string) string
CacheKey builds the secret store key for a given flow, fingerprint, and scope. Format: <prefix><flow>.<fingerprint>.<base64url(scope)>
func (*TokenCache) Clear ¶ added in v0.6.0
func (c *TokenCache) Clear(ctx context.Context) error
Clear removes all cached tokens by listing secrets with the handler's token prefix and deleting them.
func (*TokenCache) Delete ¶ added in v0.6.0
Delete removes a cached token for the given flow, fingerprint, and scope.
func (*TokenCache) Get ¶ added in v0.6.0
Get retrieves a token for the given flow, fingerprint, and scope from disk cache. Returns nil, nil if no token is cached for this combination. Returns nil, error if there was an error reading the cache.
func (*TokenCache) ListCachedEntries ¶ added in v0.6.0
func (c *TokenCache) ListCachedEntries(ctx context.Context) ([]CacheEntry, error)
ListCachedEntries returns all cached (flow, fingerprint, scope) entries.
func (*TokenCache) ParseKey ¶ added in v0.6.0
ParseKey extracts the flow, fingerprint, and scope from a secret key. Returns false if the key is not a valid token cache key for this cache's prefix.
func (*TokenCache) Prefix ¶ added in v0.6.0
func (c *TokenCache) Prefix() string
Prefix returns the secret key prefix used by this TokenCache.
func (*TokenCache) PurgeExpired ¶ added in v0.6.0
func (c *TokenCache) PurgeExpired(ctx context.Context) (int, error)
PurgeExpired removes all expired access tokens from the cache. Returns the number of tokens removed.
type TokenCacheAccessor ¶ added in v0.6.0
type TokenCacheAccessor interface {
GetTokenCache() *TokenCache
}
TokenCacheAccessor provides access to the token cache. All three handler packages (entra, gcp, github) satisfy this via their Handler structs.
type TokenLister ¶ added in v0.5.0
type TokenLister interface {
ListCachedTokens(ctx context.Context) ([]*CachedTokenInfo, error)
}
TokenLister is an optional interface implemented by auth handlers that can enumerate all cached tokens (both refresh and minted access tokens).
type TokenOptions ¶
type TokenOptions struct {
Scope string `` /* 149-byte string literal not displayed */
MinValidFor time.Duration `json:"minValidFor,omitempty" yaml:"minValidFor,omitempty" doc:"Minimum remaining validity for cached tokens"`
ForceRefresh bool `json:"forceRefresh,omitempty" yaml:"forceRefresh,omitempty" doc:"Force token refresh even if cached token is valid"`
}
TokenOptions configures token acquisition.
type TokenPurger ¶ added in v0.5.0
TokenPurger is an optional interface implemented by auth handlers that can remove expired access tokens from the cache without affecting valid tokens or the refresh token. Returns the number of tokens removed.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package diagnose provides reusable auth diagnostic checks.
|
Package diagnose provides reusable auth diagnostic checks. |
|
Package entra provides Microsoft Entra ID (formerly Azure AD) authentication for scafctl using the OAuth 2.0 device authorization flow.
|
Package entra provides Microsoft Entra ID (formerly Azure AD) authentication for scafctl using the OAuth 2.0 device authorization flow. |
|
Package gcp provides Google Cloud Platform authentication for scafctl.
|
Package gcp provides Google Cloud Platform authentication for scafctl. |
|
Package github provides GitHub authentication for scafctl.
|
Package github provides GitHub authentication for scafctl. |
|
Package oauth provides shared OAuth 2.0 utilities used by multiple auth handlers, including PKCE code generation, browser launching, and local callback servers.
|
Package oauth provides shared OAuth 2.0 utilities used by multiple auth handlers, including PKCE code generation, browser launching, and local callback servers. |