services

package
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: Feb 28, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ClientTypeConfidential = "confidential"
	ClientTypePublic       = "public"
)

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

Functions

func FormatUserCode

func FormatUserCode(code string) string

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

Types

type AuditLogEntry

type AuditLogEntry struct {
	EventType     models.EventType
	Severity      models.EventSeverity
	ActorUserID   string
	ActorUsername 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 AuditService

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

AuditService handles audit logging operations

func NewAuditService

func NewAuditService(s *store.Store, enabled bool, 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 AuditLogEntry)

Log records an audit log entry asynchronously

func (*AuditService) LogSync

func (s *AuditService) LogSync(ctx context.Context, entry 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 *store.Store,
	cfg *config.Config,
	auditService *AuditService,
) *AuthorizationService

func (*AuthorizationService) CreateAuthorizationCode

func (s *AuthorizationService) CreateAuthorizationCode(
	ctx context.Context,
	applicationID int64,
	clientID, userID, redirectURI, scopes, codeChallenge, codeChallengeMethod, nonce string,
) (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(
	clientID, redirectURI, responseType, scope, 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 *store.Store, auditService *AuditService) *ClientService

func (*ClientService) CountActiveTokens

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

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

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(clientID string) (*models.OAuthApplication, error)

func (*ClientService) ListClients

func (s *ClientService) ListClients() ([]models.OAuthApplication, error)

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

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

func (*ClientService) VerifyClientSecret

func (s *ClientService) VerifyClientSecret(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 CreateClientRequest

type CreateClientRequest struct {
	ClientName                  string
	Description                 string
	UserID                      string
	Scopes                      string
	RedirectURIs                []string
	CreatedBy                   string
	ClientType                  string // ClientTypeConfidential or ClientTypePublic (default: ClientTypeConfidential)
	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
}

type DeviceService

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

func NewDeviceService

func NewDeviceService(
	s *store.Store,
	cfg *config.Config,
	auditService *AuditService,
	m metrics.Recorder,
) *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(
	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 TokenService

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

func NewTokenService

func NewTokenService(
	s *store.Store,
	cfg *config.Config,
	ds *DeviceService,
	localProvider *token.LocalTokenProvider,
	httpProvider *token.HTTPTokenProvider,
	providerMode string,
	auditService *AuditService,
	m metrics.Recorder,
) *TokenService

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

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

GetUserTokens returns all active tokens for a user

func (*TokenService) GetUserTokensWithClient

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

GetUserTokensWithClient returns all active 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) 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) 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 UpdateClientRequest

type UpdateClientRequest struct {
	ClientName                  string
	Description                 string
	Scopes                      string
	RedirectURIs                []string
	IsActive                    bool
	ClientType                  string
	EnableDeviceFlow            bool
	EnableAuthCodeFlow          bool
	EnableClientCredentialsFlow bool // Enable Client Credentials Grant (RFC 6749 §4.4); confidential clients only
}

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 *store.Store,
	localProvider *auth.LocalAuthProvider,
	httpAPIProvider *auth.HTTPAPIAuthProvider,
	authMode string,
	oauthAutoRegister bool,
	auditService *AuditService,
	userCache cache.Cache[models.User],
	userCacheTTL time.Duration,
) *UserService

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

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

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.

Jump to

Keyboard shortcuts

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