1#![cfg_attr(
4 test,
5 allow(
6 unused_results,
7 unreachable_pub,
8 reason = "test code intentionally discards setup return values and exposes helpers broadly"
9 )
10)]
11
12#[cfg(all(feature = "native-tls", feature = "rustls"))]
13compile_error!(
14 "features `native-tls` and `rustls` are mutually exclusive. \
15 Enable exactly one of them: \
16 for `native-tls` (default), remove the `rustls` feature; \
17 for `rustls`, set `default-features = false, features = [\"rustls\"]`."
18);
19
20#[cfg(not(any(feature = "native-tls", feature = "rustls")))]
21compile_error!(
22 "one of the TLS backends must be enabled: \
23 enable either the `native-tls` (default) or `rustls` feature."
24);
25
26macro_rules! route_openapi_metadata {
29 ($schema:ident) => {
30 fn static_openapi_metadata(&self) -> alibi_core::PluginOpenApiMetadata {
31 crate::metadata::plugin_metadata(
32 <Self as alibi_core::AuthPlugin<$schema>>::name(self),
33 &<Self as alibi_core::AuthPlugin<$schema>>::routes(self),
34 )
35 }
36
37 fn openapi_metadata(
38 &self,
39 ctx: &alibi_core::AuthInitContext<$schema>,
40 ) -> alibi_core::PluginOpenApiMetadata {
41 crate::metadata::instance_plugin_metadata(
42 <Self as alibi_core::AuthPlugin<$schema>>::name(self),
43 &<Self as alibi_core::AuthPlugin<$schema>>::routes(self),
44 ctx,
45 )
46 }
47 };
48}
49
50pub mod access;
51pub mod account_management;
52pub mod admin;
53pub mod anonymous;
54pub mod api_key;
55pub(crate) mod authentication_helpers;
56pub mod bearer;
57pub mod captcha;
58pub mod custom_session;
59pub mod device_authorization;
60pub mod email_otp;
61pub mod email_password;
62pub mod email_verification;
63mod endpoint;
64pub mod haveibeenpwned;
65pub mod helpers;
66pub mod jwt;
67pub mod last_login_method;
68pub mod magic_link;
69pub mod metadata;
70pub mod multi_session;
71pub mod oauth;
72pub mod oauth2;
73pub mod oauth_popup;
74pub mod oauth_provider;
75pub mod oauth_proxy;
76pub mod one_tap;
77pub mod one_time_token;
78pub mod open_api;
79pub mod organization;
80#[cfg(feature = "passkey")]
81pub mod passkey;
82pub mod password_management;
83mod passwordless_numeric;
84pub mod phone_number;
85pub mod scim;
86pub mod session_management;
87pub mod siwe;
88pub mod sso;
89pub(crate) mod token_crypto;
90pub mod two_factor;
91pub mod user_management;
92
93#[cfg(test)]
95pub(crate) mod test_helpers {
96 use alibi_core::config::AuthConfig;
97 use alibi_core::wire::{SessionView, UserView};
98 use alibi_core::{AuthContext, AuthRequest, CreateSession, CreateUser, HttpMethod};
99 use alibi_seaorm::store::__private_test_support::bundled_schema::BundledSchema;
100 use alibi_seaorm::{Database, SeaOrmStore};
101 use chrono::{Duration, Utc};
102 use std::collections::HashMap;
103 use std::sync::Arc;
104
105 pub type TestDatabase = dyn alibi_core::store::AuthStore<BundledSchema>;
106
107 pub fn create_test_config() -> AuthConfig {
108 AuthConfig::new("test-secret-key-at-least-32-chars-long")
109 }
110
111 pub async fn create_test_database() -> Arc<TestDatabase> {
112 let database = Database::connect("sqlite::memory:")
113 .await
114 .expect("sqlite test database should connect");
115 alibi_seaorm::store::__private_test_support::migrator::run_migrations(&database)
116 .await
117 .expect("sqlite test migrations should run");
118 Arc::new(SeaOrmStore::<BundledSchema>::new(
119 Arc::new(create_test_config()),
120 database,
121 ))
122 }
123
124 pub async fn create_test_context() -> AuthContext<BundledSchema> {
125 create_test_context_with_config(create_test_config()).await
126 }
127
128 pub fn create_test_context_blocking() -> AuthContext<BundledSchema> {
129 tokio::runtime::Builder::new_current_thread()
130 .enable_all()
131 .build()
132 .expect("test runtime should build")
133 .block_on(create_test_context())
134 }
135
136 pub async fn create_test_context_with_config(config: AuthConfig) -> AuthContext<BundledSchema> {
137 let config = Arc::new(config);
138 let database = create_test_database().await;
139 AuthContext::new(config, database)
140 }
141
142 pub async fn create_user(
143 ctx: &AuthContext<impl alibi_core::AuthSchema>,
144 create_user: CreateUser,
145 ) -> UserView {
146 let user = ctx.database.create_user(create_user).await.unwrap();
147 UserView::from(&user)
148 }
149
150 pub async fn create_session(
151 ctx: &AuthContext<impl alibi_core::AuthSchema>,
152 user_id: String,
153 expires_in: Duration,
154 ) -> SessionView {
155 let create_session = CreateSession {
156 additional_fields: alibi_core::field_policy::FieldValues::default(),
157 token: None,
158 active_team_id: None,
159 user_id,
160 expires_at: Utc::now() + expires_in,
161 ip_address: Some("127.0.0.1".to_owned()),
162 user_agent: Some("test-agent".to_owned()),
163 impersonated_by: None,
164 active_organization_id: None,
165 };
166 let session = ctx.database.create_session(create_session).await.unwrap();
167 SessionView::from(&session)
168 }
169
170 pub async fn create_user_and_session(
171 ctx: &AuthContext<impl alibi_core::AuthSchema>,
172 user_data: CreateUser,
173 session_expires_in: Duration,
174 ) -> (UserView, SessionView) {
175 let user = create_user(ctx, user_data).await;
176 let session = create_session(ctx, user.id.clone(), session_expires_in).await;
177 (user, session)
178 }
179
180 pub async fn create_test_context_with_user(
181 create_user: CreateUser,
182 session_expires_in: Duration,
183 ) -> (AuthContext<BundledSchema>, UserView, SessionView) {
184 let ctx = create_test_context().await;
185 let (user, session) = create_user_and_session(&ctx, create_user, session_expires_in).await;
186 (ctx, user, session)
187 }
188
189 pub fn create_auth_request(
190 method: HttpMethod,
191 path: &str,
192 token: Option<&str>,
193 body: Option<Vec<u8>>,
194 query: HashMap<String, String>,
195 ) -> AuthRequest {
196 let mut headers = HashMap::new();
197 if let Some(token) = token {
198 let config = create_test_config();
199 headers.insert(
200 "cookie".to_owned(),
201 format!(
202 "{}={}",
203 config.session.cookie_name,
204 alibi_core::utils::cookie_utils::sign_cookie_value(
205 token,
206 config.current_secret()
207 )
208 ),
209 );
210 }
211
212 AuthRequest::from_parts(method, path.to_owned(), headers, body, query)
213 }
214
215 pub fn create_auth_request_no_query(
216 method: HttpMethod,
217 path: &str,
218 token: Option<&str>,
219 body: Option<Vec<u8>>,
220 ) -> AuthRequest {
221 create_auth_request(method, path, token, body, HashMap::new())
222 }
223
224 pub fn create_auth_json_request_no_query(
225 method: HttpMethod,
226 path: &str,
227 token: Option<&str>,
228 body: Option<serde_json::Value>,
229 ) -> AuthRequest {
230 create_auth_json_request(method, path, token, body, HashMap::new())
231 }
232
233 pub fn create_auth_json_request(
234 method: HttpMethod,
235 path: &str,
236 token: Option<&str>,
237 body: Option<serde_json::Value>,
238 query: HashMap<String, String>,
239 ) -> AuthRequest {
240 let mut req = create_auth_request(
241 method,
242 path,
243 token,
244 body.map(|b| serde_json::to_vec(&b).unwrap()),
245 query,
246 );
247 req.headers
248 .insert("content-type".to_owned(), "application/json".to_owned());
249 req
250 }
251}
252pub use account_management::AccountManagementPlugin;
255pub use admin::{
256 AdminBanUserRequest, AdminBannedUserMessage, AdminBannedUserMessageHandler, AdminConfig,
257 AdminCreateUserRequest, AdminImpersonationResponse, AdminListUsersQuery,
258 AdminListUsersResponse, AdminPermissionRequest, AdminPermissionResponse, AdminPlugin,
259 AdminRoleInput, AdminSetRoleRequest, AdminSetUserPasswordRequest, AdminSuccessResponse,
260 AdminUpdateUserRequest, AdminUserOutput, AdminUserResponse, AdminUserSessionsResponse,
261 RolePermissions,
262};
263pub use alibi_core::PasswordHasher;
264pub use anonymous::{
265 AnonymousConfig, AnonymousIdentity, AnonymousLink, AnonymousPlugin, LinkAnonymousAccount,
266};
267pub use api_key::{
268 ApiKeyCallbackContext, ApiKeyConfig, ApiKeyDefaultPermissions, ApiKeyErrorMessage,
269 ApiKeyGenerationOptions, ApiKeyGenerator, ApiKeyGetter, ApiKeyPermissions, ApiKeyPlugin,
270 ApiKeyValidator, DeleteExpiredApiKeysResponse,
271};
272pub use bearer::{BearerConfig, BearerPlugin};
273pub use captcha::{CaptchaConfig, CaptchaPlugin, CaptchaProvider};
274pub use custom_session::{CustomSessionPlugin, SessionTransform, SessionTransformContext};
275pub use device_authorization::DeviceAuthorizationPlugin;
276pub use email_otp::{EmailOtpConfig, EmailOtpPlugin, SendEmailOtp};
277pub use email_password::{EmailPasswordConfig, EmailPasswordPlugin};
278pub use email_verification::{
279 EmailVerificationConfig, EmailVerificationHook, EmailVerificationPlugin, SendVerificationEmail,
280};
281pub use haveibeenpwned::{HaveIBeenPwnedConfig, HaveIBeenPwnedPlugin, PwnedPasswordClient};
282pub use last_login_method::{
283 BeforeStoreLastLoginMethodCookie, LastLoginMethodConfig, LastLoginMethodContext,
284 LastLoginMethodEndpointResponse, LastLoginMethodPlugin, ResolveLastLoginMethod,
285};
286pub use magic_link::{MagicLinkConfig, MagicLinkPlugin, SendMagicLink};
287pub use multi_session::{MultiSessionConfig, MultiSessionPlugin};
288pub use oauth::OAuthPlugin;
289pub use oauth_popup::OAuthPopupPlugin;
290pub use oauth_provider::{
291 OAuthAuthenticatedClient, OAuthClientAuthenticationOptions, OAuthClientDiscovery,
292 OAuthClientIdGenerator, OAuthClientMetadataAbortSignal, OAuthClientMetadataFetchOptions,
293 OAuthClientMetadataFetchResponse, OAuthClientMetadataResourceFetch,
294 OAuthClientPrivilegeContext, OAuthClientPrivileges, OAuthClientSecretCipher,
295 OAuthClientSecretGenerator, OAuthClientSecretHasher, OAuthProviderConfig, OAuthProviderPlugin,
296 OAuthTokenHasher, OAuthTokenIssueParams, consume_client_assertion,
297};
298pub use oauth_proxy::{OAuthProxyConfig, OAuthProxyPlugin};
299pub use one_tap::{OAuthJwksSource, OneTapClientId, OneTapConfig, OneTapPlugin};
300pub use open_api::{OpenApiConfig, OpenApiPlugin};
301pub use organization::{
302 OrganizationConfig, OrganizationCreatePatch, OrganizationCreatedContext,
303 OrganizationCreationHooks, OrganizationDraftContext, OrganizationMemberCreatePatch,
304 OrganizationMemberDraftContext, OrganizationPlugin,
305};
306#[cfg(feature = "passkey")]
307pub use passkey::{
308 AuthenticationResult, PasskeyAuthenticationAfterVerification, PasskeyAuthenticationConfig,
309 PasskeyAuthenticationContext, PasskeyAuthenticatorSelection, PasskeyConfig, PasskeyExtensions,
310 PasskeyExtensionsResolver, PasskeyOptionsContext, PasskeyPlugin,
311 PasskeyRegistrationAfterVerification, PasskeyRegistrationConfig, PasskeyRegistrationContext,
312 PasskeyRegistrationOverride, PasskeyRegistrationUser, PasskeyUserResolver,
313 VerifiedPasskeyAuthentication, VerifiedPasskeyRegistration,
314};
315pub use password_management::{
316 PasswordManagementConfig, PasswordManagementPlugin, SendResetPassword,
317};
318pub use phone_number::{PhoneNumberConfig, PhoneNumberPlugin, SendPhoneOtp};
319pub use session_management::{GetSessionQuery, GetSessionResponse, SessionManagementPlugin};
320pub use siwe::{SiweConfig, SiwePlugin};
321pub use two_factor::{
322 SendTwoFactorOtp, TwoFactorBackupCipher, TwoFactorBackupStorage, TwoFactorConfig,
323 TwoFactorOtpCipher, TwoFactorOtpHasher, TwoFactorOtpStorage, TwoFactorPlugin,
324};
325pub use user_management::{
326 ChangeEmailConfig, DeleteUserConfig, SendChangeEmailConfirmation, UserManagementConfig,
327 UserManagementPlugin,
328};
329
330#[derive(Debug, serde::Serialize, serde::Deserialize)]
331pub(crate) struct StatusResponse {
332 status: bool,
333}
334
335pub use scim::ScimPlugin;
336pub use sso::{
337 SsoAccountKey, SsoConfig, SsoDefaultProvider, SsoDeprecatedAlgorithmBehavior, SsoKeyResolver,
338 SsoMutationAction, SsoMutationGuard, SsoMutationProvider, SsoOrganizationProvisioning,
339 SsoOrganizationRoleResolver, SsoPlugin, SsoPrivateKey, SsoPrivateKeyContext,
340 SsoPrivateKeyResolver, SsoProtocol, SsoProviderLimit, SsoProviderLimitResolver,
341 SsoProviderMutation, SsoProviderMutationGuard, SsoProvisioner, SsoProvisioningData,
342 SsoResolvedProfile, SsoResolver, SsoRoleResolver, SsoSamlEncryptionPolicy,
343 SsoSamlSigningPolicy, SsoUserProvisioner, SsoUserResolution, SsoUserResolutionInput,
344 SsoUserResolver,
345};