Skip to main content

better_auth_api/plugins/
mod.rs

1pub mod account_management;
2pub mod admin;
3pub mod anonymous;
4pub mod api_key;
5pub mod captcha;
6pub mod custom_session;
7pub mod device_authorization;
8pub mod email_otp;
9pub mod email_password;
10pub mod email_verification;
11pub mod endpoint_context;
12pub mod have_i_been_pwned;
13pub mod helpers;
14mod json_body;
15pub mod jwt;
16pub mod last_login_method;
17pub mod magic_link;
18pub mod multi_session;
19pub mod oauth;
20pub mod one_tap;
21pub mod one_time_token;
22pub mod open_api;
23pub mod organization;
24pub mod passkey;
25pub mod password_management;
26pub mod phone_number;
27mod query_input;
28pub mod session_management;
29mod session_update;
30pub mod siwe;
31mod symmetric;
32pub mod test_utils;
33pub mod two_factor;
34pub mod user_management;
35pub mod username;
36
37use serde::{Deserialize, Serialize};
38
39#[derive(Debug, Serialize, Deserialize)]
40pub(crate) struct StatusResponse {
41    status: bool,
42}
43
44#[cfg(test)]
45pub(crate) mod test_helpers {
46    use std::collections::HashMap;
47    use std::sync::Arc;
48
49    use better_auth_core::config::AuthConfig;
50    use better_auth_core::wire::{SessionView, UserView};
51    use better_auth_core::{AuthContext, AuthRequest, CreateSession, CreateUser, HttpMethod};
52    use better_auth_seaorm::store::__private_test_support::bundled_schema::BundledSchema;
53    use better_auth_seaorm::{Database, SeaOrmStore};
54    use chrono::{Duration, Utc};
55
56    pub type TestDatabase = dyn better_auth_core::store::AuthStore<BundledSchema>;
57
58    /// Apply the dispatch response boundary after invoking an internal handler directly.
59    pub fn finalize_response(
60        ctx: &AuthContext<impl better_auth_core::AuthSchema>,
61        req: &AuthRequest,
62        mut response: better_auth_core::AuthResponse,
63    ) -> better_auth_core::AuthResponse {
64        ctx.session_manager()
65            .finish_response(req, &mut response)
66            .expect("endpoint response should finalize");
67        response
68    }
69
70    pub fn create_test_config() -> AuthConfig {
71        let mut config = AuthConfig::new("test-secret-key-at-least-32-chars-long")
72            .base_url("http://localhost:3000");
73        config.session.bearer = Some(better_auth_core::config::BearerConfig::default());
74        config
75    }
76
77    pub async fn create_test_database() -> Arc<TestDatabase> {
78        let database = Database::connect("sqlite::memory:")
79            .await
80            .expect("sqlite test database should connect");
81        better_auth_seaorm::store::__private_test_support::migrator::run_migrations(&database)
82            .await
83            .expect("sqlite test migrations should run");
84        Arc::new(SeaOrmStore::<BundledSchema>::new(
85            Arc::new(create_test_config()),
86            database,
87        ))
88    }
89
90    pub async fn create_test_context() -> AuthContext<BundledSchema> {
91        create_test_context_with_config(create_test_config()).await
92    }
93
94    pub fn create_test_context_blocking() -> AuthContext<BundledSchema> {
95        tokio::runtime::Builder::new_current_thread()
96            .enable_all()
97            .build()
98            .expect("test runtime should build")
99            .block_on(create_test_context())
100    }
101
102    pub async fn create_test_context_with_config(config: AuthConfig) -> AuthContext<BundledSchema> {
103        let config = Arc::new(config);
104        let database = create_test_database().await;
105        AuthContext::new(config, database)
106            .initialize_request_context()
107            .await
108            .expect("test request context should initialize")
109            .as_ref()
110            .clone()
111    }
112
113    pub async fn create_user(
114        ctx: &AuthContext<impl better_auth_core::AuthSchema>,
115        create_user: CreateUser,
116    ) -> UserView {
117        let user = ctx.database.create_user(create_user).await.unwrap();
118        UserView::from(&user)
119    }
120
121    pub async fn create_session(
122        ctx: &AuthContext<impl better_auth_core::AuthSchema>,
123        user_id: String,
124        expires_in: Duration,
125    ) -> SessionView {
126        let create_session = CreateSession {
127            additional_fields: Default::default(),
128            user_id: user_id.into(),
129            expires_at: (Utc::now() + expires_in).into(),
130            ip_address: Some("127.0.0.1".to_string()),
131            user_agent: Some("test-agent".to_string()),
132            impersonated_by: None,
133            active_organization_id: None,
134        };
135        let session = ctx.database.create_session(create_session).await.unwrap();
136        SessionView::from(&session)
137    }
138
139    pub async fn create_user_and_session(
140        ctx: &AuthContext<impl better_auth_core::AuthSchema>,
141        user_data: CreateUser,
142        session_expires_in: Duration,
143    ) -> (UserView, SessionView) {
144        let user = create_user(ctx, user_data).await;
145        let session =
146            create_session(ctx, user.id.typed().unwrap().clone(), session_expires_in).await;
147        (user, session)
148    }
149
150    pub async fn create_test_context_with_user(
151        create_user: CreateUser,
152        session_expires_in: Duration,
153    ) -> (AuthContext<BundledSchema>, UserView, SessionView) {
154        let ctx = create_test_context().await;
155        let (user, session) = create_user_and_session(&ctx, create_user, session_expires_in).await;
156        (ctx, user, session)
157    }
158
159    pub fn create_auth_request(
160        method: HttpMethod,
161        path: &str,
162        token: Option<&str>,
163        body: Option<Vec<u8>>,
164        query: HashMap<String, String>,
165    ) -> AuthRequest {
166        let mut headers = HashMap::new();
167        if let Some(token) = token {
168            headers.insert("authorization".to_string(), format!("Bearer {}", token));
169        }
170
171        AuthRequest::from_parts(
172            method,
173            path.to_string(),
174            headers,
175            body,
176            Some(serde_json::json!(query)),
177        )
178    }
179
180    pub fn create_auth_request_no_query(
181        method: HttpMethod,
182        path: &str,
183        token: Option<&str>,
184        body: Option<Vec<u8>>,
185    ) -> AuthRequest {
186        create_auth_request(method, path, token, body, HashMap::new())
187    }
188
189    pub fn create_auth_json_request_no_query(
190        method: HttpMethod,
191        path: &str,
192        token: Option<&str>,
193        body: Option<serde_json::Value>,
194    ) -> AuthRequest {
195        create_auth_json_request(method, path, token, body, HashMap::new())
196    }
197
198    pub fn create_auth_json_request(
199        method: HttpMethod,
200        path: &str,
201        token: Option<&str>,
202        body: Option<serde_json::Value>,
203        query: HashMap<String, String>,
204    ) -> AuthRequest {
205        let mut req = create_auth_request(
206            method,
207            path,
208            token,
209            body.map(|b| serde_json::to_vec(&b).unwrap()),
210            query,
211        );
212        req.headers
213            .insert("content-type".to_string(), "application/json".to_string());
214        req
215    }
216}
217
218pub use account_management::AccountManagementPlugin;
219pub use admin::{AdminConfig, AdminPlugin, RolePermissions};
220pub use api_key::{ApiKeyConfig, ApiKeyPlugin};
221pub use better_auth_core::PasswordHasher;
222pub use custom_session::{CustomSessionCallback, CustomSessionInput, CustomSessionPlugin};
223pub use device_authorization::DeviceAuthorizationPlugin;
224pub use email_otp::{
225    EmailOtpApi, EmailOtpCodec, EmailOtpConfig, EmailOtpGenerator, EmailOtpMessage, EmailOtpPlugin,
226    EmailOtpStorage, EmailOtpType, SendEmailOtp,
227};
228pub use email_password::{EmailPasswordConfig, EmailPasswordPlugin};
229pub use email_verification::{
230    EmailVerificationConfig, EmailVerificationHook, EmailVerificationPlugin, SendVerificationEmail,
231};
232pub use jwt::{
233    JwtAdapterFuture, JwtAlgorithm, JwtApi, JwtAudience, JwtCallOverrides, JwtCallbackFuture,
234    JwtCallbacks, JwtCustomSign, JwtDefinePayload, JwtExpiration, JwtGetSubject, JwtKeyOptions,
235    JwtKeyPairConfig, JwtPlugin, JwtPluginConfig, JwtSigningOptions, JwtTokenOptions,
236};
237pub use magic_link::{MagicLinkConfig, MagicLinkMessage, MagicLinkPlugin, SendMagicLink};
238pub use multi_session::{MultiSessionConfig, MultiSessionPlugin};
239pub use oauth::{OAuthPopupPlugin, OAuthProxyConfig, OAuthProxyPlugin};
240pub use one_tap::{OneTapConfig, OneTapPlugin};
241pub use one_time_token::{OneTimeTokenConfig, OneTimeTokenPlugin, TokenStorage};
242pub use open_api::{OpenApiConfig, OpenApiPlugin};
243pub use organization::{OrganizationConfig, OrganizationPlugin};
244pub use passkey::{PasskeyConfig, PasskeyPlugin};
245pub use password_management::{
246    PasswordManagementConfig, PasswordManagementPlugin, SendResetPassword,
247};
248pub use session_management::SessionManagementPlugin;
249pub use two_factor::{SendTwoFactorOtp, TwoFactorConfig, TwoFactorPlugin};
250pub use user_management::{
251    ChangeEmailConfig, DeleteUserConfig, UserManagementConfig, UserManagementPlugin,
252};
253
254pub mod user_admission;
255
256pub use last_login_method::{
257    BeforeStoreLastLoginCookie, LastLoginMethodConfig, LastLoginMethodPlugin,
258    LastLoginMethodResolver,
259};