Architecture
ezauth is built with a modular architecture that separates concerns between configuration, data persistence, business logic, and the HTTP layer.
Component Overview
EzAuth (The Library Entry Point)
The EzAuth struct (defined in ezauth.go) is the primary way to interact with the library. It orchestrates the other components and provides a simplified API for common tasks.
type EzAuth struct {
Config *config.Config
Repo *repository.Repository
Service *service.Auth
Handler *handler.Handler
}
Service (Business Logic)
The service package (located in pkg/service/) contains the core authentication logic. It is independent of the HTTP layer. This is where you'll find logic for:
- User authentication and hashing, including account lockout after repeated failed attempts.
- Token generation and validation (JWT and Refresh Tokens).
- Password reset and passwordless flows.
- Multi-Factor Authentication (TOTP), WebAuthn/Passkeys, and SMS OTP.
- Refresh-token reuse detection: revokes an entire session family when an already-rotated-out token is replayed.
- Admin impersonation, invitation-based onboarding, guarded email change, and admin user management.
- RBAC (roles/permissions), organizations (lightweight multi-tenancy), and scoped API keys.
- Interaction with the Mailer and, for SMS OTP, the
SMSSender. - Dispatching lifecycle events to the
Hookinterface (see Hooks).
Handler (HTTP Layer)
The handler package (located in pkg/handler/) defines the RESTful API. It uses the service package to perform actions. It is responsible for:
- Routing requests (using
chi). - Parsing and validating request bodies.
- Enforcing authentication via middleware.
- Formatting JSON responses.
Repository (Data Persistence)
The repository package (located in pkg/db/repository/) handles all database interactions. It uses bob as a query builder and supports multiple database dialects.
Config (Configuration)
The config package (located in pkg/config/) handles loading configuration from environment variables.
Data Flow
- Incoming Request: An HTTP request arrives at the
Handler. - Routing & Middleware: The
Handlerroutes the request to the appropriate function. If the route is protected, theAuthMiddlewarevalidates the JWT. - Service Call: The
Handlerparses the request body and calls a method on theService. - Database Interaction: The
Serviceperforms business logic and interacts with theRepositoryto read or write data. - Response: The
Servicereturns a result to theHandler, which then sends a JSON response back to the client.
Extension Points
- Mailer: You can provide your own implementation of the
Mailerinterface if you need to use a service other than SMTP (e.g., SendGrid, Mailgun). - SMSSender: You can provide your own implementation of the
SMSSenderinterface to deliver SMS OTP codes through your provider of choice (e.g., Twilio, SNS). - Hooks: Implement the
Hookinterface (or embedservice.DefaultHookand override only what you need) to intercept before/after lifecycle events — user creation/update/deletion, sign-in/sign-out, password reset, OAuth2, MFA enable/disable, and impersonation start/end. See Hooks for the full list and usage. - Custom Router: You can pass your own
chi.Routerto theHandlerif you want to add global middlewares or customize the routing behavior.