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
| Option | Type | Default | Description |
|---|---|---|---|
verification_token_expiry | chrono::Duration | Duration::hours(24) | How long verification tokens stay valid |
send_email_notifications | bool | true | Use the default email provider for sign-in notifications; custom callbacks are independent |
require_verification_for_signin | bool | false | Require verification for username sign-in when attached with with_email_verification |
send_on_sign_in | bool | false | Send to unverified users when attached with EmailPasswordPlugin::with_email_verification |
send_on_sign_up | Option<bool> | None | Send for new unverified OAuth users; None follows the provider's require_email_verification |
auto_sign_in_after_verification | bool | false | Create 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
}| Status | Condition |
|---|---|
| 400 | Token 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(())
}
}