Better Auth in Rust

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

PluginDescription
EmailPasswordPluginEmail/password and username sign-up/sign-in
SessionManagementPluginSession querying, listing, and revocation
PasswordManagementPluginPassword reset and change flows
EmailVerificationPluginEmail verification via tokens
AccountManagementPluginList and unlink linked accounts

Extended Plugins

PluginDescriptionDocs
OAuthPluginSocial sign-in via Google, GitHub, Discord, and custom OAuth providersOAuth Plugin
TwoFactorPluginTwo-factor authentication with TOTP, OTP, and backup codes2FA Plugin
OrganizationPluginMulti-tenancy with organizations, members, invitations, and RBACOrganization Plugin
AdminPluginUser management, banning, impersonation, and role managementAdmin Plugin
ApiKeyPluginAPI key creation, validation, rate limiting, and usage trackingAPI Key Plugin
PasskeyPluginWebAuthn / FIDO2 passwordless authenticationPasskey 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>,
}
MethodDescription
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

On this page