tuwunel_api/client/session/
password.rs1use futures::{FutureExt, TryFutureExt};
2use ruma::{
3 OwnedUserId, UserId,
4 api::client::{
5 session::login::v3::{Password, Request},
6 uiaa,
7 },
8};
9use tuwunel_core::{Err, Result, debug_error, err, utils::hash};
10use tuwunel_service::Services;
11
12use super::ldap_login;
13use crate::Ruma;
14
15pub(super) async fn handle_login(
16 services: &Services,
17 body: &Ruma<Request>,
18 info: &Password,
19) -> Result<OwnedUserId> {
20 #[expect(deprecated)]
21 let Password { identifier, password, user, .. } = info;
22
23 let user_id = if let Some(uiaa::UserIdentifier::Matrix(uiaa::MatrixUserIdentifier {
24 user: user_id,
25 ..
26 })) = identifier
27 {
28 UserId::parse_with_server_name(user_id, &services.config.server_name)
29 } else if let Some(user) = user {
30 UserId::parse_with_server_name(user, &services.config.server_name)
31 } else {
32 return Err!(Request(Unknown(debug_warn!(
33 ?body.login_info,
34 "Valid identifier or username was not provided (invalid or unsupported login type?)"
35 ))));
36 }
37 .map_err(|e| err!(Request(InvalidUsername(warn!("Username is invalid: {e}")))))?;
38
39 let lowercased_user_id = UserId::parse_with_server_name(
40 user_id.localpart().to_lowercase(),
41 &services.config.server_name,
42 )?;
43
44 let user_is_remote = !services.globals.user_is_local(&user_id)
45 || !services
46 .globals
47 .user_is_local(&lowercased_user_id);
48
49 if user_is_remote {
50 return Err!(Request(Unknown("User ID does not belong to this homeserver")));
51 }
52
53 if cfg!(feature = "ldap") && services.config.ldap.enable {
54 ldap_login(services, &user_id, &lowercased_user_id, password)
55 .boxed()
56 .await
57 } else {
58 password_login(services, &user_id, &lowercased_user_id, password).await
59 }
60}
61
62#[tracing::instrument(skip_all, fields(%user_id), name = "password")]
66pub(super) async fn password_login(
67 services: &Services,
68 user_id: &UserId,
69 lowercased_user_id: &UserId,
70 password: &str,
71) -> Result<OwnedUserId> {
72 if services
75 .users
76 .origin(user_id)
77 .await
78 .is_ok_and(|origin| origin != "password")
79 {
80 return Err!(Request(Forbidden("Account does not permit password login.")));
81 }
82
83 let (hash, user_id) = services
84 .users
85 .password_hash(user_id)
86 .map_ok(|hash| (hash, user_id))
87 .or_else(|_| {
88 services
89 .users
90 .password_hash(lowercased_user_id)
91 .map_ok(|hash| (hash, lowercased_user_id))
92 })
93 .map_err(|_| err!(Request(Forbidden("Wrong username or password."))))
94 .await?;
95
96 if hash.is_empty() {
97 return Err!(Request(UserDeactivated("The user has been deactivated")));
98 }
99
100 hash::verify_password(password, &hash)
101 .inspect_err(|e| debug_error!("{e}"))
102 .map_err(|_| err!(Request(Forbidden("Wrong username or password."))))?;
103
104 Ok(user_id.to_owned())
105}