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 ..Default::default()
97 };
98
99 match body
100 .json_body
101 .as_ref()
102 .and_then(CanonicalJsonValue::as_object)
103 .and_then(|body| body.get("auth"))
104 .cloned()
105 .map(CanonicalJsonValue::into)
106 .map(serde_json::from_value)
107 .transpose()?
108 {
109 | Some(AuthData::Jwt(Jwt { ref token, .. })) => {
110 let sender_user = jwt::validate_user(services, token)?;
111 if !services.users.exists(&sender_user).await {
112 return Err!(Request(NotFound("User {sender_user} is not registered.")));
113 }
114
115 Ok(sender_user)
117 },
118 | Some(ref auth) => {
119 let sender_user = body
120 .sender_user
121 .as_deref()
122 .ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
123
124 let sender_device = body.sender_device()?;
125 let (worked, uiaainfo) = services
126 .uiaa
127 .try_auth(sender_user, sender_device, auth, &uiaainfo)
128 .await?;
129
130 if !worked {
131 return Err(Error::Uiaa(uiaainfo));
132 }
133
134 Ok(sender_user.to_owned())
136 },
137 | _ => match body.json_body {
138 | Some(ref json) => {
139 let sender_user = body
140 .sender_user
141 .as_deref()
142 .ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
143
144 let sender_device = body.sender_device()?;
145 uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
146
147 if let Some(ref idp) = bound_idp {
151 uiaainfo.params = to_raw_value(&json!({
152 "m.login.sso": {
153 "identity_providers": [{"id": idp}]
154 }
155 }))
156 .ok();
157 }
158
159 services
160 .uiaa
161 .create(sender_user, sender_device, &uiaainfo, json);
162
163 Err(Error::Uiaa(uiaainfo))
164 },
165 | _ => Err!(Request(NotJson("JSON body is not valid"))),
166 },
167 }
168}