auth

package module
v0.25.0 Latest Latest
Warning

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

Go to latest
Published: Jan 20, 2026 License: MIT Imports: 27 Imported by: 0

README

go-auth

A Go authentication library providing JWT based authentication, password management, and role based access control with support for resource level permissions.

Features

  • JWT token generation and validation with structured claims
  • Password hashing and verification using bcrypt
  • User registration and login with email validation
  • Password reset flow with email verification
  • Role based access control (RBAC) with hierarchical permissions
  • Resource level permissions for fine grained access control
  • HTTP middleware for route protection
  • Built in authentication controllers for web applications
  • Database persistence layer using Bun ORM
  • OAuth2 social login (GitHub, Google) with account linking
  • Customizable identity providers and role providers

Installation

go get github.com/goliatone/go-auth

Quick Start

Basic Authentication Setup
package main

import (
    "context"
    "github.com/goliatone/go-auth"
    repo "github.com/goliatone/go-auth/repository"
)

func main() {
    // Create repository manager
    repoManager := repo.NewRepositoryManager(bunDB)

    // Create user provider
    userProvider := auth.NewUserProvider(repoManager.Users())

    // Create authenticator with basic configuration
    config := &AuthConfig{
        SigningKey:      "your-secret-key",
        TokenExpiration: 24, // hours
        Issuer:         "your-app",
        Audience:       []string{"your-audience"},
    }

    authenticator := auth.NewAuthenticator(userProvider, config)

    // Login user
    token, err := authenticator.Login(context.Background(), "[email protected]", "password")
    if err != nil {
        log.Fatal(err)
    }

    // Validate token and get session
    session, err := authenticator.SessionFromToken(token)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("User ID: %s\n", session.GetUserID())
}
Enhanced Resource Level Permissions
// Implement ResourceRoleProvider for fine-grained permissions
type CustomResourceRoleProvider struct {
    repo auth.RepositoryManager
}

func (p *CustomResourceRoleProvider) FindResourceRoles(ctx context.Context, identity auth.Identity) (map[string]string, error) {
    resourceRoles := make(map[string]string)

    switch identity.Role() {
    case "admin":
        resourceRoles["admin:dashboard"] = "owner"
        resourceRoles["project:default"] = "admin"
    case "user":
        resourceRoles["project:default"] = "member"
    }

    return resourceRoles, nil
}

// Use enhanced authenticator
authenticator = authenticator.WithResourceRoleProvider(&CustomResourceRoleProvider{repo: repoManager})

// Generated tokens will include resource-specific permissions
token, err := authenticator.Login(ctx, "[email protected]", "password")
HTTP Integration
// Create HTTP authenticator for web applications
httpAuth, err := auth.NewHTTPAuthenticator(authenticator, config)
if err != nil {
    log.Fatal(err)
}

// Register authentication routes (login, register, password reset)
auth.RegisterAuthRoutes(router.Group("/auth"),
    auth.WithControllerLogger(logger),
    func(ac *auth.AuthController) *auth.AuthController {
        ac.Auther = httpAuth
        ac.Repo = repoManager
        return ac
    })

// Protect routes with middleware
protected := httpAuth.ProtectedRoute(config, errorHandler)
router.Get("/profile", profileHandler, protected)

Success handler contract (jwt middleware)
The JWT middleware now invokes a SuccessHandler with the signature func(ctx router.Context, next router.HandlerFunc) error. The default simply calls next(ctx). If you override it, you are responsible for deciding whether to call next (run the protected handler) or short-circuit (e.g., redirect). Example:

protected := httpAuth.ProtectedRoute(config, errorHandler)

// Custom success hook that adds logging, then runs the handler
wrapped := func(ctx router.Context) error {
    return protected(func(c router.Context) error {
        log.Println("auth ok for", c.Path())
        return profileHandler(c) // or c.Next() if chaining
    })(ctx)
}

router.Get("/profile", profileHandler, wrapped)
Social Login (OAuth2)
import (
    "encoding/base64"

    "github.com/goliatone/go-auth/social"
    "github.com/goliatone/go-auth/social/providers/github"
    "github.com/goliatone/go-auth/social/providers/google"
    repo "github.com/goliatone/go-auth/repository"
)

stateEncKey, _ := base64.StdEncoding.DecodeString(cfg.Social.StateEncryptionKey)
stateHMACKey, _ := base64.StdEncoding.DecodeString(cfg.Social.StateHMACKey)

socialRepo := repo.NewSocialAccountRepository(bunDB)

socialAuth := social.NewSocialAuthenticator(
    socialRepo,
    repoManager.Users(),
    authenticator.TokenService(),
    social.SocialAuthConfig{
        DefaultRedirectURL:   "/",
        StateEncryptionKey:   stateEncKey,
        StateHMACKey:         stateHMACKey,
        AllowSignup:          true,
        AllowLinking:         true,
        RequireEmailVerified: true,
    },
    social.WithProvider(github.New(github.Config{
        ClientID:     cfg.Social.GitHubClientID,
        ClientSecret: cfg.Social.GitHubClientSecret,
        CallbackURL:  cfg.BaseURL + "/auth/social/github/callback",
    })),
    social.WithProvider(google.New(google.Config{
        ClientID:     cfg.Social.GoogleClientID,
        ClientSecret: cfg.Social.GoogleClientSecret,
        CallbackURL:  cfg.BaseURL + "/auth/social/google/callback",
    })),
)

socialController := social.NewHTTPController(socialAuth, social.HTTPConfig{
    PathPrefix:        "/auth/social",
    SessionContextKey: cfg.Auth.GetContextKey(),
    CookieName:        cfg.Auth.GetContextKey(),
    SuccessRedirect:   "/dashboard",
    ErrorRedirect:     "/login?error=auth_failed",
    CookieSecure:      true,
    CookieHTTPOnly:    true,
    CookieSameSite:    "Lax",
})
socialController.RegisterRoutes(router.Group("/auth/social"))

Routes registered by the controller:

  • GET /auth/social/providers
  • GET /auth/social/:provider
  • GET /auth/social/:provider/callback
  • POST /auth/social/:provider/link
  • DELETE /auth/social/:provider
  • GET /auth/social/accounts

See docs/SOCIAL_LOGIN.md for linking policies, migration guidance, and security notes. Example wiring lives in examples/extensions/social_login.go.

Auth0 Integration (Optional)

Auth0 support is opt-in and uses provider/auth0 for JWT validation and claims mapping. For Auth0 + local tokens, use NewMultiTokenValidator to accept both.

import "github.com/goliatone/go-auth/provider/auth0"

auth0Validator, err := auth0.NewTokenValidator(auth0.Config{
    Domain:   "your-tenant.auth0.com",
    Audience: []string{"https://api.yourapp.com"},
})
if err != nil {
    return err
}

authenticator := auth.NewAuthenticator(userProvider, cfg.Auth)

// Accept Auth0 tokens only.
authenticator = authenticator.WithTokenValidator(auth0Validator)

// Or accept Auth0 + local go-auth tokens.
composite := auth.NewMultiTokenValidator(auth0Validator, authenticator.TokenService())
authenticator = authenticator.WithTokenValidator(composite)

For sync (local mirroring), use provider/auth0/sync with an IdentifierStore. See docs/AUTH0.md and examples/auth0/ for full examples.

Auth0 validation errors normalize to ErrTokenExpired / ErrTokenMalformed with provider=auth0 metadata. Use HasUserUUID(session) before calling Session.GetUserUUID because Auth0 sub values are not UUIDs.

Session with Resource Permissions
// Check permissions in handlers
func profileHandler(c router.Context) error {
    session, err := auth.GetRouterSession(c, "auth")
    if err != nil {
        return err
    }

    // Enhanced session with resource-level permissions
    if roleCapableSession, ok := session.(auth.RoleCapableSession); ok {
        canEditSettings := roleCapableSession.CanEdit("admin:settings")
        canDeleteUsers := roleCapableSession.CanDelete("admin:users")
        hasAdminRole := roleCapableSession.HasRole("admin")
        isAtLeastMember := roleCapableSession.IsAtLeast("member")

        // Use permissions for authorization logic
    }

    return c.JSON(map[string]any{
        "user_id": session.GetUserID(),
        "data": session.GetData(),
    })
}
CSRF Protection

go-auth ships with a CSRF middleware that plugs into go-router and the template helper stack.

Stateless (default)

import (
    csrf "github.com/goliatone/go-auth/middleware/csrf"
    "github.com/goliatone/go-router"
)

func main() {
    app := router.New()

    // HMAC-signed tokens; no backing store required
    app.Use(csrf.New(csrf.Config{
        SecureKey: []byte("your-32-byte-secret"),
    }))

    app.Post("/submit", func(ctx router.Context) error {
        // Token checked automatically
        return ctx.SendStatus(router.StatusNoContent)
    })

    app.Listen(":8080")
}

Stateful (storage backed)

type redisStorage struct{ client *redis.Client }

func (r *redisStorage) Get(key string) (string, error) {
    return r.client.Get(context.Background(), key).Result()
}

// Set/Delete omitted for brevity

app.Use(csrf.New(csrf.Config{
    Storage:    &redisStorage{client: redisClient},
    Expiration: 24 * time.Hour,
}))

Templates

// Inside your handler
viewCtx := auth.MergeTemplateData(ctx, router.ViewContext{
    "title": "Secure form",
})

// MergeTemplateData copies the latest request scoped helpers so csrf_field,
// csrf_meta, etc. always render the token minted by the middleware.
return ctx.Render("form", viewCtx)

csrf_field, csrf_token, and csrf_meta are now lazily evaluated helpers. When you register auth.TemplateHelpers() globally, the template engine resolves the current request’s CSRF token automatically—no need to clone helper maps on every render (though MergeTemplateData remains useful when you want a concrete snapshot).

<form method="post">
    {{ csrf_field }}
    <!-- other fields -->
</form>

<script>
    const token = "{{ csrf_token }}";
    const header = "{{ csrf_header_name }}";
    fetch("/submit", {
        method: "POST",
        headers: { [header]: token },
    });
</script>
<!-- Layout head section -->
{{ csrf_meta }}
<meta name="another-example" content="value">

See middleware/csrf/README.md for more examples and configuration options (custom token lookup, skipping routes, etc.).

AJAX/SPA bootstrap endpoint

For clients that need to refresh tokens via XHR (e.g., SPAs), register the helper route:

import csrf "github.com/goliatone/go-auth/middleware/csrf"

csrf.RegisterRoutes(app.Router())

The handler returns JSON:

{
  "token": "...",
  "field_name": "_token",
  "header_name": "X-CSRF-Token"
}

Responses include Cache-Control: no-store, so clients fetch fresh tokens as needed. Call this endpoint whenever a token expires or before issuing state-changing requests in a long-lived SPA session.

Lifecycle Extensions

User Status & State Machine

go-auth persists lifecycle metadata through two columns on users: status (pending, active, suspended, disabled, archived) and suspended_at (timestamp set whenever a user enters or exits the suspended state). The default UserStateMachine enforces the transition graph (archived is terminal, pending can only move to active or disabled, etc.), keeps timestamps in sync, and publishes ActivitySink events.

Use the shared Users repository helpers (UpdateStatus, Suspend, Reinstate) or work directly with the state machine when you need options such as WithTransitionReason, WithTransitionMetadata, or custom hooks:

auditSink := auth.ActivitySinkFunc(func(ctx context.Context, event auth.ActivityEvent) error {
    log.Printf(
        "user %s transitioned %s -> %s (reason=%v)",
        event.UserID,
        event.FromStatus,
        event.ToStatus,
        event.Metadata["reason"],
    )
    return nil
})

stateMachine := auth.NewUserStateMachine(
    repoManager.Users(),
    auth.WithStateMachineActivitySink(auditSink),
)

actor := auth.ActorRef{ID: "admin-42", Type: "admin"}
updated, err := stateMachine.Transition(
    ctx,
    actor,
    user,
    auth.UserStatusSuspended,
    auth.WithTransitionReason("manual review"),
    auth.WithTransitionMetadata(map[string]any{"ticket": "SEC-204"}),
)
if err != nil {
    panic(err)
}

fmt.Println("new status:", updated.Status, "suspended at:", updated.SuspendedAt)

See examples/extensions/extensions.go for an end-to-end sample that persists activity rows and decorates claims based on tenant context.

Transition hooks bubble failures through a configurable handler. By default go-auth panics with a detailed message (great for development). In production override this behavior with auth.WithStateMachineHookErrorHandler to convert hook failures into domain errors or alerts:

handler := func(ctx context.Context, phase auth.TransitionHookPhase, err error, tc auth.TransitionContext) error {
    log.Printf("hook stage=%s user=%s error=%v", phase, tc.User.ID, err)
    return fmt.Errorf("policy hook failed: %w", err)
}

stateMachine := auth.NewUserStateMachine(
    repoManager.Users(),
    auth.WithStateMachineHookErrorHandler(handler),
)
ActivitySink Wiring

ActivitySink is a small interface used across lifecycle transitions, login/impersonation flows, and password reset handlers:

  • ActivityEvent.EventType distinguishes lifecycle (user.status.changed), login, impersonation, and password reset actions.
  • ActorRef identifies who triggered the change (admin dashboard, API, system job).
  • Metadata is an open map for reasons, ticket numbers, IP addresses, etc.
  • Failures are logged; auth flows continue unless you wrap the sink with your own retry/alerting logic.

Configure sinks wherever lifecycle events occur:

auditSink := auth.ActivitySinkFunc(func(ctx context.Context, event auth.ActivityEvent) error {
    log.Printf("activity event=%s user=%s actor=%s", event.EventType, event.UserID, event.Actor.Type)
    return nil
})

stateMachine := auth.NewUserStateMachine(users,
    auth.WithStateMachineActivitySink(auditSink),
)

auther := auth.NewAuthenticator(provider, cfg).
    WithActivitySink(auditSink)

The same sink can forward events to a SQL table, queue, or logging pipeline. Refer to examples/extensions/extensions.go for a Postgres-based implementation and batching hints.

ClaimsDecorator Hook

Use Auther.WithClaimsDecorator to enrich JWTs with tenant metadata or derived resource roles before they are signed. Decorators receive the pending JWTClaims and may only mutate extension fields such as Resources, Metadata, or additional custom payload that your product documents. Core JWT claims (sub, uid, iss, aud, iat, exp) are guarded and any attempt to edit them aborts token generation.

decorator := auth.ClaimsDecoratorFunc(func(ctx context.Context, identity auth.Identity, claims *auth.JWTClaims) error {
	if claims.Metadata == nil {
		claims.Metadata = map[string]any{}
	}
	claims.Metadata["tenant_id"] = lookupTenant(identity.ID())
	if claims.Resources == nil {
		claims.Resources = map[string]string{}
	}
	claims.Resources["team:"+identity.ID()] = "editor"
	return nil
})

auther := auth.NewAuthenticator(provider, cfg).
	WithClaimsDecorator(decorator)

If the decorator returns an error the token flow stops, the failure is logged, and no JWT is issued. Coordinate decorations with your ActivitySink so downstream services can reconcile claims with lifecycle transitions. Additional wiring tips and a multi-tenant example live in examples/extensions/extensions.go.

API Reference

TokenService Access

The Authenticator provides access to its underlying TokenService for advanced use cases:

// Access the token service directly
tokenService := authenticator.TokenService()

// Generate tokens manually with custom resource roles
resourceRoles := map[string]string{
    "project:123": "admin",
    "files:uploads": "owner",
}
token, err := tokenService.Generate(identity, resourceRoles)

// Validate tokens manually
claims, err := tokenService.Validate(tokenString)
JWT Middleware Integration

Use the TokenService with JWT middleware for custom authentication flows:

// Create TokenServiceAdapter for jwtware middleware
tokenValidator := auth.NewTokenServiceAdapter(authenticator.TokenService())

// Configure JWT middleware
jwtConfig := jwtware.Config{
    SigningKey: jwtware.SigningKey{
        Key:    []byte(config.GetSigningKey()),
        JWTAlg: config.GetSigningMethod(),
    },
    TokenValidator: tokenValidator,
    ContextKey:     "auth",
}

middleware := jwtware.New(jwtConfig)

Core Concepts

User Roles

The library defines a hierarchical role system:

  • guest - Read-only access
  • member - Read and edit access
  • admin - Read, edit, and create access
  • owner - Full access including delete
Resource-Level Permissions

Beyond global user roles, the library supports resource-specific permissions:

// Example resource roles map
resourceRoles := map[string]string{
    "project:123":     "admin",      // Admin of project 123
    "admin:dashboard": "member",     // Member access to admin dashboard
    "files:uploads":   "owner",      // Owner of file uploads
}
JWT Claims Structure

The library generates structured JWT claims:

{
  "sub": "user-id",
  "uid": "user-id",
  "role": "admin",
  "res": {
    "project:123": "admin",
    "admin:dashboard": "member"
  },
  "iss": "your-app",
  "aud": ["your-audience"],
  "iat": 1234567890,
  "exp": 1234654290
}

Database Schema

The library requires two database tables:

Users Table
CREATE TABLE users (
    id UUID PRIMARY KEY,
    user_role VARCHAR NOT NULL,
    first_name VARCHAR NOT NULL,
    last_name VARCHAR NOT NULL,
    username VARCHAR UNIQUE NOT NULL,
    email VARCHAR UNIQUE NOT NULL,
    phone_number VARCHAR,
    password_hash VARCHAR,
    is_email_verified BOOLEAN DEFAULT FALSE,
    login_attempts INTEGER DEFAULT 0,
    login_attempt_at TIMESTAMP,
    loggedin_at TIMESTAMP,
    metadata JSONB,
    reseted_at TIMESTAMP,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP
);
Password Reset Table
CREATE TABLE password_reset (
    id UUID PRIMARY KEY,
    user_id UUID REFERENCES users(id),
    status VARCHAR NOT NULL,
    email VARCHAR NOT NULL,
    reseted_at TIMESTAMP,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP
);
Social Accounts Table

Social login adds a social_accounts table for linked provider identities. Migrations ship in data/sql/migrations and data/sql/migrations/sqlite.

CREATE TABLE social_accounts (
    id TEXT NOT NULL PRIMARY KEY,
    user_id TEXT NOT NULL,
    provider TEXT NOT NULL,
    provider_user_id TEXT NOT NULL,
    email TEXT,
    name TEXT,
    username TEXT,
    avatar_url TEXT,
    access_token TEXT,
    refresh_token TEXT,
    token_expires_at TIMESTAMP NULL,
    profile_data JSONB NOT NULL DEFAULT '{}'::jsonb,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
    CONSTRAINT uq_social_accounts_provider_id UNIQUE (provider, provider_user_id),
    CONSTRAINT uq_social_accounts_user_provider UNIQUE (user_id, provider)
);

CREATE INDEX idx_social_accounts_user_id ON social_accounts(user_id);
CREATE INDEX idx_social_accounts_provider ON social_accounts(provider, provider_user_id);
Auth0 Identifiers Table (Optional Sync Track)

Auth0 sync adds a user_identifiers mapping table and optional external ID columns on users. Migrations ship in data/sql/migrations and data/sql/migrations/sqlite:

  • 0001_auth0_identifiers.up.sql
  • 0001_auth0_identifiers.down.sql

See docs/AUTH0.md for schema details and sync wiring.

Configuration

The library uses a configuration interface that you can implement:

type Config interface {
    GetSigningKey() string
    GetTokenExpiration() int
    GetExtendedTokenDuration() int
    GetIssuer() string
    GetAudience() []string
    GetContextKey() string
    GetTokenLookup() string
    GetAuthScheme() string
    GetRejectedRouteKey() string
    GetRejectedRouteDefault() string
}

Authentication Flow

User Registration
  1. User submits registration form
  2. Password is hashed using bcrypt
  3. User record is created in database
  4. Optional email verification can be triggered
Login Process
  1. User provides credentials
  2. Identity provider verifies password
  3. Resource role provider fetches permissions (if configured)
  4. JWT token is generated with structured claims
  5. Token is returned to client
Password Reset
  1. User requests password reset via email
  2. Reset token is generated and stored
  3. Email with reset link is sent
  4. User follows link to reset password
  5. New password is hashed and updated

Middleware

HTTP Middleware

The library provides HTTP middleware for route protection:

// Protect routes requiring authentication
protectedRoute := auth.ProtectedRoute(config, errorHandler)
router.Use("/api/", protectedRoute)

// Custom authorization logic
func requireAdminRole(c router.Context) error {
    session, _ := auth.GetRouterSession(c, "auth")
    if roleSession, ok := session.(auth.RoleCapableSession); ok {
        if !roleSession.IsAtLeast("admin") {
            return c.Status(403).SendString("Forbidden")
        }
    }
    return c.Next()
}
WebSocket Authentication

The library provides seamless WebSocket authentication integration with go-router through the WSTokenValidator interface:

Basic WebSocket Setup
// Create authenticator (same as HTTP setup)
authenticator := auth.NewAuthenticator(userProvider, config)

// Create WebSocket authentication middleware using go-auth
wsAuthMiddleware := authenticator.NewWSAuthMiddleware()

// Apply to WebSocket routes
router.WS("/chat", chatHandler, wsAuthMiddleware)
Custom WebSocket Configuration
// Custom WebSocket auth configuration
wsConfig := router.WSAuthConfig{
    TokenExtractor: func(req *http.Request) (string, error) {
        // Extract token from custom header
        token := req.Header.Get("X-Auth-Token")
        if token == "" {
            return "", errors.New("missing auth token")
        }
        return token, nil
    },
    // Optional: custom error handling
    ErrorHandler: func(ctx *router.WSContext, err error) {
        ctx.WriteMessage(websocket.TextMessage, []byte(`{"error": "authentication failed"}`))
        ctx.Close()
    },
}

wsAuthMiddleware := authenticator.NewWSAuthMiddleware(wsConfig)
router.WS("/secure-chat", secureHandler, wsAuthMiddleware)
Using Authentication in WebSocket Handlers
func chatHandler(ctx *router.WSContext) error {
    // Get authenticated user claims
    claims, ok := auth.WSAuthClaimsFromContext(ctx.Context())
    if !ok {
        return errors.New("no authentication claims found")
    }

    userID := claims.UserID()
    userRole := claims.Role()

    // Use resource-level permissions
    canModerate := claims.CanEdit("chat:moderation")
    canBroadcast := claims.CanCreate("chat:announcements")

    // Your WebSocket logic here
    for {
        messageType, message, err := ctx.ReadMessage()
        if err != nil {
            break
        }

        // Process message based on user permissions
        if canModerate && isModerateCommand(message) {
            handleModerationCommand(message, userID)
        } else {
            handleRegularMessage(message, userID)
        }
    }

    return nil
}
Advanced WebSocket Authentication

For more complex scenarios, you can create a custom token validator:

// Custom validator for additional WebSocket-specific logic
type CustomWSValidator struct {
    tokenService auth.TokenService
    logger       Logger
}

func (v *CustomWSValidator) Validate(tokenString string) (router.WSAuthClaims, error) {
    // Use go-auth token service for validation
    claims, err := v.tokenService.Validate(tokenString)
    if err != nil {
        v.logger.Error("WebSocket token validation failed", "error", err)
        return nil, err
    }

    // Additional WebSocket-specific validation
    if !claims.CanRead("websocket:access") {
        return nil, errors.New("insufficient permissions for WebSocket access")
    }

    // Return adapter for go-router compatibility
    return &auth.WSAuthClaimsAdapter{Claims: claims}, nil
}

// Use custom validator
validator := &CustomWSValidator{
    tokenService: authenticator.TokenService(),
    logger:       logger,
}

wsConfig := router.WSAuthConfig{TokenValidator: validator}
wsAuth := router.NewWSAuth(wsConfig)

Command Handlers

The library includes command handlers for common operations:

// Register user
registerUser := auth.RegisterUserHandler{repo: repositoryManager}
err := registerUser.Execute(ctx, auth.RegisterUserMessage{
    FirstName: "John",
    LastName:  "Doe",
    Email:     "[email protected]",
    Password:  "securepassword",
})

// Initialize password reset
initReset := auth.InitializePasswordResetHandler{repo: repositoryManager}
err := initReset.Execute(ctx, auth.InitializePasswordResetMessage{
    Email: "[email protected]",
    Stage: "show-reset",
})

Documentation

Overview

Package auth provides authentication primitives (JWT issuance, stateful repositories, HTTP helpers) plus lifecycle extension points for downstream admin workflows.

User lifecycle:

  • Users carry a UserStatus field that is persisted via Bun. Statuses cover pending, active, suspended, disabled, and archived flows so every product can opt into the same invariants.
  • UserStateMachine centralizes the transition graph, timestamp handling, hooks, and persistence. Embed the shared Users repository and invoke Transition with ActorRef metadata whenever an admin moves an account. Hook failures route through a configurable error handler so production services can translate panics into domain errors.

Activity sinks:

  • ActivitySink is a light-weight audit emitter used by Auther and the state machine to describe lifecycle, login, impersonation, and password reset events. Sinks run best-effort (errors are logged) so you can forward to a database or queue without blocking authentication.

Claims decoration:

  • ClaimsDecorator is invoked before JWTs are signed. Decorators may enrich extension fields such as resource roles or metadata while protected claims (sub, iss, aud, exp, etc.) remain immutable. Combine WithClaimsDecorator with ActivitySink to keep lifecycle state and issued tokens consistent.

Index

Constants

View Source
const (
	TextCodeInvalidCreds       = "INVALID_CREDENTIALS"
	TextCodeTooManyAttempts    = "TOO_MANY_ATTEMPTS"
	TextCodeSessionNotFound    = "SESSION_NOT_FOUND"
	TextCodeSessionDecodeError = "SESSION_DECODE_ERROR"
	TextCodeClaimsMappingError = "CLAIMS_MAPPING_ERROR"
	TextCodeDataParseError     = "DATA_PARSE_ERROR"
	TextCodeEmptyPassword      = "EMPTY_PASSWORD_NOT_ALLOWED"
	TextCodeTokenExpired       = "TOKEN_EXPIRED"
	TextCodeTokenMalformed     = "TOKEN_MALFORMED"
	TextCodeImmutableClaim     = "IMMUTABLE_CLAIM_MUTATION"
	TextCodeAccountSuspended   = "ACCOUNT_SUSPENDED"
	TextCodeAccountDisabled    = "ACCOUNT_DISABLED"
	TextCodeAccountArchived    = "ACCOUNT_ARCHIVED"
	TextCodeAccountPending     = "ACCOUNT_PENDING"
)
View Source
const (
	// ResetUnknownStatus is the unknown status
	ResetUnknownStatus = "unknown"
	// ResetRequestedStatus is the requested status
	ResetRequestedStatus = "requested"
	// ResetExpiredStatus is the expired status
	ResetExpiredStatus = "expired"
	// ResetChangedStatus is the changed status
	ResetChangedStatus = "changed"
)

Variables

View Source
var CoolDownPeriod = "24h"

CoolDownPeriod is the period in which we enforce a cool down

View Source
var ErrIdentityNotFound = errors.New("identity not found", errors.CategoryNotFound).
	WithCode(errors.CodeNotFound)

ErrIdentityNotFound is returned when an identity cannot be found.

View Source
var ErrImmutableClaimMutation = errors.New("claims decorator attempted to mutate immutable claim", errors.CategoryValidation).
	WithTextCode(TextCodeImmutableClaim).
	WithCode(errors.CodeBadRequest)

ErrImmutableClaimMutation is returned when a decorator tampers with protected claims.

View Source
var ErrInvalidTransition = goerrors.New("invalid user state transition", goerrors.CategoryValidation).
	WithTextCode(textCodeInvalidTransition).
	WithCode(goerrors.CodeBadRequest)

ErrInvalidTransition is returned when a requested status change is not allowed.

View Source
var ErrMismatchedHashAndPassword = errors.New("the credentials provided are invalid", errors.CategoryAuth).
	WithTextCode(TextCodeInvalidCreds).
	WithCode(errors.CodeUnauthorized)

ErrMismatchedHashAndPassword is returned on a failure to check a password hash. The message is generic to avoid leaking information.

View Source
var ErrNoEmptyString = errors.New("password can't be an empty string", errors.CategoryValidation).
	WithTextCode(TextCodeEmptyPassword).
	WithCode(errors.CodeBadRequest)

ErrNoEmptyString is returned when an empty string is provided for a value that must not be empty, like a password.

View Source
var ErrTerminalState = goerrors.New("user state is terminal", goerrors.CategoryConflict).
	WithTextCode(textCodeTerminalState).
	WithCode(goerrors.CodeConflict)

ErrTerminalState is returned when attempting to move away from a terminal status (e.g., archived).

View Source
var ErrTokenExpired = errors.New("token is expired", errors.CategoryAuth).
	WithTextCode(TextCodeTokenExpired).
	WithCode(errors.CodeUnauthorized)

ErrTokenExpired is returned when a JWT token has expired.

View Source
var ErrTokenMalformed = errors.New("token is malformed", errors.CategoryAuth).
	WithTextCode(TextCodeTokenMalformed).
	WithCode(errors.CodeBadRequest)

ErrTokenMalformed is returned when a JWT token is malformed.

View Source
var ErrTooManyLoginAttempts = errors.New("too many login attempts, please try again later", errors.CategoryRateLimit).
	WithTextCode(TextCodeTooManyAttempts).
	WithCode(errors.CodeTooManyRequests)

ErrTooManyLoginAttempts indicates the user has tried to log in too many times.

View Source
var ErrUnableToDecodeSession = errors.New("unable to decode session", errors.CategoryAuth).
	WithTextCode(TextCodeSessionDecodeError).
	WithCode(errors.CodeUnauthorized)

ErrUnableToDecodeSession is returned when a session token (e.g., JWT) cannot be decoded or parsed.

View Source
var ErrUnableToFindSession = errors.New("unable to find session", errors.CategoryAuth).
	WithTextCode(TextCodeSessionNotFound).
	WithCode(errors.CodeUnauthorized)

ErrUnableToFindSession is returned when a session (e.g., a cookie) is missing from a request.

View Source
var ErrUnableToMapClaims = errors.New("unable to map claims from token", errors.CategoryAuth).
	WithTextCode(TextCodeClaimsMappingError).
	WithCode(errors.CodeUnauthorized)

ErrUnableToMapClaims is returned when claims cannot be extracted from a parsed token.

View Source
var ErrUnableToParseData = errors.New("unable to parse authentication data", errors.CategoryBadInput).
	WithTextCode(TextCodeDataParseError).
	WithCode(errors.CodeBadRequest)

ErrUnableToParseData is returned on a generic data parsing error within the auth context.

View Source
var ErrUserArchived = errors.New("user account is archived", errors.CategoryAuth).
	WithTextCode(TextCodeAccountArchived).
	WithCode(errors.CodeForbidden)

ErrUserArchived is returned when an account is archived.

View Source
var ErrUserDisabled = errors.New("user account is disabled", errors.CategoryAuth).
	WithTextCode(TextCodeAccountDisabled).
	WithCode(errors.CodeForbidden)

ErrUserDisabled is returned when an account is disabled.

View Source
var ErrUserPending = errors.New("user account is pending activation", errors.CategoryAuth).
	WithTextCode(TextCodeAccountPending).
	WithCode(errors.CodeForbidden)

ErrUserPending is returned when an account is pending activation.

View Source
var ErrUserSuspended = errors.New("user account is suspended", errors.CategoryAuth).
	WithTextCode(TextCodeAccountSuspended).
	WithCode(errors.CodeForbidden)

ErrUserSuspended is returned when an account is suspended.

View Source
var MaxLoginAttempts = 5

MaxLoginAttempts is the maximun number of attempts a user gets in a period

View Source
var ResetUserPasswordSQL = `` /* 148-byte string literal not displayed */
View Source
var TemplateUserKey = "current_user"

Functions

func Can added in v0.7.0

func Can(ctx context.Context, resource, permission string) bool

Can is a convenience function to check permissions directly from the standard context Use CanFromRouter for router-based contexts.

func CanFromRouter added in v0.7.0

func CanFromRouter(ctx router.Context, resource, permission string) bool

CanFromRouter is a convenience function to check permissions directly from the router context

func ComparePasswordAndHash

func ComparePasswordAndHash(password, hash string) error

ComparePasswordAndHash will validate the given cleartext password matches the hashed password

func ContextEnricherAdapter added in v0.23.0

func ContextEnricherAdapter(c context.Context, claims jwtware.AuthClaims) context.Context

ContextEnricherAdapter adapts jwtware.AuthClaims to auth.AuthClaims and stores claims + actor context in the standard context for downstream guard usage.

func GetMigrationsFS

func GetMigrationsFS() embed.FS

GetMigrationsFS returns the migration files for this package

func GetTemplateUser added in v0.10.0

func GetTemplateUser(ctx router.Context, userKey string) (any, bool)

GetTemplateUser is a convenience function to extract user data from router context for template usage. It returns the user object and a boolean indicating if it was found.

Usage:

if user, ok := auth.GetTemplateUser(ctx, auth.TemplateUserKey); ok {
	// Use user in template data
	data["user"] = user
}

func HasUserUUID added in v0.24.0

func HasUserUUID(session Session) bool

HasUserUUID reports whether Session.GetUserUUID will succeed.

func HashPassword

func HashPassword(password string) (string, error)

HashPassword will generate a password hash

func IsMalformedError

func IsMalformedError(err error) bool

func IsOutsideThresholdPeriod

func IsOutsideThresholdPeriod(t time.Time, pattern string) (bool, error)

IsOutsideThresholdPeriod is the negation of IsWithinThresholdPeriod

func IsTokenExpiredError

func IsTokenExpiredError(err error) bool

func IsWithinThresholdPeriod

func IsWithinThresholdPeriod(t time.Time, pattern string) (bool, error)

IsWithinThresholdPeriod checks if the given time is within the threshold

func MergeTemplateData added in v0.16.0

func MergeTemplateData(ctx router.Context, data router.ViewContext) router.ViewContext

MergeTemplateData ensures every render includes the latest template helpers (current user, CSRF helpers, etc.) merged with the provided view context. It evaluates helper closures that return strings or template.HTML so request- scoped helpers are always materialized before hitting the renderer.

func NewPasswordResetsRepository

func NewPasswordResetsRepository(db *bun.DB) repository.Repository[*PasswordReset]

func RandomPasswordHash

func RandomPasswordHash() string

RandomPasswordHash is a temporary password

func RegisterAuthRoutes

func RegisterAuthRoutes[T any](app router.Router[T], opts ...AuthControllerOption)

func RegisterValidationListeners added in v0.23.0

func RegisterValidationListeners(cfg *jwtware.Config, listeners ...ValidationListener)

RegisterValidationListeners appends listeners to a jwtware.Config in a safe, reusable way.

func TemplateHelpers added in v0.10.0

func TemplateHelpers() map[string]any

TemplateHelpers returns a map of helper functions and data that can be used with go-template's WithGlobalData option for authentication-related template functionality.

Usage:

renderer, err := template.NewRenderer(
    template.WithBaseDir("./templates"),
    template.WithGlobalData(auth.TemplateHelpers()),
)

In templates, you can then use:

{% if current_user %}
{% if current_user|has_role:"admin" %}
{% if current_user|can_create:"posts" %}
{{ csrf_field }}
{{ csrf_token }}

func TemplateHelpersWithRouter added in v0.10.0

func TemplateHelpersWithRouter(ctx router.Context, userKey string) map[string]any

TemplateHelpersWithRouter returns template helpers with user data extracted from router context. This is useful for automatically injecting the current user from JWT middleware context. It also includes CSRF token helpers when a CSRF token is available in the context.

Usage:

// In your route handler
globalData := auth.TemplateHelpersWithRouter(ctx, auth.TemplateUserKey)
// Merge with request-specific data and render template

Or with a reusable render helper:

func renderTemplate(ctx router.Context, name string, data map[string]any) (string, error) {
	globalData := auth.TemplateHelpersWithRouter(ctx, auth.TemplateUserKey)
	// Create renderer with current context or use a cached one
	return renderer.RenderTemplate(name, data)
}

func TemplateHelpersWithUser added in v0.10.0

func TemplateHelpersWithUser(user *User) map[string]any

TemplateHelpersWithUser returns template helpers with a specific user set as current_user. This is useful when you want to inject the current user directly into the global context.

Usage:

currentUser := getCurrentUser(ctx)
renderer, err := template.NewRenderer(
    template.WithBaseDir("./templates"),
    template.WithGlobalData(auth.TemplateHelpersWithUser(currentUser)),
)

func ValidateStringEquals

func ValidateStringEquals(str string) validation.RuleFunc

ValidateStringEquals will check that both values match

func WithActorContext added in v0.15.0

func WithActorContext(ctx context.Context, actor *ActorContext) context.Context

WithActorContext stores the ActorContext in the provided context.

func WithClaimsContext added in v0.7.0

func WithClaimsContext(r context.Context, claims AuthClaims) context.Context

WithClaimsContext sets the AuthClaims in the given context

func WithContext added in v0.4.0

func WithContext(r context.Context, user *User) context.Context

WithContext sets the User in the given context

Types

type AccountRegistrerer

type AccountRegistrerer interface {
	RegisterUser(ctx context.Context, email, username, password string) (*User, error)
}

AccountRegistrerer is the interface we need to handle new user registrations

type AccountVerificationHandler

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

func (*AccountVerificationHandler) Execute

type AccountVerificationMesage

type AccountVerificationMesage struct {
	Session    string `json:"session" example:"350399bc-c095-4bdc-a59c-3352d44848e4" doc:"Reset password session token"`
	OnResponse func(a *AccountVerificationResponse)
}

type AccountVerificationResponse

type AccountVerificationResponse struct {
	Stage    string   `json:"stage" example:"Rone" doc:"Customer last name."`
	Redirect string   `json:"redirect" example:"Rone" doc:"Customer last name."`
	Expired  bool     `json:"expired" example:"true" doc:"Has the request expired?"`
	Found    bool     `json:"found" example:"true" doc:"Has the request been found?"`
	Errors   []string `json:"errors" example:"['invalid username']" doc:"Error messages."`
}

type ActivityEvent added in v0.14.0

type ActivityEvent struct {
	EventType  ActivityEventType
	Actor      ActorRef
	UserID     string
	FromStatus UserStatus
	ToStatus   UserStatus
	Metadata   map[string]any
	OccurredAt time.Time
}

ActivityEvent captures audit-friendly information about an action.

type ActivityEventType added in v0.14.0

type ActivityEventType string

ActivityEventType enumerates supported activity categories.

const (
	ActivityEventUserStatusChanged    ActivityEventType = "user.status.changed"
	ActivityEventLoginSuccess         ActivityEventType = "auth.login.success"
	ActivityEventLoginFailure         ActivityEventType = "auth.login.failure"
	ActivityEventSocialLogin          ActivityEventType = "auth.social.login"
	ActivityEventImpersonationSuccess ActivityEventType = "auth.impersonation.success"
	ActivityEventImpersonationFailure ActivityEventType = "auth.impersonation.failure"
	ActivityEventPasswordResetSuccess ActivityEventType = "auth.password.reset"
)

type ActivitySink added in v0.14.0

type ActivitySink interface {
	Record(ctx context.Context, event ActivityEvent) error
}

ActivitySink consumes activity events for auditing/telemetry purposes.

type ActivitySinkFunc added in v0.14.0

type ActivitySinkFunc func(ctx context.Context, event ActivityEvent) error

ActivitySinkFunc adapts a function to the ActivitySink interface.

func (ActivitySinkFunc) Record added in v0.14.0

func (f ActivitySinkFunc) Record(ctx context.Context, event ActivityEvent) error

Record implements ActivitySink.

type ActorContext added in v0.15.0

type ActorContext struct {
	ActorID        string
	Subject        string
	Role           string
	ResourceRoles  map[string]string
	TenantID       string
	OrganizationID string
	Metadata       map[string]any
	ImpersonatorID string
	IsImpersonated bool
}

ActorContext captures normalized actor metadata for downstream policy/guard layers.

func ActorContextFromClaims added in v0.15.0

func ActorContextFromClaims(claims AuthClaims) *ActorContext

ActorContextFromClaims normalizes actor metadata from AuthClaims into an ActorContext structure.

func ActorFromContext added in v0.15.0

func ActorFromContext(ctx context.Context) (*ActorContext, bool)

ActorFromContext extracts the ActorContext from the standard context.

func ActorFromRouterContext added in v0.15.0

func ActorFromRouterContext(ctx router.Context) (*ActorContext, bool)

ActorFromRouterContext extracts the ActorContext from a router context by reading the underlying standard context.

type ActorRef added in v0.14.0

type ActorRef struct {
	ID   string
	Type string
}

ActorRef identifies who/what triggered a transition.

type AuthClaims added in v0.7.0

type AuthClaims interface {
	Subject() string
	UserID() string
	Role() string
	CanRead(resource string) bool
	CanEdit(resource string) bool
	CanCreate(resource string) bool
	CanDelete(resource string) bool
	HasRole(role string) bool
	IsAtLeast(minRole string) bool
	Expires() time.Time
	IssuedAt() time.Time
}

AuthClaims represents structured JWT claims with enhanced permission checking

func GetClaims added in v0.7.0

func GetClaims(ctx context.Context) (AuthClaims, bool)

GetClaims extracts the AuthClaims from the standard context

func GetRouterClaims added in v0.7.0

func GetRouterClaims(ctx router.Context, key string) (AuthClaims, bool)

GetRouterClaims extracts the AuthClaims from the router context

func WSAuthClaimsFromContext added in v0.9.0

func WSAuthClaimsFromContext(ctx context.Context) (AuthClaims, bool)

WSAuthClaimsFromContext is a convenience function to retrieve auth claims from WebSocket context. It returns the underlying go-auth AuthClaims for easier access to go-auth specific functionality.

type AuthController

type AuthController struct {
	Debug            bool
	Logger           Logger
	Repo             RepositoryManager
	Routes           *AuthControllerRoutes
	Views            *AuthControllerViews
	Auther           HTTPAuthenticator
	ErrorHandler     router.ErrorHandler
	RegisterRedirect string
	UseHashID        bool
	// contains filtered or unexported fields
}

func NewAuthController

func NewAuthController(opts ...AuthControllerOption) *AuthController

func (*AuthController) LogOut

func (a *AuthController) LogOut(ctx router.Context) error

func (*AuthController) LoginPost

func (a *AuthController) LoginPost(ctx router.Context) error

func (*AuthController) LoginShow

func (a *AuthController) LoginShow(ctx router.Context) error

func (*AuthController) PasswordResetExecute

func (a *AuthController) PasswordResetExecute(ctx router.Context) error

func (*AuthController) PasswordResetForm

func (a *AuthController) PasswordResetForm(ctx router.Context) error

func (*AuthController) PasswordResetGet

func (a *AuthController) PasswordResetGet(ctx router.Context) error

func (*AuthController) PasswordResetPost

func (a *AuthController) PasswordResetPost(ctx router.Context) error

func (*AuthController) RegistrationCreate

func (a *AuthController) RegistrationCreate(ctx router.Context) error

func (*AuthController) RegistrationShow

func (a *AuthController) RegistrationShow(ctx router.Context) error

func (*AuthController) WithLogger added in v0.4.0

func (a *AuthController) WithLogger(l Logger) *AuthController

type AuthControllerOption

type AuthControllerOption func(*AuthController) *AuthController

func WithAuthControllerActivitySink added in v0.14.0

func WithAuthControllerActivitySink(sink ActivitySink) AuthControllerOption

func WithAuthControllerRedirect added in v0.3.0

func WithAuthControllerRedirect(r string) AuthControllerOption

func WithAuthControllerRoutes added in v0.3.0

func WithAuthControllerRoutes(r *AuthControllerRoutes) AuthControllerOption

func WithAuthControllerUseHashID added in v0.3.0

func WithAuthControllerUseHashID(v bool) AuthControllerOption

func WithAuthControllerViews added in v0.3.0

func WithAuthControllerViews(v *AuthControllerViews) AuthControllerOption

func WithControllerLogger added in v0.1.1

func WithControllerLogger(logger Logger) AuthControllerOption

func WithErrorHandler added in v0.3.0

func WithErrorHandler(errHandler router.ErrorHandler) AuthControllerOption

type AuthControllerRoutes

type AuthControllerRoutes struct {
	Login         string
	Logout        string
	Register      string
	PasswordReset string
}

type AuthControllerViews

type AuthControllerViews struct {
	Login         string
	Logout        string
	Register      string
	PasswordReset string
}

type Authenticator

type Authenticator interface {
	Login(ctx context.Context, identifier, password string) (string, error)
	Impersonate(ctx context.Context, identifier string) (string, error)
	SessionFromToken(token string) (Session, error)
	IdentityFromSession(ctx context.Context, session Session) (Identity, error)
	TokenService() TokenService
}

Authenticator holds methods to deal with authentication

type Auther

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

func NewAuthenticator

func NewAuthenticator(provider IdentityProvider, opts Config) *Auther

NewAuthenticator returns a new Authenticator

func (*Auther) IdentityFromSession

func (s *Auther) IdentityFromSession(ctx context.Context, session Session) (Identity, error)

func (*Auther) Impersonate

func (s *Auther) Impersonate(ctx context.Context, identifier string) (string, error)

func (*Auther) Login

func (s *Auther) Login(ctx context.Context, identifier, password string) (string, error)

func (*Auther) NewWSAuthMiddleware added in v0.9.0

func (a *Auther) NewWSAuthMiddleware(config ...router.WSAuthConfig) router.WebSocketMiddleware

NewWSAuthMiddleware creates a fully configured WebSocket authentication middleware using the go-auth TokenService. This is a convenience function for go-auth users.

func (Auther) SessionFromToken

func (s Auther) SessionFromToken(raw string) (Session, error)

func (*Auther) TokenService added in v0.11.0

func (s *Auther) TokenService() TokenService

TokenService returns the TokenService instance used by this Authenticator

func (*Auther) WithActivitySink added in v0.14.0

func (s *Auther) WithActivitySink(sink ActivitySink) *Auther

WithActivitySink configures an ActivitySink for emitting auth events.

func (*Auther) WithClaimsDecorator added in v0.14.0

func (s *Auther) WithClaimsDecorator(decorator ClaimsDecorator) *Auther

WithClaimsDecorator configures a ClaimsDecorator for enriching JWTs.

func (*Auther) WithLogger added in v0.1.1

func (s *Auther) WithLogger(logger Logger) *Auther

func (*Auther) WithResourceRoleProvider added in v0.7.0

func (s *Auther) WithResourceRoleProvider(provider ResourceRoleProvider) *Auther

WithResourceRoleProvider sets a custom ResourceRoleProvider for the Auther. This enables resource-level permissions in JWT tokens.

func (*Auther) WithTokenValidator added in v0.24.0

func (s *Auther) WithTokenValidator(validator TokenValidator) *Auther

WithTokenValidator sets a custom token validator for externally issued tokens.

type ClaimsDecorator added in v0.14.0

type ClaimsDecorator interface {
	Decorate(ctx context.Context, identity Identity, claims *JWTClaims) error
}

ClaimsDecorator can mutate allowed JWT claim extensions before a token is signed. Implementations may only touch extension fields (e.g. Resources, Metadata) and must leave registered/identity claims untouched so core auth semantics stay stable.

type ClaimsDecoratorFunc added in v0.14.0

type ClaimsDecoratorFunc func(ctx context.Context, identity Identity, claims *JWTClaims) error

ClaimsDecoratorFunc adapts a function into a ClaimsDecorator.

func (ClaimsDecoratorFunc) Decorate added in v0.14.0

func (f ClaimsDecoratorFunc) Decorate(ctx context.Context, identity Identity, claims *JWTClaims) error

Decorate satisfies the ClaimsDecorator interface.

type Config

type Config interface {
	GetSigningKey() string
	GetSigningMethod() string
	GetContextKey() string
	GetTokenExpiration() int
	GetExtendedTokenDuration() int
	GetTokenLookup() string
	GetAuthScheme() string
	GetIssuer() string
	GetAudience() []string
	GetRejectedRouteKey() string
	GetRejectedRouteDefault() string
}

Config holds auth options

type FinalizePasswordResetHandler

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

func NewFinalizePasswordResetHandler added in v0.14.0

func NewFinalizePasswordResetHandler(repo RepositoryManager) *FinalizePasswordResetHandler

NewFinalizePasswordResetHandler creates a handler with sane defaults.

func (*FinalizePasswordResetHandler) Execute

func (*FinalizePasswordResetHandler) WithActivitySink added in v0.14.0

WithActivitySink sets the sink used to emit password reset events.

func (*FinalizePasswordResetHandler) WithLogger added in v0.14.0

WithLogger overrides the logger used by the handler.

type FinalizePasswordResetMesasge

type FinalizePasswordResetMesasge struct {
	Session  string `json:"session" example:"350399bc-c095-4bdc-a59c-3352d44848e4" doc:"Reset password session token"`
	Password string `json:"password" example:"some_secret_word" doc:"Password"`
}

type HTTPAuthenticator

type HTTPAuthenticator interface {
	Middleware
	Login(c router.Context, payload LoginPayload) error
	Logout(c router.Context)
	SetRedirect(c router.Context)
	GetRedirect(c router.Context, def ...string) string
	GetRedirectOrDefault(c router.Context) string
	MakeClientRouteAuthErrorHandler(optionalAuth bool) func(c router.Context, err error) error
}

type HookErrorHandler added in v0.15.0

type HookErrorHandler func(ctx context.Context, phase TransitionHookPhase, err error, tc TransitionContext) error

HookErrorHandler handles errors surfaced by transition hooks.

type Identity

type Identity interface {
	ID() string
	Username() string
	Email() string
	Role() string
}

Identity holds the attributes of an identity

func NewIdentityFromUser added in v0.24.0

func NewIdentityFromUser(user *User) Identity

NewIdentityFromUser returns an Identity adapter for the provided user.

type IdentityProvider

type IdentityProvider interface {
	VerifyIdentity(ctx context.Context, identifier, password string) (Identity, error)
	FindIdentityByIdentifier(ctx context.Context, identifier string) (Identity, error)
}

IdentityProvider ensure we have a store to retrieve auth identity

type InitializePasswordResetHandler

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

func (*InitializePasswordResetHandler) Execute

type InitializePasswordResetMessage

type InitializePasswordResetMessage struct {
	Stage      string `json:"stage" example:"Rone" doc:"Customer last name."`
	Session    string `json:"session" example:"350399bc-c095-4bdc-a59c-3352d44848e4" doc:"Reset password session token"`
	Email      string `json:"email" example:"[email protected]" doc:"Customer email."`
	OnResponse func(resp *InitializePasswordResetResponse)
}

func (InitializePasswordResetMessage) Type

type InitializePasswordResetResponse

type InitializePasswordResetResponse struct {
	Reset   *PasswordReset
	Stage   string
	Success bool
}

type JWTClaims added in v0.7.0

type JWTClaims struct {
	jwt.RegisteredClaims
	UID       string            `json:"uid,omitempty"`
	UserRole  string            `json:"role,omitempty"`
	Resources map[string]string `json:"res,omitempty"`      // resource -> role mapping
	Metadata  map[string]any    `json:"metadata,omitempty"` // extension payload
}

JWTClaims is the concrete implementation of AuthClaims

func (*JWTClaims) CanCreate added in v0.7.0

func (c *JWTClaims) CanCreate(resource string) bool

CanCreate checks if the user can create a specific resource

func (*JWTClaims) CanDelete added in v0.7.0

func (c *JWTClaims) CanDelete(resource string) bool

CanDelete checks if the user can delete a specific resource

func (*JWTClaims) CanEdit added in v0.7.0

func (c *JWTClaims) CanEdit(resource string) bool

CanEdit checks if the user can edit a specific resource

func (*JWTClaims) CanRead added in v0.7.0

func (c *JWTClaims) CanRead(resource string) bool

CanRead checks if the user can read a specific resource

func (*JWTClaims) ClaimsMetadata added in v0.15.0

func (c *JWTClaims) ClaimsMetadata() map[string]any

ClaimsMetadata exposes metadata extensions for optional context enrichment.

func (*JWTClaims) Expires added in v0.7.0

func (c *JWTClaims) Expires() time.Time

Expires returns the expiration time

func (*JWTClaims) HasRole added in v0.7.0

func (c *JWTClaims) HasRole(role string) bool

HasRole checks if the user has a specific role (either global or for any resource)

func (*JWTClaims) IsAtLeast added in v0.7.0

func (c *JWTClaims) IsAtLeast(minRole string) bool

IsAtLeast checks if the user's role is at least the minimum required role

func (*JWTClaims) IssuedAt added in v0.7.0

func (c *JWTClaims) IssuedAt() time.Time

IssuedAt returns the issued at time

func (*JWTClaims) ResourceRoles added in v0.15.0

func (c *JWTClaims) ResourceRoles() map[string]string

ResourceRoles exposes resource-specific roles for optional context enrichment.

func (*JWTClaims) Role added in v0.7.0

func (c *JWTClaims) Role() string

Role returns the global role

func (*JWTClaims) Subject added in v0.7.0

func (c *JWTClaims) Subject() string

Subject returns the subject claim

func (*JWTClaims) UserID added in v0.7.0

func (c *JWTClaims) UserID() string

UserID returns the user ID

type Logger

type Logger interface {
	Debug(format string, args ...any)
	Info(format string, args ...any)
	Warn(format string, args ...any)
	Error(format string, args ...any)
}

type LoginPayload

type LoginPayload interface {
	GetIdentifier() string
	GetPassword() string
	GetExtendedSession() bool
}

type LoginRequest

type LoginRequest struct {
	Identifier string `form:"identifier" json:"identifier"`
	Password   string `form:"password" json:"password"`
	RememberMe bool   `form:"remember_me" json:"remember_me"`
}

LoginRequest payload

func (LoginRequest) GetExtendedSession

func (r LoginRequest) GetExtendedSession() bool

GetExtendedSession will return the password

func (LoginRequest) GetIdentifier

func (r LoginRequest) GetIdentifier() string

GetIdentifier returns the identifier

func (LoginRequest) GetPassword

func (r LoginRequest) GetPassword() string

GetPassword will return the password

func (LoginRequest) Validate

func (r LoginRequest) Validate() *errors.Error

Validate will run validation rules

type Middleware

type Middleware interface {
	Impersonate(c router.Context, identifier string) error
	ProtectedRoute(cfg Config, errorHandler func(router.Context, error) error) router.MiddlewareFunc
}

type MultiTokenValidator added in v0.24.0

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

MultiTokenValidator tries validators in order until one succeeds. It treats ErrTokenMalformed as "try next" and returns the last malformed error if all validators fail.

func NewMultiTokenValidator added in v0.24.0

func NewMultiTokenValidator(validators ...TokenValidator) *MultiTokenValidator

NewMultiTokenValidator filters nil validators and returns a composite validator.

func (*MultiTokenValidator) Validate added in v0.24.0

func (m *MultiTokenValidator) Validate(tokenString string) (AuthClaims, error)

Validate satisfies the TokenValidator interface.

type PasswordAuthenticator

type PasswordAuthenticator interface {
	HashPassword(password string) (string, error)
	ComparePasswordAndHash(password, hash string) error
}

PasswordAuthenticator authenticates passwords

type PasswordReset

type PasswordReset struct {
	bun.BaseModel `bun:"table:password_reset,alias:pwdr"`
	ID            uuid.UUID  `bun:"id,pk,nullzero,type:uuid" json:"id,omitempty"`
	UserID        *uuid.UUID `bun:"user_id,notnull" json:"user_id,omitempty"`
	User          *User      `bun:"rel:has-one,join:user_id=id" json:"user,omitempty"`
	Status        string     `bun:"status,notnull" json:"status,omitempty"`
	Email         string     `bun:"email,notnull" json:"email,omitempty"`
	DeletedAt     *time.Time `bun:"deleted_at,soft_delete,nullzero" json:"deleted_at,omitempty"`
	ResetedAt     *time.Time `bun:"reseted_at,nullzero" json:"reseted_at,omitempty"`
	CreatedAt     *time.Time `bun:"created_at,nullzero,default:current_timestamp" json:"created_at,omitempty"`
	UpdatedAt     *time.Time `bun:"updated_at,nullzero,default:current_timestamp" json:"updated_at,omitempty"`
}

PasswordReset is the user model

func MarkPasswordAsReseted

func MarkPasswordAsReseted(id uuid.UUID) *PasswordReset

MarkPasswordAsReseted will create a new instance

type PasswordResetRequestPayload

type PasswordResetRequestPayload struct {
	Email string `form:"email" json:"email"`
	Stage string `form:"stage" json:"stage"`
}

PasswordResetRequestPayload holds values for password reset

func (PasswordResetRequestPayload) Validate

Validate will validate the payload

type PasswordResetStep

type PasswordResetStep = string

PasswordResetStep step on password reset

const (
	// ResetUnknown is the unknown status
	ResetUnknown PasswordResetStep = "unknown"
	// ResetInit is the initial step
	ResetInit PasswordResetStep = "show-reset"
	//AccountVerification notifiction sent
	AccountVerification PasswordResetStep = "email-sent"
	// ChangingPassword user will change password
	ChangingPassword PasswordResetStep = "change-password"
	// ChangeFinalized processing change
	ChangeFinalized PasswordResetStep = "password-changed"
)

type PasswordResetVerifyPayload

type PasswordResetVerifyPayload struct {
	Stage           string `form:"stage" json:"stage"`
	Password        string `form:"password" json:"password"`
	ConfirmPassword string `form:"confirm_password" json:"confirm_password"`
}

PasswordResetVerifyPayload holds values for password reset

func (PasswordResetVerifyPayload) Validate

func (r PasswordResetVerifyPayload) Validate() *errors.Error

Validate will validate the payload

type RegisterUserHandler

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

Test handlers

func (*RegisterUserHandler) Execute

type RegisterUserMessage

type RegisterUserMessage struct {
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	Username  string `json:"username"`
	Email     string `json:"email"`
	Phone     string `json:"phone"`
	Role      string `json:"role"`
	Password  string `json:"password"`
	UseHashid bool
}

func (RegisterUserMessage) Type

func (e RegisterUserMessage) Type() string

type RegistrationCreatePayload

type RegistrationCreatePayload struct {
	FirstName       string `form:"first_name" json:"first_name"`
	LastName        string `form:"last_name" json:"last_name"`
	Email           string `form:"email" json:"email"`
	Phone           string `form:"phone_number" json:"phone_number"`
	Password        string `form:"password" json:"password"`
	ConfirmPassword string `form:"confirm_password" json:"confirm_password"`
}

RegistrationCreatePayload is the form paylaod

func (RegistrationCreatePayload) Validate

func (r RegistrationCreatePayload) Validate() *errors.Error

Validate will validate the payload

type RepositoryManager

type RepositoryManager interface {
	repository.Validator
	repository.TransactionManager
	Users() Users
	PasswordResets() repository.Repository[*PasswordReset]
}

RepositoryManager exposes all repositories

func NewRepositoryManager

func NewRepositoryManager(db *bun.DB) RepositoryManager

type ResourceRoleProvider added in v0.7.0

type ResourceRoleProvider interface {
	FindResourceRoles(ctx context.Context, identity Identity) (map[string]string, error)
}

ResourceRoleProvider is an optional interface for fetching resource-specific roles. If provided to an Auther, it will be used to embed fine-grained permissions into the JWT, upgrading it to a structured claims format.

type RoleCapableSession added in v0.7.0

type RoleCapableSession interface {
	Session // Embed the existing Session interface

	// CanRead checks if the role can read a specific resource
	CanRead(resource string) bool

	// CanEdit checks if the role can edit a specific resource
	CanEdit(resource string) bool

	// CanCreate checks if the role can create a specific resource
	CanCreate(resource string) bool

	// CanDelete checks if the role can delete a specific resource
	CanDelete(resource string) bool

	// HasRole checks if the user has a specific role
	HasRole(role string) bool

	// IsAtLeast checks if the user's role is at least the minimum required role
	IsAtLeast(minRole UserRole) bool
}

RoleCapableSession extends Session with role-based access control capabilities

type RoleValidator added in v0.7.0

type RoleValidator interface {
	// CanRead checks if the role can read a specific resource
	CanRead(resource string) bool

	// CanEdit checks if the role can edit a specific resource
	CanEdit(resource string) bool

	// CanCreate checks if the role can create a specific resource
	CanCreate(resource string) bool

	// CanDelete checks if the role can delete a specific resource
	CanDelete(resource string) bool

	// HasRole checks if the user has a specific role
	HasRole(role string) bool

	// IsAtLeast checks if the user's role is at least the minimum required role
	IsAtLeast(minRole UserRole) bool
}

RoleValidator defines the interface for role-based access control validation

type RouteAuthenticator

type RouteAuthenticator struct {
	AuthErrorHandler func(c router.Context, err error) error // TODO: make functions
	ErrorHandler     func(c router.Context, err error) error // TODO: make functions
	// contains filtered or unexported fields
}

func NewHTTPAuthenticator

func NewHTTPAuthenticator(auther Authenticator, cfg Config) (*RouteAuthenticator, error)

func (RouteAuthenticator) GetCookieDuration

func (a RouteAuthenticator) GetCookieDuration() time.Duration

func (RouteAuthenticator) GetExtendedCookieDuration

func (a RouteAuthenticator) GetExtendedCookieDuration() time.Duration

func (*RouteAuthenticator) GetRedirect

func (a *RouteAuthenticator) GetRedirect(ctx router.Context, def ...string) string

func (*RouteAuthenticator) GetRedirectOrDefault

func (a *RouteAuthenticator) GetRedirectOrDefault(ctx router.Context) string

func (*RouteAuthenticator) Impersonate

func (a *RouteAuthenticator) Impersonate(c router.Context, identifier string) error

func (*RouteAuthenticator) Login

func (a *RouteAuthenticator) Login(ctx router.Context, payload LoginPayload) error

func (*RouteAuthenticator) Logout

func (a *RouteAuthenticator) Logout(ctx router.Context)

func (*RouteAuthenticator) MakeClientRouteAuthErrorHandler

func (a *RouteAuthenticator) MakeClientRouteAuthErrorHandler(optional bool) func(router.Context, error) error

func (*RouteAuthenticator) ProtectedRoute

func (a *RouteAuthenticator) ProtectedRoute(cfg Config, errorHandler func(router.Context, error) error) router.MiddlewareFunc

func (*RouteAuthenticator) SetRedirect

func (a *RouteAuthenticator) SetRedirect(ctx router.Context)

func (*RouteAuthenticator) WithLogger added in v0.4.0

func (a *RouteAuthenticator) WithLogger(l Logger) *RouteAuthenticator

func (*RouteAuthenticator) WithValidationListeners added in v0.15.0

func (a *RouteAuthenticator) WithValidationListeners(listeners ...ValidationListener) *RouteAuthenticator

WithValidationListeners registers callbacks invoked immediately after token validation.

type Session

type Session interface {
	GetUserID() string
	GetUserUUID() (uuid.UUID, error)
	GetAudience() []string
	GetIssuer() string
	GetIssuedAt() *time.Time
	GetData() map[string]any
}

Session holds attributes that are part of an auth session

type SessionObject

type SessionObject struct {
	UserID         string         `json:"user_id,omitempty"`
	Audience       []string       `json:"audience,omitempty"`
	Issuer         string         `json:"issuer,omitempty"`
	IssuedAt       *time.Time     `json:"issued_at,omitempty"`
	ExpirationDate *time.Time     `json:"expiration_date,omitempty"`
	Data           map[string]any `json:"data,omitempty"`
}

func GetRouterSession

func GetRouterSession(c router.Context, key string) (*SessionObject, error)

func (*SessionObject) CanCreate added in v0.7.0

func (s *SessionObject) CanCreate(resource string) bool

CanCreate checks if the role can create a specific resource

func (*SessionObject) CanDelete added in v0.7.0

func (s *SessionObject) CanDelete(resource string) bool

CanDelete checks if the role can delete a specific resource

func (*SessionObject) CanEdit added in v0.7.0

func (s *SessionObject) CanEdit(resource string) bool

CanEdit checks if the role can edit a specific resource

func (*SessionObject) CanRead added in v0.7.0

func (s *SessionObject) CanRead(resource string) bool

CanRead checks if the role can read a specific resource

func (*SessionObject) GetAudience

func (s *SessionObject) GetAudience() []string

func (*SessionObject) GetData

func (s *SessionObject) GetData() map[string]any

func (*SessionObject) GetIssuedAt

func (s *SessionObject) GetIssuedAt() *time.Time

func (*SessionObject) GetIssuer

func (s *SessionObject) GetIssuer() string

func (*SessionObject) GetUserID

func (s *SessionObject) GetUserID() string

func (*SessionObject) GetUserUUID

func (s *SessionObject) GetUserUUID() (uuid.UUID, error)

func (*SessionObject) HasRole added in v0.7.0

func (s *SessionObject) HasRole(role string) bool

HasRole checks if the user has a specific role

func (*SessionObject) IsAtLeast added in v0.7.0

func (s *SessionObject) IsAtLeast(minRole UserRole) bool

IsAtLeast checks if the user's role is at least the minimum required role

func (SessionObject) String

func (s SessionObject) String() string

TODO: enable only in development!

type StateMachineOption added in v0.14.0

type StateMachineOption func(*userStateMachine)

StateMachineOption customizes state machine construction.

func WithStateMachineActivitySink added in v0.14.0

func WithStateMachineActivitySink(sink ActivitySink) StateMachineOption

WithStateMachineActivitySink sets the ActivitySink used to publish lifecycle events.

func WithStateMachineClock added in v0.14.0

func WithStateMachineClock(clock func() time.Time) StateMachineOption

WithStateMachineClock injects a custom clock (useful for tests).

func WithStateMachineHookErrorHandler added in v0.15.0

func WithStateMachineHookErrorHandler(handler HookErrorHandler) StateMachineOption

WithStateMachineHookErrorHandler overrides how hook failures are propagated. Provide a handler to convert hook errors into domain-specific responses, otherwise the default handler panics with guidance for developers.

func WithStateMachineLogger added in v0.14.0

func WithStateMachineLogger(logger Logger) StateMachineOption

WithStateMachineLogger overrides the logger used for sink failures.

type StatusUpdateOption added in v0.14.0

type StatusUpdateOption func(*User)

StatusUpdateOption allows callers to mutate the user record before persisting status changes.

func WithSuspendedAt added in v0.14.0

func WithSuspendedAt(at *time.Time) StatusUpdateOption

WithSuspendedAt sets the SuspendedAt timestamp during a status transition.

type TokenService added in v0.7.0

type TokenService interface {
	// Generate creates a new JWT token for the given identity with resource-specific roles
	Generate(identity Identity, resourceRoles map[string]string) (string, error)

	// SignClaims signs the provided claims without mutating registered fields, enabling
	// callers to apply decorators before the token is finalized.
	SignClaims(claims *JWTClaims) (string, error)

	// Validate parses and validates a token string, returning structured claims
	Validate(tokenString string) (AuthClaims, error)
}

TokenService provides transport-agnostic JWT operations

func NewTokenService added in v0.7.0

func NewTokenService(signingKey []byte, tokenExpiration int, issuer string, audience jwt.ClaimStrings, logger Logger) TokenService

NewTokenService creates a new TokenService instance

type TokenServiceAdapter added in v0.7.0

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

TokenServiceAdapter adapts TokenValidator to jwtware.TokenValidator interface

func NewTokenServiceAdapter added in v0.11.0

func NewTokenServiceAdapter(tokenValidator TokenValidator) *TokenServiceAdapter

NewTokenServiceAdapter creates a new TokenServiceAdapter

func (*TokenServiceAdapter) Validate added in v0.7.0

func (tsa *TokenServiceAdapter) Validate(tokenString string) (jwtware.AuthClaims, error)

Validate implements the jwtware.TokenValidator interface

type TokenServiceImpl added in v0.7.0

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

TokenServiceImpl implements the TokenService interface

func (*TokenServiceImpl) Generate added in v0.7.0

func (ts *TokenServiceImpl) Generate(identity Identity, resourceRoles map[string]string) (string, error)

Generate creates a JWT token with resource specific roles

func (*TokenServiceImpl) SignClaims added in v0.14.0

func (ts *TokenServiceImpl) SignClaims(claims *JWTClaims) (string, error)

SignClaims signs arbitrary JWT claims using the configured signing key.

func (*TokenServiceImpl) Validate added in v0.7.0

func (ts *TokenServiceImpl) Validate(tokenString string) (AuthClaims, error)

Validate parses and validates a token string, returning structured claims

type TokenValidator added in v0.24.0

type TokenValidator interface {
	Validate(tokenString string) (AuthClaims, error)
}

TokenValidator validates tokens and extracts claims without tying callers to a specific signing implementation.

type TokenValidatorFunc added in v0.24.0

type TokenValidatorFunc func(tokenString string) (AuthClaims, error)

TokenValidatorFunc adapts a function into a TokenValidator.

func (TokenValidatorFunc) Validate added in v0.24.0

func (f TokenValidatorFunc) Validate(tokenString string) (AuthClaims, error)

Validate satisfies the TokenValidator interface.

type TransitionContext added in v0.14.0

type TransitionContext struct {
	Actor ActorRef
	User  *User
	From  UserStatus
	To    UserStatus
	Meta  TransitionMetadata
}

TransitionContext is passed into hooks for additional processing.

type TransitionHook added in v0.14.0

type TransitionHook func(ctx context.Context, tc TransitionContext) error

TransitionHook is executed before or after a transition.

type TransitionHookPhase added in v0.15.0

type TransitionHookPhase string

TransitionHookPhase identifies whether a hook ran before or after persistence.

const (
	HookPhaseBefore TransitionHookPhase = "before_transition"
	HookPhaseAfter  TransitionHookPhase = "after_transition"
)

type TransitionMetadata added in v0.14.0

type TransitionMetadata struct {
	Reason   string
	Metadata map[string]any
}

TransitionMetadata captures extra context for a transition.

type TransitionOption added in v0.14.0

type TransitionOption func(*transitionOptions)

TransitionOption customizes state machine behavior.

func WithAfterTransitionHook added in v0.14.0

func WithAfterTransitionHook(h TransitionHook) TransitionOption

WithAfterTransitionHook adds a hook executed after the status update succeeds.

func WithBeforeTransitionHook added in v0.14.0

func WithBeforeTransitionHook(h TransitionHook) TransitionOption

WithBeforeTransitionHook adds a hook executed before the status update.

func WithForceTransition added in v0.14.0

func WithForceTransition() TransitionOption

WithForceTransition bypasses validation rules (use sparingly).

func WithSuspensionTime added in v0.14.0

func WithSuspensionTime(t time.Time) TransitionOption

WithSuspensionTime overrides the timestamp recorded when entering the suspended state.

func WithTransitionMetadata added in v0.14.0

func WithTransitionMetadata(metadata map[string]any) TransitionOption

WithTransitionMetadata merges metadata into the transition context.

func WithTransitionReason added in v0.14.0

func WithTransitionReason(reason string) TransitionOption

WithTransitionReason sets the human-readable reason for the transition.

type User

type User struct {
	bun.BaseModel      `bun:"table:users,alias:usr"`
	ID                 uuid.UUID      `bun:"id,pk,nullzero,type:uuid" json:"id,omitempty"`
	Role               UserRole       `bun:"user_role,notnull" json:"user_role,omitempty"`
	Status             UserStatus     `bun:"status,notnull,default:'active'" json:"status,omitempty"`
	FirstName          string         `bun:"first_name,notnull" json:"first_name,omitempty"`
	LastName           string         `bun:"last_name,notnull" json:"last_name,omitempty"`
	Username           string         `bun:"username,notnull,unique" json:"username,omitempty"`
	ProfilePicture     string         `bun:"profile_picture" json:"profile_picture,omitempty"`
	Email              string         `bun:"email,notnull,unique" json:"email,omitempty"`
	ExternalID         string         `bun:"external_id" json:"external_id,omitempty"`
	ExternalIDProvider string         `bun:"external_id_provider" json:"external_id_provider,omitempty"`
	Phone              string         `bun:"phone_number" json:"phone_number,omitempty"`
	PasswordHash       string         `bun:"password_hash" json:"password_hash,omitempty"`
	EmailValidated     bool           `bun:"is_email_verified" json:"is_email_verified,omitempty"`
	LoginAttempts      int            `bun:"login_attempts" json:"login_attempts,omitempty"`
	LoginAttemptAt     *time.Time     `bun:"login_attempt_at" json:"login_attempt_at,omitempty"`
	LoggedInAt         *time.Time     `bun:"loggedin_at" json:"loggedin_at,omitempty"`
	SuspendedAt        *time.Time     `bun:"suspended_at,nullzero" json:"suspended_at,omitempty"`
	Metadata           map[string]any `bun:"metadata" json:"metadata,omitempty"`
	ResetedAt          *time.Time     `bun:"reseted_at,nullzero" json:"reseted_at,omitempty"`
	CreatedAt          *time.Time     `bun:"created_at,nullzero,default:current_timestamp" json:"created_at,omitempty"`
	UpdatedAt          *time.Time     `bun:"updated_at,nullzero,default:current_timestamp" json:"updated_at,omitempty"`
	DeletedAt          *time.Time     `bun:"deleted_at,soft_delete,nullzero" json:"deleted_at,omitempty"`
}

User is the user model

func FromContext added in v0.4.0

func FromContext(ctx context.Context) (*User, bool)

FromContext finds the user from the context.

func (*User) AddMetadata

func (u *User) AddMetadata(key string, val any) *User

AddMetadata will append information to a metadata attribute TODO: make a trigger to merge metadata in database! https://stackoverflow.com/a/42954907/125083

func (*User) EnsureStatus added in v0.14.0

func (u *User) EnsureStatus() *User

EnsureStatus sets a default status when empty to keep DB constraints satisfied.

func (*User) HasStatus added in v0.14.0

func (u *User) HasStatus(status UserStatus) bool

HasStatus reports whether the user is currently in the provided status.

func (*User) IsActive added in v0.14.0

func (u *User) IsActive() bool

IsActive returns true when the user is marked active.

func (*User) IsArchived added in v0.14.0

func (u *User) IsArchived() bool

IsArchived returns true when the user is archived.

func (*User) IsDisabled added in v0.14.0

func (u *User) IsDisabled() bool

IsDisabled returns true when the user is disabled.

func (*User) IsPending added in v0.14.0

func (u *User) IsPending() bool

IsPending returns true when the user is pending activation.

func (*User) IsSuspended added in v0.14.0

func (u *User) IsSuspended() bool

IsSuspended returns true when the user is suspended.

type UserIdentity added in v0.24.0

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

UserIdentity adapts a User into the Identity interface for token generation.

func (UserIdentity) Email added in v0.24.0

func (u UserIdentity) Email() string

Email returns the user's email address.

func (UserIdentity) ID added in v0.24.0

func (u UserIdentity) ID() string

ID returns the user's ID as a string.

func (UserIdentity) Role added in v0.24.0

func (u UserIdentity) Role() string

Role returns the user's role as a string.

func (UserIdentity) Status added in v0.24.0

func (u UserIdentity) Status() UserStatus

Status returns the user's lifecycle status.

func (UserIdentity) Username added in v0.24.0

func (u UserIdentity) Username() string

Username returns the user's username.

type UserProvider

type UserProvider struct {
	Validator func(*User) error
	// contains filtered or unexported fields
}

UserProvider handles users

func NewUserProvider

func NewUserProvider(store UserTracker) *UserProvider

NewUserProvider will create a new UserProvider

func (UserProvider) FindIdentityByIdentifier

func (u UserProvider) FindIdentityByIdentifier(ctx context.Context, identfier string) (Identity, error)

func (UserProvider) VerifyIdentity

func (u UserProvider) VerifyIdentity(ctx context.Context, identifier, password string) (Identity, error)

VerifyIdentity will find the user, compare to the password, and return identity

func (*UserProvider) WithLogger added in v0.4.0

func (u *UserProvider) WithLogger(l Logger) *UserProvider

type UserRole

type UserRole string

UserRole is the user's role

const (
	// RoleGuest is an guest role (ie. view)
	RoleGuest UserRole = "guest"
	// RoleMember us a member (i.e. view, edit)
	RoleMember UserRole = "member"
	// RoleAdmin is an admin role (i.e. view, edit, create)
	RoleAdmin UserRole = "admin"
	// RoleOwner is an admin role (i.e. view, edit, create, delete)
	RoleOwner UserRole = "owner"
)

func GetAllRoles added in v0.7.0

func GetAllRoles() []UserRole

GetAllRoles returns all predefined roles in hierarchical order

func ParseRole added in v0.7.0

func ParseRole(roleStr string) (UserRole, bool)

ParseRole safely parses a string into a UserRole type

func (UserRole) CanCreate added in v0.7.0

func (r UserRole) CanCreate() bool

CanCreate checks if this role can create resources

func (UserRole) CanDelete added in v0.7.0

func (r UserRole) CanDelete() bool

CanDelete checks if this role can delete resources

func (UserRole) CanEdit added in v0.7.0

func (r UserRole) CanEdit() bool

CanEdit checks if this role can edit resources

func (UserRole) CanRead added in v0.7.0

func (r UserRole) CanRead() bool

CanRead checks if this role can read resources

func (UserRole) IsAtLeast added in v0.7.0

func (r UserRole) IsAtLeast(minRole UserRole) bool

IsAtLeast checks if this role meets the minimum required level

func (UserRole) IsValid added in v0.7.0

func (r UserRole) IsValid() bool

IsValid checks if the role is one of the predefined valid roles

type UserStateMachine added in v0.14.0

type UserStateMachine interface {
	Transition(ctx context.Context, actor ActorRef, user *User, target UserStatus, opts ...TransitionOption) (*User, error)
	CurrentStatus(user *User) UserStatus
}

UserStateMachine defines lifecycle operations for users.

func NewUserStateMachine added in v0.14.0

func NewUserStateMachine(users Users, opts ...StateMachineOption) UserStateMachine

NewUserStateMachine returns the default implementation backed by the provided repository.

type UserStatus added in v0.14.0

type UserStatus string

UserStatus represents lifecycle states for a user account

const (
	// UserStatusPending indicates the account exists but is not yet active
	UserStatusPending UserStatus = "pending"
	// UserStatusActive represents a fully active account
	UserStatusActive UserStatus = "active"
	// UserStatusSuspended indicates temporary suspension
	UserStatusSuspended UserStatus = "suspended"
	// UserStatusDisabled indicates manual disablement with no path back to active except admin intervention
	UserStatusDisabled UserStatus = "disabled"
	// UserStatusArchived signals the record should be treated as deleted/read-only
	UserStatusArchived UserStatus = "archived"
)

type UserTracker

type UserTracker interface {
	GetByIdentifier(ctx context.Context, identifier string) (*User, error)
	TrackAttemptedLogin(ctx context.Context, user *User) error
	TrackSucccessfulLogin(ctx context.Context, user *User) error
}

UserTracker is a store we can use to retrieve users

type Users

type Users interface {
	repository.Repository[*User]

	TrackAttemptedLogin(ctx context.Context, user *User) error
	TrackAttemptedLoginTx(ctx context.Context, tx bun.IDB, user *User) error
	TrackSucccessfulLogin(ctx context.Context, user *User) error
	TrackSucccessfulLoginTx(ctx context.Context, tx bun.IDB, user *User) error

	Register(ctx context.Context, user *User) (*User, error)
	RegisterTx(ctx context.Context, tx bun.IDB, user *User) (*User, error)
	GetOrRegisterTx(ctx context.Context, tx bun.IDB, record *User) (*User, error)
	GetOrCreate(ctx context.Context, record *User) (*User, error)
	GetOrCreateTx(ctx context.Context, tx bun.IDB, record *User) (*User, error)
	Create(ctx context.Context, record *User, criteria ...repository.InsertCriteria) (*User, error)
	CreateTx(ctx context.Context, tx bun.IDB, record *User, criteria ...repository.InsertCriteria) (*User, error)
	Upsert(ctx context.Context, record *User, criteria ...repository.UpdateCriteria) (*User, error)
	UpsertTx(ctx context.Context, tx bun.IDB, record *User, criteria ...repository.UpdateCriteria) (*User, error)
	UpdateStatus(ctx context.Context, id uuid.UUID, status UserStatus, opts ...StatusUpdateOption) (*User, error)
	UpdateStatusTx(ctx context.Context, tx bun.IDB, id uuid.UUID, status UserStatus, opts ...StatusUpdateOption) (*User, error)
	Suspend(ctx context.Context, actor ActorRef, user *User, opts ...TransitionOption) (*User, error)
	Reinstate(ctx context.Context, actor ActorRef, user *User, opts ...TransitionOption) (*User, error)

	ResetPassword(ctx context.Context, id uuid.UUID, passwordHash string) error
	ResetPasswordTx(ctx context.Context, tx bun.IDB, id uuid.UUID, passwordHash string) error
}

func NewUsersRepository

func NewUsersRepository(db *bun.DB, opts ...UsersOption) Users

type UsersOption added in v0.14.0

type UsersOption func(*users)

func WithUsersStateMachine added in v0.14.0

func WithUsersStateMachine(sm UserStateMachine) UsersOption

func WithUsersStateMachineOptions added in v0.14.0

func WithUsersStateMachineOptions(options ...StateMachineOption) UsersOption

type ValidationListener added in v0.23.0

type ValidationListener = jwtware.ValidationListener

ValidationListener aliases the jwtware listener so consumers can use auth helpers directly.

type WSAuthClaimsAdapter added in v0.9.0

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

WSAuthClaimsAdapter adapts go-auth AuthClaims to go-router's WSAuthClaims interface

func (*WSAuthClaimsAdapter) CanCreate added in v0.9.0

func (w *WSAuthClaimsAdapter) CanCreate(resource string) bool

CanCreate checks if the user can create a specific resource

func (*WSAuthClaimsAdapter) CanDelete added in v0.9.0

func (w *WSAuthClaimsAdapter) CanDelete(resource string) bool

CanDelete checks if the user can delete a specific resource

func (*WSAuthClaimsAdapter) CanEdit added in v0.9.0

func (w *WSAuthClaimsAdapter) CanEdit(resource string) bool

CanEdit checks if the user can edit a specific resource

func (*WSAuthClaimsAdapter) CanRead added in v0.9.0

func (w *WSAuthClaimsAdapter) CanRead(resource string) bool

CanRead checks if the user can read a specific resource

func (*WSAuthClaimsAdapter) HasRole added in v0.9.0

func (w *WSAuthClaimsAdapter) HasRole(role string) bool

HasRole checks if the user has a specific role

func (*WSAuthClaimsAdapter) IsAtLeast added in v0.9.0

func (w *WSAuthClaimsAdapter) IsAtLeast(minRole string) bool

IsAtLeast checks if the user's role is at least the minimum required role

func (*WSAuthClaimsAdapter) Role added in v0.9.0

func (w *WSAuthClaimsAdapter) Role() string

Role returns the user's role

func (*WSAuthClaimsAdapter) Subject added in v0.9.0

func (w *WSAuthClaimsAdapter) Subject() string

Subject returns the subject claim

func (*WSAuthClaimsAdapter) UserID added in v0.9.0

func (w *WSAuthClaimsAdapter) UserID() string

UserID returns the user ID

type WSTokenValidator added in v0.9.0

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

WSTokenValidator implements go-router's WSTokenValidator interface using the go-auth TokenValidator for seamless WebSocket authentication

func NewWSTokenValidator added in v0.9.0

func NewWSTokenValidator(tokenValidator TokenValidator) *WSTokenValidator

NewWSTokenValidator creates a new WebSocket token validator using the provided TokenValidator

func (*WSTokenValidator) Validate added in v0.9.0

func (w *WSTokenValidator) Validate(tokenString string) (router.WSAuthClaims, error)

Validate validates a token string and returns WebSocket-compatible auth claims

Directories

Path Synopsis
middleware
provider
auth0
Package auth0 provides Auth0 JWT validation and claims mapping for go-auth.
Package auth0 provides Auth0 JWT validation and claims mapping for go-auth.
auth0/sync
Package sync provides optional Auth0 user synchronization helpers.
Package sync provides optional Auth0 user synchronization helpers.
Package social provides OAuth2 social login primitives for go-auth.
Package social provides OAuth2 social login primitives for go-auth.

Jump to

Keyboard shortcuts

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