services

package
v0.28.0 Latest Latest
Warning

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

Go to latest
Published: Apr 23, 2026 License: MIT Imports: 32 Imported by: 0

Documentation

Index

Constants

View Source
const (
	AuthModeLocal   = "local"
	AuthModeHTTPAPI = "http_api"
)

Variables

View Source
var (
	ErrInvalidAuthCodeRequest  = errors.New("invalid_request")
	ErrUnauthorizedClient      = errors.New("unauthorized_client")
	ErrAccessDeniedConsent     = errors.New("access_denied")
	ErrUnsupportedResponseType = errors.New("unsupported_response_type")
	ErrInvalidAuthCodeScope    = errors.New("invalid_scope")
	ErrInvalidRedirectURI      = errors.New("invalid redirect_uri")
	ErrAuthCodeNotFound        = errors.New("authorization code not found")
	ErrAuthCodeExpired         = errors.New("authorization code expired")
	ErrAuthCodeAlreadyUsed     = errors.New("authorization code already used")
	ErrInvalidCodeVerifier     = errors.New("invalid code_verifier")
	ErrPKCERequired            = errors.New("pkce required for public clients")
	ErrAuthorizationNotFound   = errors.New("authorization not found")
)

Authorization Code Flow errors

View Source
var (
	ErrClientNotFound      = errors.New("client not found")
	ErrInvalidClientData   = errors.New("invalid client data")
	ErrClientNameRequired  = errors.New("client name is required")
	ErrRedirectURIRequired = errors.New(
		"at least one redirect URI is required when Authorization Code Flow is enabled",
	)
	ErrAtLeastOneGrantRequired              = errors.New("at least one grant type must be enabled")
	ErrClientCredentialsRequireConfidential = errors.New(
		"client credentials flow requires a confidential client",
	)
	ErrClientOwnershipRequired  = errors.New("you do not own this client")
	ErrCannotDeleteActiveClient = errors.New("cannot delete an active client")
	ErrInvalidScopeForUser      = errors.New(
		"scope not allowed for user-created clients",
	)
	ErrInvalidClientStatus = errors.New(
		"status must be \"active\", \"inactive\", or \"pending\"",
	)
	ErrInvalidTokenProfile = fmt.Errorf(
		"token profile must be %q, %q, or %q",
		models.TokenProfileShort,
		models.TokenProfileStandard,
		models.TokenProfileLong,
	)
)
View Source
var (
	ErrInvalidClient               = errors.New("invalid client_id")
	ErrClientInactive              = errors.New("client is inactive")
	ErrDeviceFlowNotEnabled        = errors.New("device flow not enabled for this client")
	ErrDeviceCodeNotFound          = errors.New("device code not found")
	ErrDeviceCodeExpired           = errors.New("device code expired")
	ErrUserCodeNotFound            = errors.New("user code not found")
	ErrDeviceCodeAlreadyAuthorized = errors.New("device code already authorized")
)
View Source
var (
	ErrAuthorizationPending = errors.New("authorization_pending")
	ErrSlowDown             = errors.New("slow_down")
	ErrAccessDenied         = errors.New("access_denied")
	ErrExpiredToken         = errors.New("expired_token")
	ErrTokenCannotDisable   = errors.New(
		"token cannot be disabled: only active tokens can be disabled",
	)
	ErrTokenCannotEnable = errors.New(
		"token cannot be enabled: only disabled tokens can be re-enabled",
	)

	// Client Credentials Flow errors (RFC 6749 §4.4)
	ErrInvalidClientCredentials = errors.New("invalid client credentials")
	ErrClientNotConfidential    = errors.New(
		"client_credentials grant requires a confidential client",
	)
	ErrClientCredentialsFlowDisabled = errors.New(
		"client_credentials flow is not enabled for this client",
	)
)
View Source
var (
	ErrInvalidCredentials        = errors.New("invalid username or password")
	ErrUserNotFound              = errors.New("user not found")
	ErrAuthProviderFailed        = errors.New("authentication provider failed")
	ErrUserSyncFailed            = errors.New("failed to sync user from external provider")
	ErrUsernameConflict          = errors.New("username already exists")
	ErrOAuthAutoRegisterDisabled = errors.New("OAuth auto-registration is disabled")
	ErrOAuthEmailNotVerified     = errors.New(
		"OAuth email not verified and auto-registration is disabled",
	)
	ErrCannotDeleteSelf        = errors.New("cannot delete your own account")
	ErrCannotChangeOwnRole     = errors.New("cannot change your own role")
	ErrCannotRemoveLastAdmin   = errors.New("cannot remove the last admin user")
	ErrPasswordResetNotAllowed = errors.New("password reset only available for local auth users")
	ErrInvalidRole             = errors.New("role must be admin or user")
	ErrEmailRequired           = errors.New("email is required")
	ErrEmailConflict           = errors.New("email already in use by another user")
	ErrAccountDisabled         = errors.New("account is disabled")
	ErrUsernameRequired        = errors.New("username is required")
	ErrCannotChangeOwnStatus   = errors.New("cannot change your own active status")
	ErrUserAlreadyActive       = errors.New("user is already active")
	ErrUserAlreadyDisabled     = errors.New("user is already disabled")
	ErrOAuthConnectionNotFound = errors.New("OAuth connection not found")
	ErrAmbiguousEmail          = errors.New(
		"multiple local users share this email; please deduplicate before signing in via OAuth",
	)
)

Functions

func FormatUserCode

func FormatUserCode(code string) string

FormatUserCode formats a user code for display (e.g., "ABCDEFGH" -> "ABCD-EFGH")

func SetAuditMetricsRegisterer added in v0.25.0

func SetAuditMetricsRegisterer(registerer prometheus.Registerer)

SetAuditMetricsRegisterer configures the Prometheus registerer used by the audit service. If the dropped-events counter was created before metrics were configured, setting a non-nil registerer will register the existing counter, ensuring late initialization (e.g. in tests) is not silently lost.

Types

type AuditService

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

AuditService handles audit logging operations

func NewAuditService

func NewAuditService(s core.Store, bufferSize int) *AuditService

NewAuditService creates a new audit service

func (*AuditService) CleanupOldLogs

func (s *AuditService) CleanupOldLogs(retention time.Duration) (int64, error)

CleanupOldLogs deletes audit logs older than the retention period

func (*AuditService) GetAuditLogStats

func (s *AuditService) GetAuditLogStats(startTime, endTime time.Time) (store.AuditLogStats, error)

GetAuditLogStats returns statistics about audit logs

func (*AuditService) GetAuditLogs

GetAuditLogs retrieves audit logs with pagination and filtering

func (*AuditService) Log

func (s *AuditService) Log(ctx context.Context, entry core.AuditLogEntry)

Log records an audit log entry asynchronously. Events submitted after Shutdown has been called are dropped. The RWMutex ensures all in-flight sends complete before Shutdown closes logChan, eliminating the send-on-closed-channel race.

func (*AuditService) LogSync

func (s *AuditService) LogSync(ctx context.Context, entry core.AuditLogEntry) error

LogSync records an audit log entry synchronously (for critical events)

func (*AuditService) Shutdown

func (s *AuditService) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the audit service

type AuthorizationRequest

type AuthorizationRequest struct {
	Client              *models.OAuthApplication
	RedirectURI         string
	Scopes              string
	State               string
	CodeChallenge       string
	CodeChallengeMethod string
	Nonce               string
}

AuthorizationRequest holds validated parameters for an authorization request

type AuthorizationService

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

AuthorizationService manages the OAuth 2.0 Authorization Code Flow (RFC 6749)

func NewAuthorizationService

func NewAuthorizationService(
	s core.Store,
	cfg *config.Config,
	auditService core.AuditLogger,
	tokenService *TokenService,
	clientService *ClientService,
) *AuthorizationService

func (*AuthorizationService) CreateAuthorizationCode

func (s *AuthorizationService) CreateAuthorizationCode(
	ctx context.Context,
	params CreateAuthorizationCodeParams,
) (plainCode string, record *models.AuthorizationCode, err error)

CreateAuthorizationCode generates a one-time authorization code and saves it to the database. Returns the plaintext code (to be sent in the redirect) and the stored record.

func (*AuthorizationService) ExchangeCode

func (s *AuthorizationService) ExchangeCode(
	ctx context.Context,
	plainCode, clientID, redirectURI, clientSecret, codeVerifier string,
) (*models.AuthorizationCode, error)

ExchangeCode validates a plaintext authorization code and marks it as used. The caller (TokenHandler) is responsible for issuing tokens after this returns successfully.

func (*AuthorizationService) GetUserAuthorization

func (s *AuthorizationService) GetUserAuthorization(
	userID string,
	applicationID int64,
) (*models.UserAuthorization, error)

GetUserAuthorization returns the active consent record for a (user, application) pair. Returns nil, nil when no consent exists (not an error condition).

func (*AuthorizationService) ListClientAuthorizations

func (s *AuthorizationService) ListClientAuthorizations(
	ctx context.Context,
	clientID string,
) ([]UserAuthorizationWithUser, error)

ListClientAuthorizations returns all active consent grants for a given client, with user details. Intended for the admin overview page.

func (*AuthorizationService) ListUserAuthorizations

func (s *AuthorizationService) ListUserAuthorizations(
	ctx context.Context,
	userID string,
) ([]UserAuthorizationWithClient, error)

ListUserAuthorizations returns all active authorizations for a user with client display names.

func (*AuthorizationService) RevokeAllApplicationTokens

func (s *AuthorizationService) RevokeAllApplicationTokens(
	ctx context.Context,
	clientID, actorUserID string,
) (int64, error)

RevokeAllApplicationTokens revokes all active tokens and consent records for an application. This is an admin operation that forces all users to re-authenticate.

func (*AuthorizationService) RevokeUserAuthorization

func (s *AuthorizationService) RevokeUserAuthorization(
	ctx context.Context,
	authUUID, userID string,
) error

RevokeUserAuthorization revokes a user's consent for an application. It also revokes all active tokens that were issued under this authorization.

func (*AuthorizationService) SaveUserAuthorization

func (s *AuthorizationService) SaveUserAuthorization(
	ctx context.Context,
	userID string,
	applicationID int64,
	clientID, scopes string,
) (*models.UserAuthorization, error)

SaveUserAuthorization creates or updates the consent record for a user+application pair.

func (*AuthorizationService) ValidateAuthorizationRequest

func (s *AuthorizationService) ValidateAuthorizationRequest(
	ctx context.Context,
	clientID, redirectURI, responseType, scope, codeChallenge, codeChallengeMethod, nonce string,
) (*AuthorizationRequest, error)

ValidateAuthorizationRequest validates all parameters of an incoming authorization request. Returns the parsed AuthorizationRequest on success.

type ClientResponse

type ClientResponse struct {
	*models.OAuthApplication
	ClientSecretPlain string // Only populated on creation
}

type ClientService

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

func NewClientService

func NewClientService(
	s core.Store,
	auditService core.AuditLogger,
	countCache core.Cache[int64],
	countCacheTTL time.Duration,
	clientCache core.Cache[models.OAuthApplication],
	clientCacheTTL time.Duration,
) *ClientService

func (*ClientService) ApproveClient added in v0.17.0

func (s *ClientService) ApproveClient(
	ctx context.Context,
	clientID, adminUserID string,
) error

ApproveClient sets a client's status to active and enables it for OAuth flows.

func (*ClientService) CountActiveTokens

func (s *ClientService) CountActiveTokens(clientID string) (int64, error)

CountActiveTokens returns the number of active tokens for a given client.

func (*ClientService) CountPendingClients added in v0.17.0

func (s *ClientService) CountPendingClients(ctx context.Context) (int64, error)

CountPendingClients returns the number of clients awaiting admin approval. Results are cached for countCacheTTL and invalidated on approve/reject/create/delete.

func (*ClientService) CreateClient

func (s *ClientService) CreateClient(
	ctx context.Context,
	req CreateClientRequest,
) (*ClientResponse, error)

func (*ClientService) DeleteClient

func (s *ClientService) DeleteClient(ctx context.Context, clientID, actorUserID string) error

func (*ClientService) GetClient

func (s *ClientService) GetClient(
	ctx context.Context,
	clientID string,
) (*models.OAuthApplication, error)

GetClient returns a cached OAuth client by client_id. The returned copy has ClientSecret cleared for defense-in-depth. Use GetClientWithSecret for flows that need secret verification. On cache backend errors (e.g. Redis unavailable), falls back to direct DB lookup so that valid OAuth flows are not rejected due to cache infrastructure issues.

func (*ClientService) GetClientWithSecret added in v0.25.0

func (s *ClientService) GetClientWithSecret(
	ctx context.Context,
	clientID string,
) (*models.OAuthApplication, error)

GetClientWithSecret returns an OAuth client by client_id without caching. Use this for flows that need to verify the client secret (e.g., confidential client auth).

func (*ClientService) ListClientsByUser added in v0.17.0

func (s *ClientService) ListClientsByUser(
	userID string,
	params store.PaginationParams,
) ([]models.OAuthApplication, store.PaginationResult, error)

ListClientsByUser returns paginated OAuth clients owned by the given user.

func (*ClientService) ListClientsPaginated

func (s *ClientService) ListClientsPaginated(
	params store.PaginationParams,
) ([]models.OAuthApplication, store.PaginationResult, error)

ListClientsPaginated returns paginated OAuth clients with search support

func (*ClientService) ListClientsPaginatedWithCreator

func (s *ClientService) ListClientsPaginatedWithCreator(
	params store.PaginationParams,
) ([]ClientWithCreator, store.PaginationResult, error)

ListClientsPaginatedWithCreator returns paginated OAuth clients with creator information This method prevents N+1 queries by batch loading users via GetUsersByIDs

func (*ClientService) RegenerateSecret

func (s *ClientService) RegenerateSecret(
	ctx context.Context,
	clientID, actorUserID string,
) (string, error)

func (*ClientService) RejectClient added in v0.17.0

func (s *ClientService) RejectClient(
	ctx context.Context,
	clientID, adminUserID string,
) error

RejectClient sets a client's status to inactive and disables it for OAuth flows.

func (*ClientService) UpdateClient

func (s *ClientService) UpdateClient(
	ctx context.Context,
	clientID, actorUserID string,
	req UpdateClientRequest,
) error

func (*ClientService) UserDeleteClient added in v0.17.0

func (s *ClientService) UserDeleteClient(
	ctx context.Context,
	clientID, actorUserID string,
) error

UserDeleteClient deletes a client owned by actorUserID. Deletion is blocked for clients with Status=active (must be rejected first).

func (*ClientService) UserUpdateClient added in v0.17.0

func (s *ClientService) UserUpdateClient(
	ctx context.Context,
	clientID, actorUserID string,
	req UserUpdateClientRequest,
) error

UserUpdateClient updates a client owned by actorUserID with the restricted field set. Ownership is enforced; the approval Status is never changed by this method.

func (*ClientService) VerifyClientSecret

func (s *ClientService) VerifyClientSecret(
	ctx context.Context,
	clientID, clientSecret string,
) error

type ClientWithCreator

type ClientWithCreator struct {
	models.OAuthApplication
	CreatorUsername string // Empty string if user not found or deleted
}

ClientWithCreator combines OAuth client and creator user information for display

type CreateAuthorizationCodeParams added in v0.24.0

type CreateAuthorizationCodeParams struct {
	ApplicationID       int64
	ClientID            string
	UserID              string
	RedirectURI         string
	Scopes              string
	CodeChallenge       string
	CodeChallengeMethod string
	Nonce               string
}

CreateAuthorizationCodeParams bundles the inputs for authorization code creation.

type CreateClientRequest

type CreateClientRequest struct {
	ClientName                  string
	Description                 string
	UserID                      string
	Scopes                      string
	RedirectURIs                []string
	CreatedBy                   string
	ClientType                  core.ClientType
	EnableDeviceFlow            bool   // Enable Device Authorization Grant (RFC 8628)
	EnableAuthCodeFlow          bool   // Enable Authorization Code Flow (RFC 6749)
	EnableClientCredentialsFlow bool   // Enable Client Credentials Grant (RFC 6749 §4.4); confidential clients only
	IsAdminCreated              bool   // When true: Status=active; when false: Status=pending
	TokenProfile                string // "short" / "standard" / "long"; empty = standard
}

type CreateUserRequest added in v0.26.0

type CreateUserRequest struct {
	Username string
	Email    string
	FullName string
	Role     string
	Password string // optional — if empty, generate random
}

CreateUserRequest carries the fields for admin user creation.

type DashboardService added in v0.24.0

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

func NewDashboardService added in v0.24.0

func NewDashboardService(
	s core.Store,
	auditService core.AuditLogger,
) *DashboardService

func (*DashboardService) GetDashboardStats added in v0.24.0

func (s *DashboardService) GetDashboardStats(ctx context.Context) *DashboardStats

GetDashboardStats returns aggregated stats in a single raw SQL query for counts plus one query for recent audit activity.

type DashboardStats added in v0.24.0

type DashboardStats struct {
	store.DashboardCounts
	RegularUsers   int64
	RecentActivity []models.AuditLog
}

DashboardStats holds aggregated metrics for the admin dashboard.

type DeviceService

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

func NewDeviceService

func NewDeviceService(
	s core.Store,
	cfg *config.Config,
	auditService core.AuditLogger,
	m core.Recorder,
	clientService *ClientService,
) *DeviceService

func (*DeviceService) AuthorizeDeviceCode

func (s *DeviceService) AuthorizeDeviceCode(
	ctx context.Context,
	userCode, userID, username string,
) error

AuthorizeDeviceCode marks a device code as authorized by a user

func (*DeviceService) GenerateDeviceCode

func (s *DeviceService) GenerateDeviceCode(
	ctx context.Context,
	clientID, scope string,
) (*models.DeviceCode, error)

GenerateDeviceCode creates a new device code request

func (*DeviceService) GetClientByUserCode

func (s *DeviceService) GetClientByUserCode(
	ctx context.Context,
	userCode string,
) (*models.OAuthApplication, *models.DeviceCode, error)

GetClientByUserCode retrieves the OAuth client and device code associated with a user code

func (*DeviceService) GetDeviceCode

func (s *DeviceService) GetDeviceCode(deviceCode string) (*models.DeviceCode, error)

GetDeviceCode retrieves a device code by its code

func (*DeviceService) GetDeviceCodeByUserCode

func (s *DeviceService) GetDeviceCodeByUserCode(userCode string) (*models.DeviceCode, error)

GetDeviceCodeByUserCode retrieves a device code by user code

type NoopAuditService added in v0.25.0

type NoopAuditService struct{}

NoopAuditService implements AuditLogger with no-op behavior. Used when audit logging is disabled.

func NewNoopAuditService added in v0.25.0

func NewNoopAuditService() *NoopAuditService

NewNoopAuditService returns the shared no-op audit service instance.

func (*NoopAuditService) CleanupOldLogs added in v0.25.0

func (n *NoopAuditService) CleanupOldLogs(_ time.Duration) (int64, error)

CleanupOldLogs is a no-op.

func (*NoopAuditService) GetAuditLogStats added in v0.25.0

func (n *NoopAuditService) GetAuditLogStats(_, _ time.Time) (types.AuditLogStats, error)

GetAuditLogStats returns empty stats.

func (*NoopAuditService) GetAuditLogs added in v0.25.0

GetAuditLogs returns empty results with pagination metadata reflecting the requested params.

func (*NoopAuditService) Log added in v0.25.0

Log is a no-op.

func (*NoopAuditService) LogSync added in v0.25.0

LogSync is a no-op.

func (*NoopAuditService) Shutdown added in v0.25.0

func (n *NoopAuditService) Shutdown(_ context.Context) error

Shutdown is a no-op.

type TokenService

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

func NewTokenService

func NewTokenService(
	s core.Store,
	cfg *config.Config,
	ds *DeviceService,
	provider core.TokenProvider,
	auditService core.AuditLogger,
	m core.Recorder,
	tokenCache core.Cache[models.AccessToken],
	clientService *ClientService,
) *TokenService

func (*TokenService) AuthenticateClient added in v0.20.0

func (s *TokenService) AuthenticateClient(
	ctx context.Context,
	clientID, clientSecret string,
) error

AuthenticateClient verifies client credentials (client_id + client_secret). Returns nil on success, or an error if the client is not found, inactive, or the secret is invalid.

func (*TokenService) DisableToken

func (s *TokenService) DisableToken(ctx context.Context, tokenID, actorUserID string) error

DisableToken disables a token (can be re-enabled)

func (*TokenService) EnableToken

func (s *TokenService) EnableToken(ctx context.Context, tokenID, actorUserID string) error

EnableToken re-enables a disabled token

func (*TokenService) ExchangeAuthorizationCode

func (s *TokenService) ExchangeAuthorizationCode(
	ctx context.Context,
	authCode *models.AuthorizationCode,
	authorizationID *uint,
) (*models.AccessToken, *models.AccessToken, string, error)

ExchangeAuthorizationCode issues an access token, a refresh token, and (when the openid scope was granted) an OIDC ID Token for an already-validated authorization code. The AuthorizationCode record must have been validated and marked as used by AuthorizationService.ExchangeCode before calling this method. Returns: accessToken, refreshToken, idToken (empty string when openid not requested), error.

func (*TokenService) ExchangeDeviceCode

func (s *TokenService) ExchangeDeviceCode(
	ctx context.Context,
	deviceCode, clientID string,
) (*models.AccessToken, *models.AccessToken, error)

ExchangeDeviceCode exchanges an authorized device code for access and refresh tokens

func (*TokenService) GetActiveRefreshTokens

func (s *TokenService) GetActiveRefreshTokens(userID string) ([]models.AccessToken, error)

GetActiveRefreshTokens gets all active refresh tokens for a user

func (*TokenService) GetUserByID added in v0.20.0

func (s *TokenService) GetUserByID(userID string) (*models.User, error)

GetUserByID returns a user by their ID.

func (*TokenService) GetUserTokens

func (s *TokenService) GetUserTokens(userID string) ([]models.AccessToken, error)

GetUserTokens returns all tokens for a user

func (*TokenService) GetUserTokensWithClient

func (s *TokenService) GetUserTokensWithClient(userID string) ([]TokenWithClient, error)

GetUserTokensWithClient returns all tokens for a user with client information

func (*TokenService) GetUserTokensWithClientPaginated

func (s *TokenService) GetUserTokensWithClientPaginated(
	userID string,
	params store.PaginationParams,
) ([]TokenWithClient, store.PaginationResult, error)

GetUserTokensWithClientPaginated returns paginated tokens for a user with client information

func (*TokenService) IntrospectToken added in v0.20.0

func (s *TokenService) IntrospectToken(
	ctx context.Context,
	tokenString, callerClientID string,
) (*models.AccessToken, bool)

IntrospectToken looks up a token by its raw string and returns the database record along with its active status. Unlike ValidateToken, this method does NOT require JWT signature validation — it is designed for RFC 7662 introspection where the authorization server is the token issuer and can rely on its own database state. Returns (token, true) for active tokens, (token, false) for inactive/expired tokens, and (nil, false) if the token does not exist.

This method intentionally bypasses the token cache and always queries the database, because introspection serves as the authoritative ground-truth endpoint for resource servers (RFC 7662 §2.2) and must reflect real-time revocation status.

func (*TokenService) InvalidateTokenCacheByHashes added in v0.24.0

func (s *TokenService) InvalidateTokenCacheByHashes(ctx context.Context, hashes []string)

InvalidateTokenCacheByHashes removes multiple tokens from cache by their hashes. Exported for use by other services (e.g., AuthorizationService) during bulk revocation.

func (*TokenService) IsTokenOwnedByUser added in v0.16.0

func (s *TokenService) IsTokenOwnedByUser(tokenID, userID string) (bool, error)

IsTokenOwnedByUser returns true if the token with the given ID belongs to the given user. A missing token is treated the same as an unowned token: returns (false, nil).

func (*TokenService) IssueClientCredentialsToken added in v0.12.0

func (s *TokenService) IssueClientCredentialsToken(
	ctx context.Context,
	clientID, clientSecret, requestedScopes string,
) (*models.AccessToken, error)

IssueClientCredentialsToken issues an access token for the client_credentials grant (RFC 6749 §4.4). Only confidential clients with EnableClientCredentialsFlow=true may use this flow. No refresh token is issued (per RFC 6749 §4.4.3).

The resulting token carries a synthetic machine identity in UserID: "client:<clientID>". This distinguishes M2M tokens from user-delegated tokens in all downstream lookups.

func (*TokenService) ListAllTokensPaginated added in v0.24.0

func (s *TokenService) ListAllTokensPaginated(
	params store.PaginationParams,
) ([]TokenWithUser, store.PaginationResult, error)

ListAllTokensPaginated returns paginated tokens across all users with client and user info.

func (*TokenService) RefreshAccessToken

func (s *TokenService) RefreshAccessToken(
	ctx context.Context,
	refreshTokenString, clientID, requestedScopes string,
) (*models.AccessToken, *models.AccessToken, error)

RefreshAccessToken generates new access token (and optionally new refresh token in rotation mode)

func (*TokenService) RevokeAllUserTokens

func (s *TokenService) RevokeAllUserTokens(userID string) error

RevokeAllUserTokens revokes all tokens for a user

func (*TokenService) RevokeToken

func (s *TokenService) RevokeToken(tokenString string) error

RevokeToken revokes a token by its JWT string

func (*TokenService) RevokeTokenByID

func (s *TokenService) RevokeTokenByID(ctx context.Context, tokenID, actorUserID string) error

RevokeTokenByID revokes a token by its ID

func (*TokenService) RevokeTokenByStatus

func (s *TokenService) RevokeTokenByStatus(tokenID string) error

RevokeTokenByStatus permanently revokes a token (uses status update, not deletion)

func (*TokenService) ValidateToken

func (s *TokenService) ValidateToken(
	ctx context.Context,
	tokenString string,
) (*token.ValidationResult, error)

ValidateToken validates a JWT token using the configured provider

type TokenWithClient

type TokenWithClient struct {
	models.AccessToken
	ClientName string
}

TokenWithClient combines token and client information for display

type TokenWithUser added in v0.24.0

type TokenWithUser struct {
	TokenWithClient
	Username string
}

TokenWithUser extends TokenWithClient with the token owner's username.

type UpdateClientRequest

type UpdateClientRequest struct {
	ClientName                  string
	Description                 string
	Scopes                      string
	RedirectURIs                []string
	Status                      string // "active" or "inactive"
	ClientType                  core.ClientType
	EnableDeviceFlow            bool
	EnableAuthCodeFlow          bool
	EnableClientCredentialsFlow bool   // Enable Client Credentials Grant (RFC 6749 §4.4); confidential clients only
	TokenProfile                string // "short" / "standard" / "long"; empty = standard
}

type UpdateUserProfileRequest added in v0.24.0

type UpdateUserProfileRequest struct {
	FullName string
	Email    string
	Role     string
}

UpdateUserProfileRequest carries the fields an admin can edit.

type UserAuthorizationWithClient

type UserAuthorizationWithClient struct {
	models.UserAuthorization
	ClientName string
}

UserAuthorizationWithClient combines a UserAuthorization with its client's display name

type UserAuthorizationWithUser

type UserAuthorizationWithUser struct {
	models.UserAuthorization
	Username string
	Email    string
}

UserAuthorizationWithUser combines a UserAuthorization with the authorizing user's details

type UserService

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

func NewUserService

func NewUserService(
	s core.Store,
	localProvider core.AuthProvider,
	httpAPIProvider core.AuthProvider,
	authMode string,
	oauthAutoRegister bool,
	auditService core.AuditLogger,
	userCache core.Cache[models.User],
	userCacheTTL time.Duration,
) *UserService

func (*UserService) AdminGetUserByID added in v0.24.0

func (s *UserService) AdminGetUserByID(userID string) (*models.User, error)

AdminGetUserByID fetches a user directly from the store (no cache) so that auth_source and other mutable fields are always fresh.

func (*UserService) Authenticate

func (s *UserService) Authenticate(
	ctx context.Context,
	username, password string,
) (*models.User, error)

func (*UserService) AuthenticateWithOAuth

func (s *UserService) AuthenticateWithOAuth(
	ctx context.Context,
	provider string,
	oauthUserInfo *auth.OAuthUserInfo,
	token *oauth2.Token,
) (*models.User, error)

AuthenticateWithOAuth authenticates a user via OAuth and creates/updates user account

func (*UserService) CountUsersByRole added in v0.24.0

func (s *UserService) CountUsersByRole(role string) (int64, error)

CountUsersByRole returns the number of active users with the given role. Disabled users are excluded so that last-admin guards work correctly.

func (*UserService) CreateUserAdmin added in v0.26.0

func (s *UserService) CreateUserAdmin(
	ctx context.Context,
	req CreateUserRequest,
	actorUserID string,
) (*models.User, string, error)

CreateUserAdmin creates a new local-auth user. Returns the user and the plaintext password (to show once).

func (*UserService) DeleteUserAdmin added in v0.24.0

func (s *UserService) DeleteUserAdmin(
	ctx context.Context,
	userID, actorUserID string,
) error

DeleteUserAdmin deletes a user and cleans up related data. Callers must revoke tokens via TokenService before calling this method to ensure token cache invalidation. Guards are re-checked for safety.

func (*UserService) DeleteUserOAuthConnection added in v0.26.0

func (s *UserService) DeleteUserOAuthConnection(
	ctx context.Context,
	userID, connectionID, actorUserID string,
) error

DeleteUserOAuthConnection deletes a specific OAuth connection for a user.

func (*UserService) GetUserByID

func (s *UserService) GetUserByID(ctx context.Context, id string) (*models.User, error)

func (*UserService) GetUserOAuthConnections added in v0.26.0

func (s *UserService) GetUserOAuthConnections(userID string) ([]models.OAuthConnection, error)

GetUserOAuthConnections returns all OAuth connections for a user.

func (*UserService) GetUserStats added in v0.24.0

func (s *UserService) GetUserStats(userID string) (UserStats, error)

GetUserStats returns aggregate counts for a user in a single database query.

func (*UserService) InvalidateUserCache added in v0.14.0

func (s *UserService) InvalidateUserCache(id string)

InvalidateUserCache removes the cached user entry for the given user ID. Call this after any mutation to user data to ensure stale data is not served.

func (*UserService) ListUsersPaginated added in v0.24.0

func (s *UserService) ListUsersPaginated(
	params storeTypes.PaginationParams,
) ([]models.User, storeTypes.PaginationResult, error)

ListUsersPaginated returns a paginated list of users.

func (*UserService) ResetUserPassword added in v0.24.0

func (s *UserService) ResetUserPassword(
	ctx context.Context,
	userID, actorUserID string,
) (string, error)

ResetUserPassword generates a new random password for a local-auth user. Returns the plaintext password (to be shown once) or an error.

func (*UserService) SetUserActiveStatus added in v0.26.0

func (s *UserService) SetUserActiveStatus(
	ctx context.Context,
	userID, actorUserID string,
	isActive bool,
) error

SetUserActiveStatus enables or disables a user account. Validation guards (self-change, already in target state, last-admin) are re-checked here as defense in depth so direct callers cannot bypass invariants. Handlers should still call ValidateSetUserActiveStatus first when they need to gate pre-change side effects (e.g. token revocation).

func (*UserService) UpdateUserProfile added in v0.24.0

func (s *UserService) UpdateUserProfile(
	ctx context.Context,
	userID, actorUserID string,
	req UpdateUserProfileRequest,
) error

UpdateUserProfile updates a user's profile fields. Role changes are blocked when actorUserID == userID to prevent admins from demoting themselves.

func (*UserService) ValidateDeleteUser added in v0.24.0

func (s *UserService) ValidateDeleteUser(userID, actorUserID string) error

ValidateDeleteUser checks whether the user can be deleted without performing the deletion. The caller can use this to run pre-deletion side effects (e.g. token revocation) before committing the delete.

func (*UserService) ValidateSetUserActiveStatus added in v0.26.0

func (s *UserService) ValidateSetUserActiveStatus(
	userID, actorUserID string,
	isActive bool,
) error

ValidateSetUserActiveStatus checks whether the active status change is allowed without performing it. Callers can use this to run pre-change side effects (e.g. token revocation) before committing the update.

type UserStats added in v0.24.0

type UserStats struct {
	ActiveTokenCount     int64
	OAuthConnectionCount int64
	AuthorizationCount   int64
}

UserStats contains aggregate counts for a user's related entities.

type UserUpdateClientRequest added in v0.17.0

type UserUpdateClientRequest struct {
	ClientName                  string
	Description                 string
	Scopes                      string // validated against allowedUserScopes
	RedirectURIs                []string
	ClientType                  core.ClientType
	EnableDeviceFlow            bool
	EnableAuthCodeFlow          bool
	EnableClientCredentialsFlow bool // Enable Client Credentials Grant (RFC 6749 §4.4); confidential clients only
}

UserUpdateClientRequest contains the restricted set of fields a non-admin user may update on their own client.

Jump to

Keyboard shortcuts

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