Plugins
The AuthPlugin trait and plugin architecture.
Better Auth uses a plugin system to compose authentication features. Each plugin registers routes and can customize initialization and request handling. Use SeaORM hooks for database lifecycle operations.
Built-in Plugins
Core Plugins
| Plugin | Description |
|---|---|
EmailPasswordPlugin | Email/password and username sign-up/sign-in |
SessionManagementPlugin | Session querying, listing, and revocation |
PasswordManagementPlugin | Password reset and change flows |
EmailVerificationPlugin | Email verification via tokens |
AccountManagementPlugin | List and unlink linked accounts |
Extended Plugins
| Plugin | Description | Docs |
|---|---|---|
OAuthPlugin | Social sign-in via Google, GitHub, Discord, and custom OAuth providers | OAuth Plugin |
TwoFactorPlugin | Two-factor authentication with TOTP, OTP, and backup codes | 2FA Plugin |
OrganizationPlugin | Multi-tenancy with organizations, members, invitations, and RBAC | Organization Plugin |
AdminPlugin | User management, banning, impersonation, and role management | Admin Plugin |
ApiKeyPlugin | API key creation, validation, rate limiting, and usage tracking | API Key Plugin |
PasskeyPlugin | WebAuthn / FIDO2 passwordless authentication | Passkey Plugin |
Registering Plugins
Plugins are added via the builder:
These examples assume you've already defined AppAuthSchema. If you want a complete setup from entity definitions through Axum mounting, start with the Axum integration guide.
let auth = BetterAuth::<AppAuthSchema>::new(config)
.store(store)
.plugin(EmailPasswordPlugin::new().enable_signup(true))
.plugin(SessionManagementPlugin::new())
.plugin(PasswordManagementPlugin::new())
.build()
.await?;The AuthPlugin Trait
#[async_trait]
pub trait AuthPlugin<S: AuthSchema>: Send + Sync + std::any::Any {
fn name(&self) -> &'static str;
fn routes(&self) -> Vec<AuthRoute>;
/// Called during build to initialize plugin state.
async fn on_init(&self, ctx: &mut AuthInitContext<S>) -> AuthResult<()>;
/// Called before core endpoints and plugin request handlers.
async fn before_request(
&self, req: &AuthRequest, ctx: &AuthContext<S>
) -> AuthResult<Option<BeforeRequestAction>>;
/// Return Some(response) to handle the request, or None to continue.
async fn on_request(
&self, req: &AuthRequest, ctx: &AuthContext<S>
) -> AuthResult<Option<AuthResponse>>;
}on_init and before_request have default no-op implementations. Plugins must implement name, routes, and on_request.
For user and session creation or deletion, register SeaOrmHooks on SeaOrmStore. These hooks receive the active database transaction.
AuthRoute
Routes are defined with a method, path, and operation ID (used as the OpenAPI operationId):
use better_auth::plugin::AuthRoute;
use better_auth::prelude::HttpMethod;
let routes = vec![
AuthRoute::post("/my-endpoint", "handle_my_endpoint"),
AuthRoute::get("/my-query", "handle_my_query"),
];Paths are relative to the auth mount point (e.g., /auth).
AuthContext
Plugins receive an AuthContext providing access to shared state:
pub struct AuthContext<S: AuthSchema> {
pub config: Arc<AuthConfig>,
pub database: Arc<dyn AuthStore<S>>,
pub email_provider: Option<Arc<dyn EmailProvider>>,
pub metadata: HashMap<String, serde_json::Value>,
}| Method | Description |
|---|---|
email_provider() | Returns the email provider or an error if none configured |
set_metadata(key, value) | Store plugin-specific metadata |
get_metadata(key) | Retrieve plugin-specific metadata |