services

package
v0.17.0 Latest Latest
Warning

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

Go to latest
Published: Mar 6, 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")
	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\"",
	)
)
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,
	countCache core.Cache[int64],
	countCacheTTL 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(clientID string) (*models.OAuthApplication, error)

func (*ClientService) ListClients

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

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(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
	IsAdminCreated              bool   // When true: Status=active; when false: Status=pending
}

type DeviceService

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

func NewDeviceService

func NewDeviceService(
	s *store.Store,
	cfg *config.Config,
	auditService *AuditService,
	m core.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,
	provider core.TokenProvider,
	auditService *AuditService,
	m core.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) 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) 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
	Status                      string // "active" or "inactive"
	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 core.AuthProvider,
	httpAPIProvider core.AuthProvider,
	authMode string,
	oauthAutoRegister bool,
	auditService *AuditService,
	userCache core.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.

type UserUpdateClientRequest added in v0.17.0

type UserUpdateClientRequest struct {
	ClientName         string
	Description        string
	Scopes             string // validated against allowedUserScopes
	RedirectURIs       []string
	ClientType         string
	EnableDeviceFlow   bool
	EnableAuthCodeFlow bool
}

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