tuwunel_api/router/auth/
uiaa.rs1use ruma::{
2 CanonicalJsonValue, OwnedUserId,
3 api::{
4 IncomingRequest,
5 client::uiaa::{AuthData, AuthFlow, AuthType, Jwt, UiaaInfo},
6 },
7};
8use serde_json::{json, value::to_raw_value};
9use tuwunel_core::{
10 Err, Error, Result, err, is_equal_to, utils,
11 utils::{
12 OptionExt,
13 future::{OptionFutureExt, TryExtExt},
14 },
15};
16use tuwunel_service::{Services, uiaa::SESSION_ID_LENGTH};
17
18use crate::{Ruma, client::jwt};
19
20pub(crate) async fn auth_uiaa<T>(services: &Services, body: &Ruma<T>) -> Result<OwnedUserId>
21where
22 T: IncomingRequest + Send + Sync,
23{
24 let sender_user = body.sender_user.as_deref();
25
26 let password_flow = [AuthType::Password];
27 let user_origin = sender_user
28 .map_async(|sender_user| services.users.origin(sender_user).ok())
29 .unwrap_or(None)
30 .await;
31 let has_password = sender_user
32 .map_async(|sender_user| {
33 services
34 .users
35 .has_password(sender_user)
36 .unwrap_or(false)
37 })
38 .unwrap_or(false)
39 .await || (cfg!(feature = "ldap") && services.config.ldap.enable);
40
41 let sso_flow = [AuthType::Sso];
54 let bound_idp: Option<String> = sender_user
55 .map_async(async |sender_user| {
56 body.sender_device
57 .as_deref()
58 .map_async(async |device_id| {
59 services
60 .users
61 .get_oidc_device_idp(sender_user, device_id)
62 .await
63 .filter(|s| !s.is_empty())
64 })
65 .await
66 .flatten()
67 .or_else(|| {
68 let use_sso = user_origin
69 .as_deref()
70 .is_some_and(is_equal_to!("sso"))
71 && services.config.identity_provider.len() == 1;
72
73 use_sso
74 .then(|| services.oauth.providers.get_default_id())
75 .flatten()
76 })
77 })
78 .await
79 .flatten();
80
81 let has_sso = bound_idp.is_some();
82
83 let jwt_flow = [AuthType::Jwt];
84 let has_jwt = services.config.jwt.enable;
85
86 let mut uiaainfo = UiaaInfo {
87 flows: has_password
88 .then_some(password_flow)
89 .into_iter()
90 .chain(has_sso.then_some(sso_flow))
91 .chain(has_jwt.then_some(jwt_flow))
92 .map(Vec::from)
93 .map(AuthFlow::new)
94 .collect(),
95
96 params: to_raw_value(&json!({})).ok(),
97 ..Default::default()
98 };
99
100 match body
101 .json_body
102 .as_ref()
103 .and_then(CanonicalJsonValue::as_object)
104 .and_then(|body| body.get("auth"))
105 .cloned()
106 .map(CanonicalJsonValue::into)
107 .map(serde_json::from_value)
108 .transpose()?
109 {
110 | Some(AuthData::Jwt(Jwt { ref token, .. })) => {
111 let sender_user = jwt::validate_user(services, token)?;
112 if !services.users.exists(&sender_user).await {
113 return Err!(Request(NotFound("User {sender_user} is not registered.")));
114 }
115
116 Ok(sender_user)
118 },
119 | Some(ref auth) => {
120 let sender_user = body
121 .sender_user
122 .as_deref()
123 .ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
124
125 let sender_device = body.sender_device()?;
126 let (worked, uiaainfo) = services
127 .uiaa
128 .try_auth(sender_user, sender_device, auth, &uiaainfo)
129 .await?;
130
131 if !worked {
132 return Err(Error::Uiaa(uiaainfo));
133 }
134
135 Ok(sender_user.to_owned())
137 },
138 | _ => match body.json_body {
139 | Some(ref json) => {
140 let sender_user = body
141 .sender_user
142 .as_deref()
143 .ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
144
145 let sender_device = body.sender_device()?;
146 uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
147
148 if let Some(ref idp) = bound_idp {
152 uiaainfo.params = to_raw_value(&json!({
153 "m.login.sso": {
154 "identity_providers": [{"id": idp}]
155 }
156 }))
157 .ok();
158 }
159
160 services
161 .uiaa
162 .create(sender_user, sender_device, &uiaainfo, json);
163
164 Err(Error::Uiaa(uiaainfo))
165 },
166 | _ => Err!(Request(NotJson("JSON body is not valid"))),
167 },
168 }
169}