Better Auth in Rust

Email Verification

Send and verify email addresses.

The EmailVerificationPlugin handles sending verification emails and validating tokens.

Setup

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.

use std::sync::Arc;
use better_auth::{AuthError, AuthResult, BetterAuth};
use better_auth::email::{ConsoleEmailProvider, EmailProvider};
use better_auth::plugins::{EmailVerificationPlugin, SendVerificationEmail};
use better_auth::prelude::UserView;

struct VerificationEmail;

#[async_trait::async_trait]
impl SendVerificationEmail for VerificationEmail {
    async fn send(&self, user: &UserView, url: &str, _token: &str) -> AuthResult<()> {
        let email = user.email.as_deref().ok_or_else(|| AuthError::bad_request("Email is required"))?;
        ConsoleEmailProvider
            .send(email, "Verify your email", "", url)
            .await
    }
}

let auth = BetterAuth::<AppAuthSchema>::new(config)
    .store(store)
    .plugin(
        EmailVerificationPlugin::new()
            .custom_send_verification_email(Arc::new(VerificationEmail))
    )
    .build()
    .await?;

Configure a SendVerificationEmail callback for /send-verification-email. Setting only the global EmailProvider does not enable that endpoint. The example uses ConsoleEmailProvider inside the callback for local development.

Plugin Options

OptionTypeDefaultDescription
verification_token_expirychrono::DurationDuration::hours(24)How long verification tokens stay valid
send_email_notificationsbooltrueUse the default email provider for sign-in notifications; custom callbacks are independent
require_verification_for_signinboolfalseRequire verification for username sign-in when attached with with_email_verification
send_on_sign_inboolfalseSend to unverified users when attached with EmailPasswordPlugin::with_email_verification
send_on_sign_upOption<bool>NoneSend for new unverified OAuth users; None follows the provider's require_email_verification
auto_sign_in_after_verificationboolfalseCreate a session after verification

Send Verification Email

POST /send-verification-email
Authorization: Bearer <token>
Content-Type: application/json
{
  "email": "[email protected]",
  "callbackURL": "https://example.com/verify"
}

Response

{
  "status": true
}

Verify Email

Called when the user clicks the link in the verification email.

GET /verify-email?token=<verification_token>

Response

{
  "user": {
    "id": "uuid",
    "email": "[email protected]",
    "emailVerified": true,
    ...
  },
  "status": true
}
StatusCondition
400Token missing, expired, or invalid

Sending After Sign-Up

Call /send-verification-email after a successful sign-up. Registering EmailVerificationPlugin does not automatically send mail when a user is created. Users become verified only after completing the verification flow.

For Generic OAuth, attach the verification plugin with OAuthPlugin::with_email_verification. New unverified users follow send_on_sign_up; subsequent sign-ins send only when the provider requires email verification and send_on_sign_in is enabled. Sender failures are logged and preserve the verification decision, matching upstream. See Generic OAuth / OIDC.

Use SeaORM hooks for application database operations associated with user creation.

Email Provider

The ConsoleEmailProvider logs emails to stderr and is useful for development:

[EMAIL] To: [email protected] | Subject: Verify your email | Body: ...

For production, implement EmailProvider and call that provider from your SendVerificationEmail callback:

use better_auth::email::EmailProvider;

struct SmtpProvider { /* ... */ }

#[async_trait::async_trait]
impl EmailProvider for SmtpProvider {
    async fn send(
        &self, to: &str, subject: &str, html: &str, text: &str
    ) -> better_auth::error::AuthResult<()> {
        // Send via SMTP, SendGrid, etc.
        Ok(())
    }
}

On this page