Documentation
¶
Index ¶
- type AuditLogEntry
- type AuditLogger
- type AuditStore
- type AuthProvider
- type AuthResult
- type AuthorizationCodeStore
- type Cache
- type CleanupStore
- type ClientReader
- type ClientType
- type ClientWriter
- type DashboardStore
- type DeviceCodeStore
- type IDTokenParams
- type IDTokenProvider
- type Infrastructure
- type MetricsStore
- type OAuthConnectionStore
- type Recorder
- type Store
- type TokenProvider
- type TokenReader
- type TokenRefreshResult
- type TokenResult
- type TokenValidationResult
- type TokenWriter
- type Transactor
- type UserAuthorizationStore
- type UserReader
- type UserWriter
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AuditLogEntry ¶ added in v0.25.0
type AuditLogEntry struct {
EventType models.EventType
Severity models.EventSeverity
ActorUserID string
ActorUsername string
ActorFullName string
ActorIP string
ResourceType models.ResourceType
ResourceID string
ResourceName string
Action string
Details models.AuditDetails
Success bool
ErrorMessage string
UserAgent string
RequestPath string
RequestMethod string
}
AuditLogEntry represents the data needed to create an audit log entry.
type AuditLogger ¶ added in v0.25.0
type AuditLogger interface {
Log(ctx context.Context, entry AuditLogEntry)
LogSync(ctx context.Context, entry AuditLogEntry) error
GetAuditLogs(
params types.PaginationParams,
filters types.AuditLogFilters,
) ([]models.AuditLog, types.PaginationResult, error)
CleanupOldLogs(retention time.Duration) (int64, error)
GetAuditLogStats(startTime, endTime time.Time) (types.AuditLogStats, error)
Shutdown(ctx context.Context) error
}
AuditLogger defines the contract for audit logging operations. Implementations must be safe for concurrent use by multiple goroutines. Implementations include the real AuditService (buffered, database-backed) and NoopAuditService (silent no-op for when auditing is disabled).
type AuditStore ¶ added in v0.20.0
type AuditStore interface {
CreateAuditLog(log *models.AuditLog) error
CreateAuditLogBatch(logs []*models.AuditLog) error
GetAuditLogsPaginated(
params types.PaginationParams,
filters types.AuditLogFilters,
) ([]models.AuditLog, types.PaginationResult, error)
DeleteOldAuditLogs(olderThan time.Time) (int64, error)
GetAuditLogStats(startTime, endTime time.Time) (types.AuditLogStats, error)
}
AuditStore groups audit log operations.
type AuthProvider ¶
type AuthProvider interface {
Authenticate(ctx context.Context, username, password string) (*AuthResult, error)
Name() string
}
AuthProvider is the interface that password-based authentication backends must implement.
type AuthResult ¶
type AuthResult struct {
Username string
ExternalID string // External user ID (e.g., LDAP DN, API user ID)
Email string // Optional
FullName string // Optional
}
AuthResult holds the outcome of an authentication attempt.
type AuthorizationCodeStore ¶ added in v0.20.0
type AuthorizationCodeStore interface {
CreateAuthorizationCode(code *models.AuthorizationCode) error
GetAuthorizationCodeByHash(hash string) (*models.AuthorizationCode, error)
MarkAuthorizationCodeUsed(id uint) error
}
AuthorizationCodeStore groups authorization code operations.
type Cache ¶
type Cache[T any] interface { // Get retrieves a single value from cache. // Returns ErrCacheMiss if the key does not exist or has expired. Get(ctx context.Context, key string) (T, error) // Set stores a single value in cache with TTL Set(ctx context.Context, key string, value T, ttl time.Duration) error // Delete removes a key from cache Delete(ctx context.Context, key string) error // Close closes the cache connection Close() error // Health checks if the cache is healthy Health(ctx context.Context) error // GetWithFetch retrieves a value using the cache-aside pattern. // On cache miss, fetchFunc is called and the result is stored in cache. // Implementations may provide stampede protection (e.g. RueidisAsideCache). GetWithFetch( ctx context.Context, key string, ttl time.Duration, fetchFunc func(ctx context.Context, key string) (T, error), ) (T, error) }
Cache[T] defines the primitive operations for a key-value cache. T is the type of value stored in the cache (e.g. int64, string, or a struct).
type CleanupStore ¶ added in v0.20.0
CleanupStore groups expired-data cleanup operations.
type ClientReader ¶ added in v0.20.0
type ClientReader interface {
GetClient(clientID string) (*models.OAuthApplication, error)
GetClientByIntID(id int64) (*models.OAuthApplication, error)
GetClientsByIDs(clientIDs []string) (map[string]*models.OAuthApplication, error)
ListClientsPaginated(
params types.PaginationParams,
) ([]models.OAuthApplication, types.PaginationResult, error)
ListClientsByUserID(
userID string,
params types.PaginationParams,
) ([]models.OAuthApplication, types.PaginationResult, error)
CountClientsByStatus(status string) (int64, error)
CountActiveTokensByClientID(clientID string) (int64, error)
}
ClientReader groups read-only client operations.
type ClientType ¶ added in v0.22.0
type ClientType string
ClientType represents the OAuth 2.0 client type.
const ( ClientTypeConfidential ClientType = "confidential" ClientTypePublic ClientType = "public" )
func NormalizeClientType ¶ added in v0.22.0
func NormalizeClientType(raw string) ClientType
NormalizeClientType converts a raw string to a ClientType. Returns ClientTypeConfidential for any unrecognized value.
func (ClientType) IsValid ¶ added in v0.22.0
func (ct ClientType) IsValid() bool
IsValid reports whether the ClientType is a known value.
func (ClientType) OrDefault ¶ added in v0.22.0
func (ct ClientType) OrDefault() ClientType
OrDefault returns the ClientType if valid, otherwise ClientTypeConfidential.
func (ClientType) String ¶ added in v0.22.0
func (ct ClientType) String() string
String returns the string representation of the ClientType.
type ClientWriter ¶ added in v0.20.0
type ClientWriter interface {
CreateClient(client *models.OAuthApplication) error
UpdateClient(client *models.OAuthApplication) error
DeleteClient(clientID string) error
}
ClientWriter groups client mutation operations.
type DashboardStore ¶ added in v0.24.0
type DashboardStore interface {
GetDashboardCounts() (types.DashboardCounts, error)
}
DashboardStore groups aggregated count queries for the admin dashboard.
type DeviceCodeStore ¶ added in v0.20.0
type DeviceCodeStore interface {
CreateDeviceCode(dc *models.DeviceCode) error
GetDeviceCodesByID(deviceCodeID string) ([]*models.DeviceCode, error)
GetDeviceCodeByUserCode(userCode string) (*models.DeviceCode, error)
UpdateDeviceCode(dc *models.DeviceCode) error
AuthorizeDeviceCode(id int64, userID string) error
DeleteDeviceCodeByID(id int64) (int64, error)
}
DeviceCodeStore groups device code operations.
type IDTokenParams ¶
type IDTokenParams struct {
Issuer string
Subject string // UserID
Audience string // ClientID
AuthTime time.Time
Nonce string
Expiry time.Duration
AtHash string // base64url(SHA-256(access_token)[:16]) – optional
// Scope-gated profile claims (include when "profile" scope was granted)
Name string
PreferredUsername string
Picture string
UpdatedAt *time.Time
// Scope-gated email claims (include when "email" scope was granted)
Email string
EmailVerified bool
}
IDTokenParams holds all data needed to generate an OIDC ID Token (OIDC Core 1.0 §2).
type IDTokenProvider ¶
type IDTokenProvider interface {
GenerateIDToken(params IDTokenParams) (string, error)
}
IDTokenProvider is an optional capability of a TokenProvider.
type Infrastructure ¶ added in v0.20.0
Infrastructure groups lifecycle and health operations.
type MetricsStore ¶
type MetricsStore interface {
CountActiveTokensByCategory(category string) (int64, error)
CountTotalDeviceCodes() (int64, error)
CountPendingDeviceCodes() (int64, error)
}
MetricsStore defines the DB operations needed by CacheWrapper.
type OAuthConnectionStore ¶ added in v0.20.0
type OAuthConnectionStore interface {
CreateOAuthConnection(conn *models.OAuthConnection) error
GetOAuthConnection(provider, providerUserID string) (*models.OAuthConnection, error)
GetOAuthConnectionByUserAndProvider(userID, provider string) (*models.OAuthConnection, error)
GetOAuthConnectionByUserAndID(userID, connectionID string) (*models.OAuthConnection, error)
GetOAuthConnectionsByUserID(userID string) ([]models.OAuthConnection, error)
UpdateOAuthConnection(conn *models.OAuthConnection) error
DeleteOAuthConnection(id string) error
DeleteOAuthConnectionsByUserID(userID string) error
}
OAuthConnectionStore groups external OAuth connection operations.
type Recorder ¶
type Recorder interface {
// OAuth Device Flow
RecordOAuthDeviceCodeGenerated(success bool)
RecordOAuthDeviceCodeAuthorized(authorizationTime time.Duration)
RecordOAuthDeviceCodeValidation(result string)
// Token Operations
RecordTokenIssued(tokenType, grantType string, generationTime time.Duration, provider string)
RecordTokenRevoked(tokenType, reason string)
RecordTokenRefresh(success bool)
RecordTokenValidation(result string, duration time.Duration, provider string)
// Authentication
RecordAuthAttempt(method string, success bool, duration time.Duration)
RecordLogin(authSource string, success bool)
RecordLogout(sessionDuration time.Duration)
RecordOAuthCallback(provider string, success bool)
// Gauge Setters (for periodic updates)
SetActiveTokensCount(tokenType string, count int)
SetActiveDeviceCodesCount(total, pending int)
// Database Operations
RecordDatabaseQueryError(operation string)
}
Recorder defines the interface for recording application metrics. Implementations include Metrics (Prometheus-based) and NoopMetrics (no-op).
type Store ¶ added in v0.20.0
type Store interface {
UserReader
UserWriter
ClientReader
ClientWriter
DeviceCodeStore
TokenReader
TokenWriter
AuthorizationCodeStore
UserAuthorizationStore
OAuthConnectionStore
AuditStore
MetricsStore
DashboardStore
CleanupStore
Transactor
Infrastructure
}
Store is the aggregate data-access interface. Services accept this; the composition root passes the concrete *store.Store.
type TokenProvider ¶
type TokenProvider interface {
GenerateToken(
ctx context.Context,
userID, clientID, scopes string,
ttl time.Duration,
extraClaims map[string]any,
audience []string,
) (*TokenResult, error)
GenerateRefreshToken(
ctx context.Context,
userID, clientID, scopes string,
ttl time.Duration,
extraClaims map[string]any,
audience []string,
) (*TokenResult, error)
// GenerateClientCredentialsToken generates a token for the client_credentials grant.
// May apply a different expiry or claim set than GenerateToken.
GenerateClientCredentialsToken(
ctx context.Context,
userID, clientID, scopes string,
ttl time.Duration,
extraClaims map[string]any,
audience []string,
) (*TokenResult, error)
ValidateToken(ctx context.Context, tokenString string) (*TokenValidationResult, error)
// ValidateRefreshToken verifies a refresh-token JWT and returns the parsed
// claims (including `aud`). Callers — notably the refresh flow in
// TokenService — use the signed claims as the authoritative source of the
// original grant's audience when the persisted `Resource` column is
// empty (e.g. legacy rows issued before fix #2 snapshotted the effective
// audience on issuance). This prevents a later JWT_AUDIENCE rotation
// from silently retargeting refreshed access tokens to an audience the
// original token's JWT never carried.
ValidateRefreshToken(
ctx context.Context,
tokenString string,
) (*TokenValidationResult, error)
// RefreshAccessToken issues a new access token (and, in rotation mode, a
// new refresh token) from a valid refresh-token string.
//
// accessScope narrows the new access token's `scope` claim (RFC 6749 §6).
// When non-empty it overrides the scope signed into the original refresh
// token; when empty the original scope is reused. The caller (TokenService)
// is responsible for enforcing that accessScope is a subset of the original
// grant before invoking this method. The rotated refresh token always keeps
// the original grant's scope so a later refresh can re-request up to it.
//
// accessAudience and refreshAudience are kept separate so a refresh token
// never carries the per-request resource-server `aud`. A refresh token is
// presented to the AS, not the RS — emitting the same per-request audience
// as the access token would let a downstream JWT validator that only
// checks signature/iss/exp/aud silently accept the refresh token as if it
// were an access token. Pass nil for refreshAudience to fall back to the
// static JWTAudience config; deployments are expected to set
// `JWT_AUDIENCE` to an AS-only value (or leave it unset so the claim is
// omitted) — pointing it at a resource server would re-introduce the
// confusion this split is designed to prevent.
RefreshAccessToken(
ctx context.Context,
refreshToken string,
accessTTL, refreshTTL time.Duration,
extraClaims map[string]any,
accessScope string,
accessAudience []string,
refreshAudience []string,
) (*TokenRefreshResult, error)
Name() string
}
TokenProvider is the interface that token-generation backends must implement. A ttl of 0 on any Generate* method means "use the provider's default expiry" (typically derived from config). A non-zero ttl overrides the default and disables any jitter the provider would normally apply.
extraClaims (when non-empty) is merged into the generated JWT before standard claims are set; standard claims (iss, sub, exp, iat, jti, aud, type, scope, user_id, client_id) take precedence and cannot be overridden.
audience (when non-empty) overrides the "aud" claim with the supplied values (RFC 8707 Resource Indicators). When empty, the provider falls back to its default audience configuration. Caller-supplied "aud" inside extraClaims is always stripped — audience must be passed explicitly through this parameter.
type TokenReader ¶ added in v0.20.0
type TokenReader interface {
GetAccessTokenByHash(hash string) (*models.AccessToken, error)
GetAccessTokenByID(tokenID string) (*models.AccessToken, error)
GetTokensByUserID(userID string) ([]models.AccessToken, error)
GetTokensByUserIDPaginated(
userID string,
params types.PaginationParams,
) ([]models.AccessToken, types.PaginationResult, error)
GetTokensPaginated(
params types.PaginationParams,
) ([]models.AccessToken, types.PaginationResult, error)
GetTokensByCategoryAndStatus(userID, category, status string) ([]models.AccessToken, error)
GetActiveTokenHashesByFamilyID(familyID string) ([]string, error)
GetActiveTokenHashesByAuthorizationID(authorizationID uint) ([]string, error)
GetActiveTokenHashesByClientID(clientID string) ([]string, error)
GetTokenHashesByUserID(userID string) ([]string, error)
}
TokenReader groups read-only token operations.
type TokenRefreshResult ¶
type TokenRefreshResult struct {
AccessToken *TokenResult // required
RefreshToken *TokenResult // non-nil only in rotation mode
}
TokenRefreshResult is the outcome of a refresh-token exchange.
type TokenResult ¶
type TokenResult struct {
TokenString string
TokenType string
ExpiresAt time.Time
Claims map[string]any
}
TokenResult is the outcome of a token generation call.
type TokenValidationResult ¶
type TokenValidationResult struct {
Valid bool
UserID string
ClientID string
Scopes string
ExpiresAt time.Time
Claims map[string]any
}
TokenValidationResult is the outcome of a token validation call.
type TokenWriter ¶ added in v0.20.0
type TokenWriter interface {
CreateAccessToken(token *models.AccessToken) error
RevokeToken(tokenID string) error
RevokeTokensByUserID(userID string) error
RevokeTokensByClientID(clientID string) error
RevokeTokenFamily(familyID string) (int64, error)
UpdateTokenStatus(tokenID, status string) error
// RevokeRefreshTokenIfActive atomically revokes a refresh token only when it
// is still active, returning RowsAffected (1 = won the race, 0 = a concurrent
// refresh already consumed it). Used by rotation-mode refresh to guarantee
// single use; distinct from UpdateTokenStatus, which stays unconditional for
// the enable/disable/revoke admin paths.
RevokeRefreshTokenIfActive(tokenID string) (int64, error)
UpdateTokenLastUsedAt(tokenID string, t time.Time) error
RevokeTokensByAuthorizationID(authorizationID uint) error
RevokeAllActiveTokensByClientID(clientID string) (int64, error)
}
TokenWriter groups token mutation operations.
type Transactor ¶ added in v0.20.0
Transactor provides database transaction support.
type UserAuthorizationStore ¶ added in v0.20.0
type UserAuthorizationStore interface {
GetUserAuthorization(userID string, applicationID int64) (*models.UserAuthorization, error)
GetUserAuthorizationByUUID(authUUID, userID string) (*models.UserAuthorization, error)
UpsertUserAuthorization(auth *models.UserAuthorization) error
RevokeUserAuthorization(authUUID, userID string) (*models.UserAuthorization, error)
ListUserAuthorizations(userID string) ([]models.UserAuthorization, error)
GetClientAuthorizations(clientID string) ([]models.UserAuthorization, error)
RevokeAllUserAuthorizationsByClientID(clientID string) error
RevokeAllUserAuthorizationsByUserID(userID string) error
}
UserAuthorizationStore groups per-app consent grant operations.
type UserReader ¶ added in v0.20.0
type UserReader interface {
GetUserByUsername(username string) (*models.User, error)
GetUserByID(id string) (*models.User, error)
GetUserByEmail(email string) (*models.User, error)
// FindUserByNormalizedEmail locates a user whose stored email matches the
// input after trimming whitespace on both sides, and returns an error when
// more than one legacy row ties to the same normalized value. Used by the
// OAuth auto-link path where picking a non-deterministic match would risk
// binding a verified provider to the wrong local user. Slower than
// GetUserByEmail because the fallback is not index-friendly — callers that
// don't need whitespace tolerance should keep using GetUserByEmail.
FindUserByNormalizedEmail(email string) (*models.User, error)
GetUserByExternalID(externalID, authSource string) (*models.User, error)
GetUsersByIDs(userIDs []string) (map[string]*models.User, error)
ListUsersPaginated(params types.PaginationParams) ([]models.User, types.PaginationResult, error)
CountUsersByRole(role string) (int64, error)
GetUserStatsByUserID(userID string) (types.UserStatsCounts, error)
}
UserReader groups read-only user lookup operations.
type UserWriter ¶ added in v0.20.0
type UserWriter interface {
CreateUser(user *models.User) error
UpdateUser(user *models.User) error
DeleteUser(id string) error
UpsertExternalUser(
username, externalID, authSource, email, fullName string,
) (*models.User, error)
}
UserWriter groups user mutation operations.