alibi_api/plugins/password_management/
set_password.rs1use super::super::email_password::EmailPasswordConfig;
3use alibi_core::{
4 AuthAccount, AuthContext, AuthError, AuthRequest, AuthResult, AuthSchema, AuthUser,
5 CreateAccount, UpdateAccount,
6};
7
8pub async fn set_password<S: AuthSchema>(
21 request: &AuthRequest,
22 new_password: &str,
23 context: &AuthContext<S>,
24) -> AuthResult<()> {
25 let (user, _) = context
26 .require_authoritative_session(request)
27 .await
28 .map_err(|error| {
29 if matches!(error, AuthError::Unauthenticated) {
30 AuthError::Upstream {
31 status: 401,
32 code: "UNAUTHORIZED",
33 message: "Unauthorized",
34 }
35 } else {
36 error
37 }
38 })?;
39 let policy = context.extensions.get::<EmailPasswordConfig>();
40 let (minimum, maximum) = crate::plugins::email_password::password_length_limits(context);
41 let length = new_password.encode_utf16().count();
42 if length < minimum {
43 return Err(AuthError::Upstream {
44 status: 400,
45 code: "PASSWORD_TOO_SHORT",
46 message: "Password too short",
47 });
48 }
49 if length > maximum {
50 return Err(AuthError::Upstream {
51 status: 400,
52 code: "PASSWORD_TOO_LONG",
53 message: "Password too long",
54 });
55 }
56 let user_id = user.id();
57 let credential = context
58 .database
59 .get_user_accounts(user_id.as_ref())
60 .await?
61 .into_iter()
62 .find(|account| account.provider_id() == "credential" && account.account_id() == user_id);
63 let hasher = policy
64 .as_ref()
65 .and_then(|policy| policy.password_hasher.as_ref());
66 let hash_context = alibi_core::PasswordHashContext {
67 path: Some("virtual:".into()),
70 request: alibi_core::hooks::current_request_hook_context(),
71 };
72 let password = context
73 .hash_password_with_context(hasher, new_password, Some(&hash_context))
74 .await?;
75 if let Some(account) = credential {
76 if account
77 .password()
78 .is_some_and(|password| !password.is_empty())
79 {
80 return Err(AuthError::Upstream {
81 status: 400,
82 code: "PASSWORD_ALREADY_SET",
83 message: "User already has a password set",
84 });
85 }
86 drop(
87 context
88 .database
89 .update_account_record(
90 account.id().as_ref(),
91 UpdateAccount {
92 password: Some(password),
93 ..Default::default()
94 },
95 )
96 .await?,
97 );
98 } else {
99 drop(
100 context
101 .database
102 .create_account_record(CreateAccount {
103 additional_fields: Default::default(),
104 user_id: user.id().into_owned(),
105 account_id: user.id().into_owned(),
106 provider_id: "credential".to_owned(),
107 access_token: None,
108 refresh_token: None,
109 id_token: None,
110 access_token_expires_at: None,
111 refresh_token_expires_at: None,
112 scope: None,
113 password: Some(password),
114 })
115 .await?,
116 );
117 }
118 Ok(())
119}