alibi_api/plugins/email_password/
signup.rs1use super::{EmailPasswordConfig, SignUpRequest, SignUpResponse};
3use alibi_core::{AuthContext, AuthRequest, AuthResult, AuthSchema, wire::UserView};
4use serde_json::{Map, Value, json};
5use std::{future::Future, pin::Pin};
6
7pub type ExistingUserSignupCallback = dyn Fn(UserView, AuthRequest) -> Pin<Box<dyn Future<Output = AuthResult<()>> + Send>>
10 + Send
11 + Sync;
12
13#[derive(Clone, Debug)]
15pub struct SyntheticUserContext {
16 pub core_fields: Map<String, Value>,
17 pub additional_fields: Map<String, Value>,
18 pub id: String,
19}
20
21pub type CustomSyntheticUserCallback =
24 dyn Fn(SyntheticUserContext) -> AuthResult<Map<String, Value>> + Send + Sync;
25
26pub(super) async fn notify_existing(
27 user: UserView,
28 request: &AuthRequest,
29 config: &EmailPasswordConfig,
30 context: &AuthContext<impl AuthSchema>,
31) -> AuthResult<()> {
32 let Some(callback) = config.on_existing_user_signup.clone() else {
33 return Ok(());
34 };
35 let request = request.clone();
36 super::super::authentication_helpers::run_owned_notification(
37 context,
38 async move {
39 super::super::authentication_helpers::run_notification(callback(user, request)).await;
40 Ok(())
41 },
42 context.config.awaited_notification_errors,
43 )
44 .await
45}
46
47pub(super) fn synthetic_response(
48 body: &SignUpRequest,
49 config: &EmailPasswordConfig,
50 context: &AuthContext<impl AuthSchema>,
51) -> AuthResult<(SignUpResponse<Value>, Option<Vec<String>>)> {
52 let now = chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true);
53 let core_fields = Map::from_iter([
54 ("name".into(), json!(body.name)),
55 ("email".into(), json!(body.email)),
56 ("emailVerified".into(), json!(false)),
57 ("image".into(), json!(body.image)),
58 ("createdAt".into(), json!(now)),
59 ("updatedAt".into(), json!(now)),
60 ]);
61 let input = SyntheticUserContext {
62 core_fields,
63 additional_fields: Map::new(),
64 id: uuid::Uuid::new_v4().simple().to_string(),
65 };
66 let mut candidate = if let Some(customize) = &config.custom_synthetic_user {
67 customize(input)?
68 } else {
69 let mut fields = input.core_fields;
70 drop(fields.insert("id".into(), json!(input.id)));
71 if config.enable_username {
72 drop(fields.insert("username".into(), json!(body.username)));
73 drop(fields.insert("displayUsername".into(), json!(body.display_username)));
74 }
75 fields
76 };
77 let mut result = Map::new();
78 for field in ["id", "name", "email"] {
79 if let Some(value) = candidate.remove(field) {
80 drop(result.insert(field.into(), value));
81 }
82 }
83 for (field, default) in [
84 ("emailVerified", json!(false)),
85 ("image", Value::Null),
86 ("createdAt", json!(now)),
87 ("updatedAt", json!(now)),
88 ] {
89 drop(result.insert(field.into(), candidate.remove(field).unwrap_or(default)));
90 }
91 let mut defaults = Vec::new();
92 let enabled = |key: &str| {
93 context
94 .get_metadata(key)
95 .and_then(Value::as_bool)
96 .unwrap_or(false)
97 };
98 if config.enable_username {
99 defaults.extend([("username", Value::Null), ("displayUsername", Value::Null)]);
100 }
101 if enabled("two_factor.enabled") {
102 defaults.push(("twoFactorEnabled", json!(false)));
103 }
104 if enabled("admin.enabled") {
105 let mut creation = alibi_core::CreateUser::new();
106 super::super::helpers::apply_default_role(context, &mut creation);
107 defaults.extend([
108 ("role", json!(creation.role)),
109 ("banned", json!(false)),
110 ("banReason", Value::Null),
111 ("banExpires", Value::Null),
112 ]);
113 }
114 if enabled("anonymous.enabled") {
115 defaults.push(("isAnonymous", json!(false)));
116 }
117 if enabled("phone-number.enabled") {
118 defaults.extend([
119 ("phoneNumber", Value::Null),
120 ("phoneNumberVerified", Value::Null),
121 ]);
122 }
123 if enabled("last-login-method.enabled") {
124 defaults.push(("lastLoginMethod", Value::Null));
125 }
126 for (field, default) in defaults {
127 drop(result.insert(field.into(), candidate.remove(field).unwrap_or(default)));
128 }
129 Ok((
130 SignUpResponse {
131 token: None,
132 user: Value::Object(result),
133 },
134 None,
135 ))
136}