auth

package
v0.13.0 Latest Latest
Warning

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

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

README

Auth Package

Multi-provider authentication framework for scafctl with pluggable handlers, encrypted token caching, and a shared token-acquisition pipeline.

Features

  • Pluggable Handlers: Register any number of auth providers (Entra ID, GCP, GitHub) via a thread-safe Registry
  • Encrypted Token Cache: Disk-backed cache using pkg/secrets, keyed by (flow, fingerprint, scope)
  • Generic Token Pipeline: Shared GetCachedOrAcquireToken[T] eliminates boilerplate across handlers
  • Normalized Claims: Common Claims struct maps provider-specific identity data into a single shape
  • Sentinel Errors: Typed errors (ErrNotAuthenticated, ErrTokenExpired, ErrInvalidScope, etc.) for programmatic error handling
  • Optional Interfaces: Handlers can implement TokenLister, TokenPurger, or GroupsProvider for extended capabilities
  • Context Integration: Store and retrieve the Registry via context.Context helpers

Architecture

pkg/auth/
├── handler.go        # Handler interface, Flow/Token/Status types
├── registry.go       # Thread-safe handler registry
├── context.go        # Context helpers (WithRegistry, RegistryFromContext)
├── cache.go          # TokenCache — encrypted disk cache via pkg/secrets
├── token_acquire.go  # GetCachedOrAcquireToken[T] — shared generic pipeline
├── claims.go         # Normalized identity claims
├── errors.go         # Sentinel errors and helpers
├── flow.go           # ParseFlow — user-facing flow string → Flow constant
├── capability.go     # Handler capability flags
├── fingerprint.go    # SHA-256 identity fingerprint for cache keys
├── groups.go         # Optional GroupsProvider interface
├── mock.go           # MockHandler for testing
├── entra/            # Microsoft Entra ID handler
├── gcp/              # Google Cloud Platform handler
├── github/           # GitHub handler
└── oauth/            # Shared OAuth 2.0 utilities (PKCE, callback server)

Core Interface

Every handler implements Handler:

type Handler interface {
    Name() string
    DisplayName() string
    Login(ctx context.Context, opts LoginOptions) (*Result, error)
    Logout(ctx context.Context) error
    Status(ctx context.Context) (*Status, error)
    GetToken(ctx context.Context, opts TokenOptions) (*Token, error)
    InjectAuth(ctx context.Context, req *http.Request, opts TokenOptions) error
    SupportedFlows() []Flow
    Capabilities() []Capability
}

Supported Flows

Flow Entra GCP GitHub Description
interactive Browser-based OAuth with PKCE
device_code Device authorization grant
service_principal Client credentials / SA key JWT
workload_identity Federated token exchange
pat Personal access token
metadata GCE metadata server
gcloud_adc gcloud application-default credentials
github_app GitHub App installation token

Quick Start

Register Handlers
reg := auth.NewRegistry()

entraHandler, _ := entra.New(entra.WithConfig(entraConfig))
reg.Register(entraHandler)

gcpHandler, _ := gcp.New(gcp.WithConfig(gcpConfig))
reg.Register(gcpHandler)

// Attach to context
ctx = auth.WithRegistry(ctx, reg)
Login
handler, _ := auth.GetHandler(ctx, "gcp")
result, err := handler.Login(ctx, auth.LoginOptions{
    Flow:   auth.FlowServicePrincipal,
    Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"},
})
Acquire a Token
token, err := handler.GetToken(ctx, auth.TokenOptions{
    Flow:  auth.FlowServicePrincipal,
    Scope: "https://www.googleapis.com/auth/cloud-platform",
})
Inject Into HTTP Requests
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
err := handler.InjectAuth(ctx, req, auth.TokenOptions{
    Scope: "https://www.googleapis.com/auth/cloud-platform",
})
// req now has Authorization: Bearer <token>

Token Cache

TokenCache provides encrypted, disk-backed token storage via pkg/secrets.

Cache keys follow the pattern <prefix>:<flow>:<fingerprint>:<scope> where the fingerprint is a truncated SHA-256 of the identity (e.g. client email or tenant+client ID).

cache := auth.NewTokenCache(secretStore, "gcp-token")

// Store
cache.Set(ctx, auth.FlowServicePrincipal, "fingerprint", "scope", token)

// Retrieve (returns nil if missing or expired)
cached, _ := cache.Get(ctx, auth.FlowServicePrincipal, "fingerprint", "scope")

// Housekeeping
cache.PurgeExpired(ctx)
cache.Clear(ctx)

Shared Token Pipeline

GetCachedOrAcquireToken[T] is the generic function all handlers use for the cache → acquire → store cycle:

token, err := auth.GetCachedOrAcquireToken(
    ctx,
    handler.tokenCache,
    opts,
    auth.FlowServicePrincipal,
    opts.Scope,                                    // cache key
    func() (*Creds, error) { return loadCreds() }, // credential loader
    func(c *Creds) bool { return c == nil },       // nil check
    func(c *Creds) string { return fingerprint },  // cache fingerprint
    func(ctx context.Context, c *Creds, scope string) (*auth.Token, error) {
        return acquireToken(ctx, c, scope)         // actual token acquisition
    },
    "MyFlow",                                      // log prefix
)

Sentinel Errors

| Error | When | |-----------------------------|--------------------------------------------|\n| ErrNotAuthenticated | No cached credentials available |\n| ErrAuthenticationFailed | Login or token acquisition failed |\n| ErrTokenExpired | Cached token past expiry |\n| ErrInvalidScope | Empty or missing scope |\n| ErrConsentRequired | User interaction needed |\n| ErrHandlerNotFound | Handler not in registry |\n| ErrFlowNotSupported | Handler doesn't support requested flow |\n| ErrUserCancelled | User aborted interactive flow |\n| ErrTimeout | Operation timed out |\n| ErrAlreadyAuthenticated | Already logged in |\n| ErrGrantInvalid | Refresh token or grant revoked |\n| ErrCapabilityNotSupported | Handler lacks required capability |

Provider-specific sentinel errors follow the same pattern:

  • gcp.ErrNoServiceAccountConfigured
  • gcp.ErrNoWorkloadIdentityConfigured
  • gcp.ErrNoGcloudADCConfigured

Testing

Use MockHandler for unit testing code that depends on auth:

mock := auth.NewMockHandler("mock")
mock.SetToken(&auth.Token{AccessToken: "test-token", TokenType: "Bearer", ExpiresAt: time.Now().Add(time.Hour)})

Each sub-package also exposes mocks (e.g. gcp.NewMockHTTPClient()) for testing handler internals.

Capabilities

Handlers declare capabilities to let callers discover feature support:

Capability Description
CapScopesOnLogin Accepts scopes at login time
CapScopesOnTokenRequest Accepts per-request scopes
CapTenantID Supports tenant ID parameter
CapHostname Supports hostname (e.g. GHES)
CapFederatedToken Accepts federated token input
CapCallbackPort Configurable OAuth callback port
if auth.HasCapability(handler.Capabilities(), auth.CapScopesOnTokenRequest) {
    // safe to pass per-request scopes
}

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

View Source
const (
	CapScopesOnLogin        = sdkauth.CapScopesOnLogin
	CapScopesOnTokenRequest = sdkauth.CapScopesOnTokenRequest
	CapTenantID             = sdkauth.CapTenantID
	CapHostname             = sdkauth.CapHostname
	CapFederatedToken       = sdkauth.CapFederatedToken
	CapCallbackPort         = sdkauth.CapCallbackPort
	CapFlowOverride         = sdkauth.CapFlowOverride
)
View Source
const (
	FlowDeviceCode        = sdkauth.FlowDeviceCode
	FlowInteractive       = sdkauth.FlowInteractive
	FlowServicePrincipal  = sdkauth.FlowServicePrincipal
	FlowWorkloadIdentity  = sdkauth.FlowWorkloadIdentity
	FlowPAT               = sdkauth.FlowPAT
	FlowMetadata          = sdkauth.FlowMetadata
	FlowGcloudADC         = sdkauth.FlowGcloudADC
	FlowGitHubApp         = sdkauth.FlowGitHubApp
	FlowClientCredentials = sdkauth.FlowClientCredentials
)
View Source
const (
	IdentityTypeUser             = sdkauth.IdentityTypeUser
	IdentityTypeServicePrincipal = sdkauth.IdentityTypeServicePrincipal
	IdentityTypeWorkloadIdentity = sdkauth.IdentityTypeWorkloadIdentity
)
View Source
const DefaultMinValidFor = sdkauth.DefaultMinValidFor

DefaultMinValidFor is the default minimum validity duration for tokens.

Variables

View Source
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")
	ErrClaimsChallenge        = errors.New("claims challenge: re-authentication required by Conditional Access policy")
)

Sentinel errors for the auth package.

View Source
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.

View Source
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

func FingerprintHash(identity string) string

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

func HasHandler(ctx context.Context, name string) bool

HasHandler checks if an auth handler exists in the context's registry.

func IsAlreadyAuthenticated added in v0.6.0

func IsAlreadyAuthenticated(err error) bool

IsAlreadyAuthenticated returns true if the error indicates the user is already authenticated.

func IsAuthenticationFailed added in v0.6.0

func IsAuthenticationFailed(err error) bool

IsAuthenticationFailed returns true if the error indicates authentication failed.

func IsCapabilityNotSupported added in v0.5.0

func IsCapabilityNotSupported(err error) bool

IsCapabilityNotSupported returns true if the error indicates a capability is not supported.

func IsClaimsChallenge added in v0.9.0

func IsClaimsChallenge(err error) bool

IsClaimsChallenge returns true if the error indicates a claims challenge is required.

func IsConsentRequired added in v0.4.0

func IsConsentRequired(err error) bool

IsConsentRequired returns true if the error indicates consent is required for the requested scope.

func IsFlowNotSupported added in v0.6.0

func IsFlowNotSupported(err error) bool

IsFlowNotSupported returns true if the error indicates the flow is not supported.

func IsGrantInvalid added in v0.6.0

func IsGrantInvalid(err error) bool

IsGrantInvalid returns true if the error indicates the grant (refresh token) is invalid or revoked.

func IsHandlerNotFound

func IsHandlerNotFound(err error) bool

IsHandlerNotFound returns true if the error indicates the handler was not found.

func IsInvalidScope added in v0.6.0

func IsInvalidScope(err error) bool

IsInvalidScope returns true if the error indicates the scope is invalid.

func IsNotAuthenticated

func IsNotAuthenticated(err error) bool

IsNotAuthenticated returns true if the error indicates the user is not authenticated.

func IsTimeout

func IsTimeout(err error) bool

IsTimeout returns true if the error indicates a timeout occurred.

func IsTokenExpired

func IsTokenExpired(err error) bool

IsTokenExpired returns true if the error indicates the token has expired.

func IsUserCancelled

func IsUserCancelled(err error) bool

IsUserCancelled returns true if the error indicates the user cancelled.

func ListHandlers

func ListHandlers(ctx context.Context) []string

ListHandlers lists all auth handlers in the context's registry.

func WithRegistry

func WithRegistry(ctx context.Context, registry *Registry) context.Context

WithRegistry returns a new context with the auth registry attached.

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 = sdkauth.CachedTokenInfo

CachedTokenInfo holds display metadata for a cached token.

type Capability added in v0.5.0

type Capability = sdkauth.Capability

Capability represents a feature or behavior that an auth handler supports.

type Claims

type Claims = sdkauth.Claims

Claims represents normalized identity claims from any auth handler.

func ParseJWTClaims added in v0.6.0

func ParseJWTClaims(rawJWT string) (*Claims, error)

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).

type ClaimsChallengeError added in v0.9.0

type ClaimsChallengeError struct {
	// Claims is the base64url-encoded claims challenge string from the token endpoint.
	Claims string
	// Scope is the scope that triggered the claims challenge.
	Scope string
}

ClaimsChallengeError wraps ErrClaimsChallenge with the raw claims payload returned by the token endpoint so callers can pass it into a re-authentication request.

func (*ClaimsChallengeError) Error added in v0.9.0

func (e *ClaimsChallengeError) Error() string

Error implements the error interface.

func (*ClaimsChallengeError) Unwrap added in v0.9.0

func (e *ClaimsChallengeError) Unwrap() error

Unwrap returns the sentinel so errors.Is(err, ErrClaimsChallenge) works.

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.

func NewError

func NewError(handler, operation string, cause error) *Error

NewError creates a new Error.

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the underlying cause.

type Flow

type Flow = sdkauth.Flow

Flow represents an authentication flow type.

func ParseFlow added in v0.5.0

func ParseFlow(flowStr, handlerName string) (Flow, error)

ParseFlow converts a flow string to a Flow constant. If flowStr is empty, an empty Flow is returned (caller should auto-detect). handlerName is used to produce handler-specific error messages for unrecognised values.

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:

  1. If explicitFlow is non-empty, use it.
  2. Check detectors in order; use the first one with available credentials.
  3. Fall back to defaultFlow.

type FlowReporter added in v0.10.1

type FlowReporter interface {
	// ActiveFlow returns the authentication flow currently in use.
	// Returns an empty string if the flow cannot be determined.
	ActiveFlow(ctx context.Context) Flow
}

FlowReporter is an optional interface for auth handlers that can report the credential source currently being used (e.g. device-code, gcloud-adc, workload-identity). The status command uses this to display the active flow.

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. This interface is host-only and not part of the SDK because it references net/http.

func GetHandler

func GetHandler(ctx context.Context, name string) (Handler, error)

GetHandler gets an auth handler from the context's registry.

type IdentityType

type IdentityType = sdkauth.IdentityType

IdentityType represents the type of authenticated identity.

type LoginOptions

type LoginOptions = sdkauth.LoginOptions

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) Logout

func (m *MockHandler) Logout(_ context.Context) 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) Status

func (m *MockHandler) Status(_ context.Context) (*Status, 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 NewRegistry

func NewRegistry() *Registry

NewRegistry creates a new auth handler registry.

func RegistryFromContext

func RegistryFromContext(ctx context.Context) *Registry

RegistryFromContext retrieves the auth registry from the context.

func (*Registry) All

func (r *Registry) All() map[string]Handler

All returns all registered handlers as a map.

func (*Registry) Count

func (r *Registry) Count() int

Count returns the number of registered handlers.

func (*Registry) Get

func (r *Registry) Get(name string) (Handler, error)

Get retrieves an auth handler by name.

func (*Registry) Has

func (r *Registry) Has(name string) bool

Has returns true if a handler with the given name is registered.

func (*Registry) List

func (r *Registry) List() []string

List returns the names of all registered handlers in sorted order.

func (*Registry) Register

func (r *Registry) Register(handler Handler) error

Register adds an auth handler to the registry.

func (*Registry) Unregister

func (r *Registry) Unregister(name string) error

Unregister removes an auth handler from the registry.

type Result

type Result = sdkauth.Result

Result contains the result of a successful authentication.

type Status

type Status = sdkauth.Status

Status represents the current authentication state.

type Token

type Token = sdkauth.Token

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")

type TokenAcquireFunc added in v0.6.0

type TokenAcquireFunc[T any] func(ctx context.Context, creds T, scope string) (*Token, error)

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

func (c *TokenCache) Delete(ctx context.Context, flow Flow, fingerprint, scope string) error

Delete removes a cached token for the given flow, fingerprint, and scope.

func (*TokenCache) Get added in v0.6.0

func (c *TokenCache) Get(ctx context.Context, flow Flow, fingerprint, scope string) (*Token, error)

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

func (c *TokenCache) ParseKey(key string) (Flow, string, string, bool)

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.

func (*TokenCache) Set added in v0.6.0

func (c *TokenCache) Set(ctx context.Context, flow Flow, fingerprint, scope string, token *Token) error

Set stores a token for the given flow, fingerprint, and scope to disk cache.

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 = sdkauth.TokenLister

TokenLister is an optional interface for auth handlers that can enumerate cached tokens.

type TokenOptions

type TokenOptions = sdkauth.TokenOptions

TokenOptions configures token acquisition.

type TokenPurger added in v0.5.0

type TokenPurger = sdkauth.TokenPurger

TokenPurger is an optional interface for auth handlers that can remove expired tokens.

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.
Package oauth2 implements a generic configurable OAuth2 auth handler.
Package oauth2 implements a generic configurable OAuth2 auth handler.

Jump to

Keyboard shortcuts

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