auth

package
v1.3.1 Latest Latest
Warning

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

Go to latest
Published: May 11, 2026 License: AGPL-3.0 Imports: 55 Imported by: 0

README

Auth Package

This package provides OAuth 2.0 authentication for Lesser, enabling secure access to local user accounts.

Overview

The auth package implements:

  • OAuth 2.0 with PKCE (Proof Key for Code Exchange) for enhanced security
  • JWT (JSON Web Token) based access tokens
  • Refresh token support for long-lived sessions
  • Middleware for protecting endpoints
  • Scope-based authorization

Lesser is passwordless by design: users authenticate via WebAuthn/passkeys and/or wallet signatures. OAuth tokens are still presented as Authorization: Bearer …, but Lesser does not support password-based login or password grant flows.

Components

OAuthService

The main OAuth service that handles:

  • Client validation
  • Authorization code generation
  • PKCE verification
  • Token generation and validation
  • JWT signing and verification
Middleware

Authentication middleware that:

  • Extracts and validates JWT tokens from requests
  • Provides user context to handlers
  • Enforces scope requirements
  • Validates user ownership of resources

Usage

Basic Setup
import "github.com/equaltoai/lesser/pkg/auth"

// Create OAuth service
oauthSvc := auth.NewOAuthService("your-jwt-secret")

// Register a client
oauthSvc.RegisterClient(&auth.Client{
    ID:          "my-app",
    Secret:      "client-secret",
    Name:        "My Application",
    RedirectURI: "https://myapp.com/callback",
})
Protecting Endpoints
// Create middleware
authMiddleware := auth.NewMiddleware()

// In your Lambda handler
func handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    // Verify authentication
    claims, err := authMiddleware.RequireAuth(ctx, request)
    if err != nil {
        return common.Unauthorized(err), nil
    }
    
    // Check user matches resource owner
    if err := authMiddleware.RequireUser(claims, username); err != nil {
        return common.Forbidden(err), nil
    }
    
    // Check required scope
    if err := authMiddleware.RequireScope(claims, auth.ScopeWrite); err != nil {
        return common.Forbidden(err), nil
    }
    
    // User is authenticated and authorized
    // ... handle request ...
}
OAuth Flow
  1. Authorization Request

    GET /oauth/authorize?
      response_type=code&
      client_id=my-app&
      redirect_uri=https://myapp.com/callback&
      code_challenge=CHALLENGE&
      code_challenge_method=S256&
      scope=read write&
      state=RANDOM_STATE
    
  2. User Authorization

    • Users authenticate via the passwordless auth UI (WebAuthn/passkeys and/or wallet challenge + signature).
    • See docs/architecture/auth/PASSWORDLESS_OAUTH.md for the end-to-end flow and endpoints.
  3. Authorization Code Response

    HTTP/1.1 302 Found
    Location: https://myapp.com/callback?code=AUTH_CODE&state=RANDOM_STATE
    
  4. Token Exchange

    POST /oauth/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code&
    code=AUTH_CODE&
    client_id=my-app&
    client_secret=client-secret&
    code_verifier=VERIFIER&
    redirect_uri=https://myapp.com/callback
    
  5. Token Response

    {
      "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
      "token_type": "Bearer",
      "expires_in": 3600,
      "refresh_token": "REFRESH_TOKEN",
      "scope": "read write"
    }
    

Security Considerations

PKCE Required

All authorization requests must include PKCE parameters:

  • code_challenge: Base64URL encoded SHA256 hash of the code verifier
  • code_challenge_method: Must be "S256"
  • code_verifier: Random string used in token exchange
Token Expiration
  • Access tokens expire after 1 hour
  • Refresh tokens expire after 30 days
  • Authorization codes expire after 10 minutes
JWT Claims

Access tokens include:

{
  "sub": "username",
  "username": "username",
  "client_id": "my-app",
  "scopes": ["read", "write"],
  "iat": 1234567890,
  "exp": 1234571490
}

Scopes

  • read: Read access to user data
  • write: Write access to create/update activities
  • follow: Canonical relationship-management scope
  • push: Canonical push-subscription scope

Public OAuth metadata and app registration advertise the canonical scope catalog read, write, follow, push. Compatibility aliases in the read:* and write:* families remain accepted for legacy clients, while admin is internal-only and is not requestable on public OAuth surfaces. See docs/specs/oauth-scope-model.md for the full contract.

Storage

OAuth data is stored in DynamoDB:

  • Authorization codes: PK=AUTHCODE#{code}, SK=CODE
  • Refresh tokens: PK=REFRESHTOKEN#{token}, SK=TOKEN

Both include automatic expiration handling.

Configuration

Environment variables:

  • JWT_SECRET: Secret key for signing JWT tokens (required in production)

Testing

# Run OAuth package tests
go test ./pkg/auth/...

# Run OAuth storage tests
GO_ENV=test go test ./pkg/storage/dynamodb/... -run OAuth

Future Enhancements

  1. User Authentication

    • Expand passwordless login UI (passkeys/wallets)
    • Account recovery UX (recovery codes / social recovery)
    • Multi-factor authentication (passwordless-friendly)
  2. Client Management

    • Dynamic client registration
    • Client credentials from database
    • Multiple redirect URIs per client
  3. Advanced OAuth Features

    • OpenID Connect support
    • Token introspection endpoint
    • Token revocation endpoint
    • Client credentials grant type
  4. Security Enhancements

    • Rate limiting
    • Brute force protection
    • Token rotation on refresh
    • Encrypted token storage

Documentation

Overview

Package auth provides common password validation and security utilities for authentication.

Package auth provides universal authentication context and utilities for consolidating authentication patterns across the Lesser application.

Package auth provides centralized authentication extraction functions This consolidates the auth extraction patterns found across 80+ handler files

Index

Constants

View Source
const (
	DelegatedAgentRuntimeClientID     = "lesser-agent-delegation"
	SelfSovereignAgentRuntimeClientID = "lesser-agent-self-sovereign"
)

Dedicated internal runtime client IDs. These are not part of the canonical public MCP client contract.

View Source
const (
	ScopeRead  = "read"
	ScopeWrite = "write"
	ScopeAdmin = "admin"
)

Scopes define the permissions that can be granted

View Source
const (
	GrantTypeAuthorizationCode = "authorization_code"
	GrantTypeRefreshToken      = "refresh_token"
	GrantTypeClientCredentials = "client_credentials"
)

Grant types

View Source
const (
	// Access token duration: 1 hour is reasonable for external client applications
	// Balances security with usability - clients should use refresh tokens for longer sessions
	AccessTokenDuration = 1 * time.Hour
	// Refresh tokens should be rotated regularly
	RefreshTokenDuration = 7 * 24 * time.Hour // 7 days
	// Authorization codes must be very short-lived
	AuthCodeDuration = 5 * time.Minute
	// Token family tracking for refresh token rotation
	RefreshTokenFamilyExpiry = 30 * 24 * time.Hour // 30 days for family tracking
)

Token expiration times

View Source
const (
	ClientClassWeb   = "web"
	ClientClassCLI   = "cli"
	ClientClassAgent = "agent"
)

Client classes identify the client category that minted an access token. These are used for server-side policy decisions (e.g., automation safety rails) without relying on spoofable headers.

View Source
const (
	// Login attempts before lockout
	MaxLoginAttempts = 5
	MaxIPAttempts    = 20

	// Lockout durations
	AccountLockoutDuration = 30 * time.Minute
	IPLockoutDuration      = 1 * time.Hour

	// Time windows for counting attempts
	AttemptWindow = 15 * time.Minute

	// Rate limit keys
	RateLimitTypeAccount = "account"
	RateLimitTypeIP      = "ip"
)

Rate limiting constants

View Source
const (
	// Read scopes
	ReadNotifications = "read:notifications"
	ReadFollows       = "read:follows"
	ReadBlocks        = "read:blocks"
	ReadFilters       = "read:filters"

	// Write scopes
	WriteNotifications = "write:notifications"
	WriteFollows       = "write:follows"
	WriteBlocks        = "write:blocks"
	WriteFilters       = "write:filters"
)

OAuth 2.0 Scopes

View Source
const (
	// ScopeFollow grants relationship-management capability on public OAuth surfaces.
	ScopeFollow = "follow"
	// ScopePush grants push-subscription capability on public OAuth surfaces.
	ScopePush = "push"
)
View Source
const (
	SessionDuration               = 7 * 24 * time.Hour // 7 days (reduced from 30)
	ShortAccessTokenDuration      = 1 * time.Hour      // Align with client app expectations
	RefreshTokenRotationWindow    = 1 * time.Hour      // Reduced grace period (from 24h)
	MaxSessionsPerUser            = 10                 // Limit concurrent sessions
	SessionInactivityTimeout      = 24 * time.Hour     // Auto-logout after inactivity
	DeviceTrustPromotionThreshold = 7 * 24 * time.Hour // Days until device can be trusted

	// Device trust levels
	TrustLevelTrusted   = "trusted"
	TrustLevelUntrusted = "untrusted"
)

Session constants - enhanced security

View Source
const (
	ChallengeDuration     = 5 * time.Minute // WebAuthn challenges expire after 5 minutes
	MaxCredentialsPerUser = 10              // Maximum passkeys per user
)

WebAuthn constants

View Source
const AgentRuntimeRefreshAbsoluteTTL = RefreshTokenFamilyExpiry

AgentRuntimeRefreshAbsoluteTTL caps the total lifetime of an agent runtime refresh session.

View Source
const AgentRuntimeRefreshIdleTTL = RefreshTokenDuration

AgentRuntimeRefreshIdleTTL is the sliding inactivity window for agent runtime refresh sessions.

View Source
const AuthContextKey = "auth_context"

AuthContextKey is the key used to store AuthContext in request context.

View Source
const (
	// ContextKeyAuthenticatedAccount is the key for storing authenticated account in context
	ContextKeyAuthenticatedAccount contextKey = "authenticated_account"
)
View Source
const DefaultAgentRuntimeDeviceLabel = "local-agent"

DefaultAgentRuntimeDeviceLabel is used when a runtime does not provide its own label.

View Source
const DefaultBcryptCost = 12

DefaultBcryptCost is the default cost factor for bcrypt hashing

View Source
const OAuthClientSecretHashPrefix = common.OAuthClientSecretHashPrefix

OAuthClientSecretHashPrefix marks hashed secrets stored for OAuth clients.

View Source
const (
	SecurityActionDeny = "deny"
)

Security action constants

Variables

View Source
var (
	// ErrUnsupportedAgentKeyType is returned when the requested key type is not supported.
	ErrUnsupportedAgentKeyType = errors.New("unsupported agent key type")
	// ErrInvalidAgentPublicKey is returned when a provided agent public key cannot be parsed.
	ErrInvalidAgentPublicKey = errors.New("invalid agent public key")
	// ErrInvalidAgentSignature is returned when a provided signature cannot be verified.
	ErrInvalidAgentSignature = errors.New("invalid agent signature")
)
View Source
var (
	ErrInvalidCSRF = errors.New("invalid CSRF token")
	ErrExpiredCSRF = errors.New("expired CSRF token")
	ErrMissingCSRF = errors.New("missing CSRF token")
)

CSRF-related errors

View Source
var (
	// Basic password length errors
	ErrPasswordTooShort = apperrors.PasswordTooShort(8)
	ErrPasswordTooLong  = apperrors.PasswordTooLong(72)

	// Length requirement error for policy validation
	ErrPasswordInsufficientLength = apperrors.PasswordInsufficientLength()

	// Passwordless enforcement
	ErrPasswordAuthDisabled = errors.New("password authentication is disabled")

	// Password processing errors
	ErrPasswordHashFailed = apperrors.PasswordHashingFailed(errors.New("password hashing failed"))

	// Character requirement errors
	ErrPasswordMissingUppercase   = apperrors.PasswordMissingRequirement("uppercase letter")
	ErrPasswordMissingLowercase   = apperrors.PasswordMissingRequirement("lowercase letter")
	ErrPasswordMissingNumber      = apperrors.PasswordMissingRequirement("number")
	ErrPasswordMissingSpecialChar = apperrors.PasswordMissingRequirement("special character")

	// Content validation errors
	ErrPasswordContainsUsername  = apperrors.PasswordContainsUsername()
	ErrPasswordTooCommon         = apperrors.PasswordTooCommon()
	ErrPasswordSequentialPattern = apperrors.PasswordSequentialPattern()
	ErrPasswordRepeatedPattern   = apperrors.PasswordRepeatedPattern()

	// Session security errors
	ErrCSRFTokenGeneration     = apperrors.CSRFTokenGenerationFailed(errors.New("CSRF token generation failed"))
	ErrSessionIDGeneration     = apperrors.SessionIDGenerationFailed(errors.New("session ID generation failed"))
	ErrCookieEntropyGeneration = apperrors.CookieEntropyGenerationFailed(errors.New("cookie entropy generation failed"))
	ErrCSRFTokenRotation       = apperrors.CSRFTokenRotationFailed(errors.New("CSRF token rotation failed"))
	ErrCSRFValidationFailed    = apperrors.CSRFValidationFailed()

	// OAuth token validation errors
	ErrUnexpectedSigningMethod = apperrors.UnexpectedSigningMethod()
	ErrSessionIDMismatch       = apperrors.SessionIDMismatch()
	ErrIPAddressMismatch       = apperrors.IPAddressMismatch()
	ErrTokenVersionMismatch    = apperrors.TokenVersionMismatch()
	ErrTokenTooOld             = apperrors.TokenTooOld()

	// Wallet authentication errors
	ErrNonceGeneration          = apperrors.NonceGenerationFailed(errors.New("nonce generation failed"))
	ErrChallengeStorage         = apperrors.ChallengeStorageFailed(errors.New("challenge storage failed"))
	ErrChallengeRetrieval       = apperrors.ChallengeRetrievalFailed(errors.New("challenge retrieval failed"))
	ErrChallengeExpired         = apperrors.WalletChallengeExpired()
	ErrMessageMismatch          = apperrors.MessageMismatch()
	ErrAddressMismatch          = apperrors.WalletAddressMismatch()
	ErrSignatureVerification    = apperrors.SignatureVerificationFailed()
	ErrWalletCheck              = apperrors.WalletCheckFailed(errors.New("wallet check failed"))
	ErrWalletStorage            = apperrors.WalletStorageFailed(errors.New("wallet storage failed"))
	ErrWalletRetrieval          = apperrors.WalletRetrievalFailed(errors.New("wallet retrieval failed"))
	ErrWalletDeletion           = apperrors.WalletDeletionFailed(errors.New("wallet deletion failed"))
	ErrWalletAlreadyLinked      = apperrors.WalletAlreadyLinked()
	ErrInvalidSignatureFormat   = apperrors.InvalidSignatureFormat()
	ErrInvalidSignatureLength   = apperrors.InvalidSignatureLength()
	ErrPublicKeyRecovery        = apperrors.PublicKeyRecoveryFailed(errors.New("public key recovery failed"))
	ErrSignatureAddressMismatch = apperrors.SignatureAddressMismatch()

	// Social recovery errors
	ErrTrusteeActorIDRequired    = apperrors.TrusteeActorIDRequired()
	ErrInsufficientTrustees      = apperrors.InsufficientTrustees()
	ErrRecoveryTokenGeneration   = apperrors.RecoveryTokenGenerationFailed(errors.New("recovery token generation failed"))
	ErrRecoveryRequestNotFound   = apperrors.RecoveryRequestNotFound()
	ErrRecoveryRequestNotPending = apperrors.RecoveryRequestNotPending()
	ErrRecoveryRequestExpired    = apperrors.RecoveryRequestExpired()
	ErrTrusteeAlreadyVoted       = apperrors.TrusteeAlreadyVoted()

	// Social recovery repository operation errors
	ErrTrusteeStorage           = apperrors.TrusteeStorageFailed(errors.New("trustee storage failed"))
	ErrTrusteeDeletion          = apperrors.TrusteeDeletionFailed(errors.New("trustee deletion failed"))
	ErrTrusteeRetrieval         = apperrors.TrusteeRetrievalFailed(errors.New("trustee retrieval failed"))
	ErrRecoveryRequestStorage   = apperrors.RecoveryRequestStorageFailed(errors.New("recovery request storage failed"))
	ErrRecoveryRequestRetrieval = apperrors.RecoveryRequestRetrievalFailed(errors.New("recovery request retrieval failed"))
	ErrRecoveryRequestUpdate    = apperrors.RecoveryRequestUpdateFailed(errors.New("recovery request update failed"))
	ErrRecoveryTokenStorage     = apperrors.RecoveryTokenStorageFailed(errors.New("recovery token storage failed"))

	// WebAuthn service errors
	ErrWebAuthnServiceInit        = apperrors.WebAuthnServiceInitFailed(errors.New("WebAuthn service init failed"))
	ErrUserRetrieval              = apperrors.UserRetrievalFailed(errors.New("user retrieval failed"))
	ErrCredentialRetrieval        = apperrors.CredentialRetrievalFailed(errors.New("credential retrieval failed"))
	ErrRegistrationBegin          = apperrors.RegistrationBeginFailed(errors.New("registration begin failed"))
	ErrLoginBegin                 = apperrors.LoginBeginFailed(errors.New("login begin failed"))
	ErrSessionDataSerialization   = apperrors.SessionDataSerializationFailed(errors.New("session data serialization failed"))
	ErrSessionDataDeserialization = apperrors.SessionDataDeserializationFailed(errors.New("session data deserialization failed"))
	ErrWebAuthnChallengeStorage   = apperrors.WebAuthnChallengeStorageFailed(errors.New("WebAuthn challenge storage failed"))
	ErrCredentialResponse         = apperrors.CredentialResponseParseFailed(errors.New("credential response parse failed"))
	ErrCredentialCreation         = apperrors.CredentialCreationFailed(errors.New("credential creation failed"))
	ErrCredentialValidation       = apperrors.CredentialValidationFailed(errors.New("credential validation failed"))
	ErrCredentialStorage          = apperrors.CredentialStorageFailed(errors.New("credential storage failed"))
	ErrMaxCredentialsReached      = apperrors.MaxCredentialsReached()
	ErrInvalidSessionDataType     = apperrors.InvalidSessionDataType()
	ErrLastAuthMethodDelete       = apperrors.LastAuthMethodDelete()

	// Recovery code errors
	ErrRecoveryCodeGeneration = apperrors.RecoveryCodeGenerationFailed(errors.New("recovery code generation failed"))
	ErrRecoveryCodeHashing    = apperrors.RecoveryCodeHashingFailed(errors.New("recovery code hashing failed"))
	ErrRecoveryCodeStorage    = apperrors.RecoveryCodeStorageFailed(errors.New("recovery code storage failed"))
	ErrRecoveryCodeRetrieval  = apperrors.RecoveryCodeRetrievalFailed(errors.New("recovery code retrieval failed"))
	ErrRecoveryCodeMarkUsed   = apperrors.RecoveryCodeMarkUsedFailed(errors.New("recovery code mark used failed"))
	ErrRecoveryCodeClear      = apperrors.RecoveryCodeClearFailed(errors.New("recovery code clear failed"))

	// Secrets Manager errors
	ErrAWSConfigLoad              = apperrors.AWSConfigLoadFailed(errors.New("AWS config load failed"))
	ErrSecretsManagerConnection   = apperrors.SecretsManagerConnectionFailed(errors.New("secrets manager connection failed"))
	ErrInvalidPrivateKeyFormat    = apperrors.InvalidPrivateKeyFormat()
	ErrSecretValueMarshal         = apperrors.SecretValueMarshalFailed(errors.New("secret value marshal failed"))
	ErrSecretCreation             = apperrors.SecretCreationFailed(errors.New("secret creation failed"))
	ErrPrivateKeyRetrieval        = apperrors.PrivateKeyRetrievalFailed(errors.New("private key retrieval failed"))
	ErrSecretValueUnmarshal       = apperrors.SecretValueUnmarshalFailed(errors.New("secret value unmarshal failed"))
	ErrRetrievedPrivateKeyInvalid = apperrors.RetrievedPrivateKeyInvalid()
	ErrSecretDeletion             = apperrors.SecretDeletionFailed(errors.New("secret deletion failed"))
	ErrRSAKeyPairGeneration       = apperrors.RSAKeyPairGenerationFailed(errors.New("RSA key pair generation failed"))
	ErrPrivateKeyMarshal          = apperrors.PrivateKeyMarshalFailed(errors.New("private key marshal failed"))
	ErrPublicKeyMarshal           = apperrors.PublicKeyMarshalFailed(errors.New("public key marshal failed"))
	ErrGeneratedPrivateKeyStorage = apperrors.GeneratedPrivateKeyStorageFailed(errors.New("generated private key storage failed"))
	ErrKeyPairGenerationRotation  = apperrors.KeyPairGenerationRotationFailed(errors.New("key pair generation rotation failed"))
	ErrPEMBlockDecode             = apperrors.PEMBlockDecodeFailed()
	ErrPrivateKeyParse            = apperrors.PrivateKeyParseFailed(errors.New("private key parse failed"))
	ErrSecretValueNil             = apperrors.SecretValueNil()
	ErrSecretRetrievalRetries     = apperrors.SecretRetrievalRetriesFailed(errors.New("secret retrieval retries failed"))

	// Audit logging errors
	ErrAuditEventMarshal          = apperrors.AuditEventMarshalFailed(errors.New("audit event marshal failed"))
	ErrSIEMRequestCreation        = apperrors.SIEMRequestCreationFailed(errors.New("SIEM request creation failed"))
	ErrSIEMTransmission           = apperrors.SIEMTransmissionFailed(errors.New("SIEM transmission failed"))
	ErrSIEMResponseError          = apperrors.SIEMResponseError()
	ErrAuditRepositoryUnavailable = apperrors.AuditRepositoryUnavailable()

	// Rate limiting operation errors
	ErrIPRateLimitCheck       = apperrors.IPRateLimitCheckFailed(errors.New("IP rate limit check failed"))
	ErrAccountRateLimitCheck  = apperrors.AccountRateLimitCheckFailed(errors.New("account rate limit check failed"))
	ErrRecordIPAttempt        = apperrors.RecordIPAttemptFailed(errors.New("record IP attempt failed"))
	ErrRecordAccountAttempt   = apperrors.RecordAccountAttemptFailed(errors.New("record account attempt failed"))
	ErrGetIPAttemptCount      = apperrors.GetIPAttemptCountFailed(errors.New("get IP attempt count failed"))
	ErrGetAccountAttemptCount = apperrors.GetAccountAttemptCountFailed(errors.New("get account attempt count failed"))
	ErrImposeIPLockout        = apperrors.ImposeIPLockoutFailed(errors.New("impose IP lockout failed"))
	ErrImposeAccountLockout   = apperrors.ImposeAccountLockoutFailed(errors.New("impose account lockout failed"))

	// Session management errors
	ErrRefreshTokenGeneration    = apperrors.RefreshTokenGenerationFailed(errors.New("refresh token generation failed"))
	ErrDeviceIDRetrieval         = apperrors.DeviceIDRetrievalFailed(errors.New("device ID retrieval failed"))
	ErrSessionStorage            = apperrors.SessionStorageFailed(errors.New("session storage failed"))
	ErrNewRefreshTokenGeneration = apperrors.NewRefreshTokenGenerationFailed(errors.New("new refresh token generation failed"))
	ErrSessionUpdate             = apperrors.SessionUpdateFailed(errors.New("session update failed"))
	ErrUserSessionsRetrieval     = apperrors.UserSessionsRetrievalFailed(errors.New("user sessions retrieval failed"))
	ErrOldestSessionRemoval      = apperrors.OldestSessionRemovalFailed(errors.New("oldest session removal failed"))
	ErrInvalidRefreshToken       = apperrors.RefreshTokenInvalid()
	ErrSessionNotFound           = apperrors.SessionNotFound("")
	ErrSessionExpired            = apperrors.SessionExpired()
	ErrDeviceNotFound            = apperrors.DeviceNotFound("")

	// Device fingerprinting errors
	ErrUserDevicesRetrieval = apperrors.UserDevicesRetrievalFailed(errors.New("user devices retrieval failed"))
	ErrDeviceCreation       = apperrors.DeviceCreationFailed(errors.New("device creation failed"))
	ErrMaxDevicesExceeded   = apperrors.MaxDevicesExceeded()

	// Device ownership errors
	ErrDeviceOwnershipMismatch = apperrors.DeviceOwnershipMismatch()

	// JWT validation errors
	ErrJWTUnexpectedSigningMethod = apperrors.JWTUnexpectedSigningMethod()

	// Recovery federation errors
	ErrInvalidActivityObject           = apperrors.InvalidActivityObject()
	ErrNotRecoveryConfirmationActivity = apperrors.NotRecoveryConfirmationActivity()
	ErrMissingRequestID                = apperrors.MissingRequestID()
	ErrFailedToDecodePEM               = apperrors.FailedToDecodePEM()
	ErrUnsupportedPrivateKeyType       = apperrors.UnsupportedPrivateKeyType()
	ErrSecretsManagerNotAvailable      = apperrors.SecretsManagerNotAvailable()
	ErrSigningActorRetrievalFailed     = apperrors.SigningActorRetrievalFailed(errors.New("signing actor retrieval failed"))
	ErrRecoveryConfirmationFailed      = apperrors.RecoveryConfirmationFailed(errors.New("recovery confirmation failed"))
	ErrActorRetrievalFailed            = apperrors.ActorRetrievalFailed(errors.New("actor retrieval failed"))
	ErrPrivateKeyParseFailed           = apperrors.PrivateKeyParseFailed(errors.New("private key parse failed"))
	ErrPublicKeyMarshalFailed          = apperrors.PublicKeyMarshalFailed(errors.New("public key marshal failed"))
	ErrSystemActorKeyRetrievalFailed   = apperrors.SystemActorKeyRetrievalFailed(errors.New("system actor key retrieval failed"))
	ErrSystemActorKeyRotationFailed    = apperrors.SystemActorKeyRotationFailed(errors.New("system actor key rotation failed"))

	// Session lifecycle errors
	ErrSessionSecurityValidationFailed = apperrors.SessionSecurityValidationFailed("")
	ErrSessionCannotBeExtended         = apperrors.SessionCannotBeExtended("")
	ErrSessionMaxLifetimeReached       = apperrors.SessionMaxLifetimeReached()
	ErrSessionExtensionDisabled        = apperrors.SessionExtensionDisabled()
	ErrConcurrentSessionLimitExceeded  = apperrors.ConcurrentSessionLimitExceeded()
	ErrRefreshTokenRotationFailed      = apperrors.RefreshTokenRotationFailed(errors.New("refresh token rotation failed"))
	ErrInvalidRefreshTokenProvided     = apperrors.InvalidRefreshTokenProvided()
	ErrSessionSecurityCheckFailed      = apperrors.SessionSecurityCheckFailed(errors.New("session security check failed"))

	// Auth service operation errors
	ErrSessionCreationFailed         = apperrors.SessionCreationFailed(errors.New("session creation failed"))
	ErrAccessTokenGenerationFailed   = apperrors.AccessTokenGenerationFailed(errors.New("access token generation failed"))
	ErrPasswordHashingFailed         = apperrors.PasswordHashingFailed(errors.New("password hashing failed"))
	ErrPasswordUpdateFailed          = apperrors.PasswordUpdateFailed(errors.New("password update failed"))
	ErrSignatureVerificationFailed   = apperrors.SignatureVerificationFailed()
	ErrUserRetrievalFailed           = apperrors.UserRetrievalFailed(errors.New("user retrieval failed"))
	ErrRecoveryTokenGenerationFailed = apperrors.RecoveryTokenGenerationFailed(errors.New("recovery token generation failed"))
	ErrRecoveryTokenStorageFailed    = apperrors.RecoveryTokenStorageFailed(errors.New("recovery token storage failed"))

	// Common authentication errors (moved from service.go)
	ErrInvalidCredentials    = apperrors.InvalidCredentials()
	ErrUserNotFound          = apperrors.UserNotFound("")
	ErrUserSuspended         = apperrors.UserSuspended("")
	ErrUserNotApproved       = apperrors.UserNotApproved("")
	ErrInvalidToken          = apperrors.TokenInvalid("")
	ErrWebAuthnNotConfigured = apperrors.WebAuthnNotConfigured()
)

Legacy error variables for backwards compatibility These are now wrappers around the centralized error system

View Source
var (
	// ErrMissingAuthHeader is returned when Authorization header is missing
	ErrMissingAuthHeader = errors.New("missing authorization header")
	// ErrInvalidAuthHeader is returned when Authorization header is malformed
	ErrInvalidAuthHeader = errors.New("invalid authorization header")
)
View Source
var (
	// ErrInvalidGrant is returned when the authorization code is invalid or expired
	ErrInvalidGrant = errors.New("invalid_grant")
	// ErrInvalidClient is returned when the client is not authorized
	ErrInvalidClient = errors.New("invalid_client")
	// ErrInvalidRequest is returned when the request is malformed
	ErrInvalidRequest = errors.New("invalid_request")
	// ErrUnauthorizedClient is returned when the client is not authorized for this grant type
	ErrUnauthorizedClient = errors.New("unauthorized_client")
	// ErrUnsupportedGrantType is returned when the grant type is not supported
	ErrUnsupportedGrantType = errors.New("unsupported_grant_type")
	// ErrInvalidCodeChallenge is returned when the code challenge doesn't match
	ErrInvalidCodeChallenge = errors.New("invalid_code_challenge")
	// ErrInvalidScope is returned when requested scopes are invalid
	ErrInvalidScope = errors.New("invalid_scope")
	// ErrInvalidAPIKey is returned when the API key is invalid
	ErrInvalidAPIKey = errors.New("invalid_api_key")
)
View Source
var (
	ErrTooManyAttempts = errors.New("too many login attempts")
	ErrAccountLocked   = errors.New("account temporarily locked")
	ErrIPRateLimited   = errors.New("IP address rate limited")
)

Rate limiting errors

View Source
var (
	// ErrTokenReuse indicates a refresh token was reused (security breach)
	ErrTokenReuse = errors.New("refresh token reuse detected")
	// ErrExpiredRefreshToken indicates the refresh token has expired
	ErrExpiredRefreshToken = errors.New("refresh token expired")
)
View Source
var (
	ErrChallengeNotFound    = errors.New("challenge not found or expired")
	ErrCredentialNotFound   = errors.New("credential not found")
	ErrInvalidCredential    = errors.New("invalid credential")
	ErrUserHasNoCredentials = errors.New("user has no credentials")
)

WebAuthn errors

View Source
var DefaultPasswordStrengthConfig = PasswordStrengthConfig{
	MinLength:  8,
	LongLength: 16,
	LongBonus:  1,

	RequireAllCharacterTypesForLongBonus: true,
	SequentialPenalty:                    2,
	SequentialPatternMinRun:              4,
	RepeatedPenalty:                      1,
}

DefaultPasswordStrengthConfig matches the built-in strength scoring used by PasswordStrength.

View Source
var DefaultPolicy = PasswordPolicy{
	MinLength:              12,
	RequireUppercase:       true,
	RequireLowercase:       true,
	RequireNumbers:         true,
	RequireSpecialChars:    true,
	PreventCommonPasswords: true,
}

DefaultPolicy defines the default password requirements

Functions

func AdminAuth

func AdminAuth(config MiddlewareConfig) apptheory.Middleware

AdminAuth creates middleware specifically for admin operations

func AgentAccessTokenTTL added in v1.1.46

func AgentAccessTokenTTL(cfg *config.Config) time.Duration

AgentAccessTokenTTL returns the configured default lifetime for agent access tokens. When no agent-specific override is configured, Lesser falls back to the shared OAuth default.

func AgentRuntimeClientIDs added in v1.2.0

func AgentRuntimeClientIDs() []string

AgentRuntimeClientIDs returns the dedicated internal runtime client IDs that participate in runtime-session diagnostics and family rotation.

func AgentRuntimeRefreshExpiries added in v1.2.53

func AgentRuntimeRefreshExpiries(now time.Time, idleTTL, absoluteTTL time.Duration) (time.Time, time.Time)

AgentRuntimeRefreshExpiries returns the idle and absolute refresh-session expiries for an agent runtime token. Zero durations preserve Lesser's default runtime session bounds; positive durations allow delegation flows to cap refresh sessions to the requested TTL.

func CSRFMiddleware

func CSRFMiddleware(manager *CSRFManager) func(http.HandlerFunc) http.HandlerFunc

CSRFMiddleware creates middleware for CSRF protection

func CanonicalOAuthScopes added in v1.1.53

func CanonicalOAuthScopes() []string

CanonicalOAuthScopes returns the externally-advertised Lesser OAuth scope catalog.

func CoalesceAgentRuntimeLabel added in v1.1.50

func CoalesceAgentRuntimeLabel(primary, fallback string) string

CoalesceAgentRuntimeLabel returns a stable label for operator-visible runtime sessions.

func ConstantTimeCompare

func ConstantTimeCompare(a, b string) bool

ConstantTimeCompare performs a constant-time comparison of two strings

func ConstantTimeComparePadded

func ConstantTimeComparePadded(a, b string) bool

ConstantTimeComparePadded performs a constant-time comparison without early exit on length mismatch. Length still influences runtime due to required padding work; for secrets of fixed length this is acceptable.

func ConstantTimeDelay

func ConstantTimeDelay()

ConstantTimeDelay adds a small random delay to prevent timing analysis

func CreateAPIAuthMiddleware

func CreateAPIAuthMiddleware(oauthService common.OAuthServiceInterface, logger *zap.Logger) apptheory.Middleware

CreateAPIAuthMiddleware creates standard API authentication middleware

func CreateAPIAuthMiddlewareFromAuthAndOAuthServices added in v1.2.37

func CreateAPIAuthMiddlewareFromAuthAndOAuthServices(authService *AuthService, oauthService *OAuthService, logger *zap.Logger) apptheory.Middleware

CreateAPIAuthMiddlewareFromAuthAndOAuthServices creates API auth middleware that preserves native session validation while also accepting OAuth-issued tokens.

func CreateAPIAuthMiddlewareFromAuthService

func CreateAPIAuthMiddlewareFromAuthService(authService *AuthService, logger *zap.Logger) apptheory.Middleware

CreateAPIAuthMiddlewareFromAuthService creates API auth middleware from AuthService.

func CreateFederationAuthMiddleware

func CreateFederationAuthMiddleware(oauthService common.OAuthServiceInterface, logger *zap.Logger) apptheory.Middleware

CreateFederationAuthMiddleware creates federation-specific authentication middleware

func CreateFederationAuthMiddlewareFromAuthService

func CreateFederationAuthMiddlewareFromAuthService(authService *AuthService, logger *zap.Logger) apptheory.Middleware

CreateFederationAuthMiddlewareFromAuthService creates federation auth middleware from AuthService.

func CreateGraphQLAuthMiddleware

func CreateGraphQLAuthMiddleware(oauthService common.OAuthServiceInterface, logger *zap.Logger) apptheory.Middleware

CreateGraphQLAuthMiddleware creates standard GraphQL authentication middleware

func CreateGraphQLAuthMiddlewareFromAuthService

func CreateGraphQLAuthMiddlewareFromAuthService(authService *AuthService, logger *zap.Logger) apptheory.Middleware

CreateGraphQLAuthMiddlewareFromAuthService creates GraphQL auth middleware from AuthService.

func CreatePrincipalContextBridgeFromAuthAndOAuthServices added in v1.2.37

func CreatePrincipalContextBridgeFromAuthAndOAuthServices(authService *AuthService, oauthService *OAuthService, logger *zap.Logger, serviceName string) apptheory.Middleware

CreatePrincipalContextBridgeFromAuthAndOAuthServices mirrors principal data for both native session-backed tokens and OAuth-issued tokens into the legacy request context.

func CreatePrincipalContextBridgeFromAuthService added in v1.2.35

func CreatePrincipalContextBridgeFromAuthService(authService *AuthService, logger *zap.Logger, serviceName string) apptheory.Middleware

CreatePrincipalContextBridgeFromAuthService mirrors AuthService-backed principal data into the legacy request context.

func CreatePrincipalContextBridgeFromOAuthService added in v1.2.35

func CreatePrincipalContextBridgeFromOAuthService(oauthService *OAuthService, logger *zap.Logger, serviceName string) apptheory.Middleware

CreatePrincipalContextBridgeFromOAuthService mirrors OAuthService-backed principal data into the legacy request context.

func DefaultScopes

func DefaultScopes() []string

DefaultScopes returns the default scopes for a user

func ExtractBearerToken

func ExtractBearerToken(authHeader string) (string, error)

ExtractBearerToken extracts the token from the Authorization header

func FollowAuth

func FollowAuth(config MiddlewareConfig) apptheory.Middleware

FollowAuth creates middleware specifically for follow operations

func GenerateCSRFTokenHandler

func GenerateCSRFTokenHandler(manager *CSRFManager) http.HandlerFunc

GenerateCSRFTokenHandler creates an endpoint to get CSRF tokens

func GeneratePasswordHint

func GeneratePasswordHint(password string) []string

GeneratePasswordHint provides helpful hints for password improvement

func GetAuthenticatedUsername

func GetAuthenticatedUsername(ctx *apptheory.Context) string

GetAuthenticatedUsername retrieves the authenticated username or empty string

func GetJWTClaims

func GetJWTClaims(ctx *apptheory.Context) common.Claims

GetJWTClaims retrieves the JWT claims from context

func GetLegacyAuthContext

func GetLegacyAuthContext(ctx *apptheory.Context) *common.AuthContext

GetLegacyAuthContext retrieves the authentication context from the request context.

func GetUsernameFromContext

func GetUsernameFromContext(ctx *apptheory.Context, oauthService OAuthServiceInterface) (string, error)

GetUsernameFromContext extracts just the username from the context This consolidates the pattern: username, err := h.authenticateRequestWithScope(ctx, "read")

func GetUsernameFromStandardContext

func GetUsernameFromStandardContext(ctx context.Context) (string, error)

GetUsernameFromStandardContext extracts username from standard context

func HashOAuthClientSecret

func HashOAuthClientSecret(secret string) (string, error)

HashOAuthClientSecret hashes an OAuth client secret for storage.

func HashPassword

func HashPassword(password string) (string, error)

HashPassword hashes a password using bcrypt

func IsAgentRuntimeClientID added in v1.1.50

func IsAgentRuntimeClientID(clientID string) bool

IsAgentRuntimeClientID reports whether a client ID belongs to Lesser's long-lived agent runtime flows.

func IsAgentRuntimeRefreshToken added in v1.2.0

func IsAgentRuntimeRefreshToken(token *storage.RefreshToken) bool

IsAgentRuntimeRefreshToken reports whether a stored refresh token belongs to the dedicated internal runtime session model rather than ordinary public or compatibility OAuth rotation.

func IsAppTheoryContextAuthenticated added in v1.2.35

func IsAppTheoryContextAuthenticated(ctx *apptheory.Context) bool

IsAppTheoryContextAuthenticated reports whether the request has authenticated AppTheory principal state.

func IsAuthenticated

func IsAuthenticated(ctx *apptheory.Context) bool

IsAuthenticated returns true if the request is authenticated

func IsCommonPassword

func IsCommonPassword(password string) bool

IsCommonPassword checks if a password is in the common passwords list

func IsTestMode

func IsTestMode(_ *apptheory.Context) bool

IsTestMode determines if the current request is in test mode

func LegacyAuthContextFromAppTheoryContext added in v1.2.35

func LegacyAuthContextFromAppTheoryContext(ctx *apptheory.Context) *common.AuthContext

LegacyAuthContextFromAppTheoryContext synthesizes the legacy common.AuthContext from AppTheory principal state.

func ListAgentRuntimeSessions added in v1.1.50

func ListAgentRuntimeSessions(ctx context.Context, repos StorageProvider, username string) ([]storage.RefreshToken, error)

ListAgentRuntimeSessions returns the current runtime refresh session records for an agent.

func NewAppTheoryPrincipalHookFromAuthAndOAuthServices added in v1.2.37

func NewAppTheoryPrincipalHookFromAuthAndOAuthServices(authService *AuthService, oauthService *OAuthService, logger *zap.Logger, serviceName string) apptheory.PrincipalAuthHook

NewAppTheoryPrincipalHookFromAuthAndOAuthServices builds an AppTheory principal hook that preserves native session validation while also accepting OAuth-issued access tokens on the same API surface.

func NewAppTheoryPrincipalHookFromAuthService added in v1.2.35

func NewAppTheoryPrincipalHookFromAuthService(authService *AuthService, logger *zap.Logger, serviceName string)