1use std::any::TypeId;
2
3use ruma::{
4 CanonicalJsonValue, OwnedDeviceId, OwnedUserId,
5 api::{
6 auth_scheme::{
7 AccessToken, AccessTokenOptional, AppserviceToken, AppserviceTokenOptional,
8 AuthScheme, NoAccessToken, NoAuthentication,
9 },
10 client::voip::get_turn_server_info,
11 error::{ErrorKind, UnknownTokenErrorData},
12 federation::{authentication::ServerSignatures, openid::get_openid_userinfo},
13 },
14};
15use tuwunel_core::{Err, Error, Result, utils::result::LogDebugErr};
16use tuwunel_service::Services;
17
18use super::{Auth, Request, Token, appservice::auth_appservice, server::auth_server};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub(in crate::router) enum Scheme {
28 None,
29 AccessToken,
30 AccessTokenOptional,
31 AppserviceToken,
32 AppserviceTokenOptional,
33 ServerSignatures,
34}
35
36pub(in crate::router) trait AuthDispatch: AuthScheme {
42 const SCHEME: Scheme;
43
44 fn dispatch(
45 services: &Services,
46 request: &mut Request,
47 json_body: Option<&CanonicalJsonValue>,
48 token: Token,
49 route: TypeId,
50 ) -> impl Future<Output = Result<Auth>> + Send;
51}
52
53impl AuthDispatch for NoAccessToken {
54 const SCHEME: Scheme = Scheme::None;
55
56 async fn dispatch(
57 services: &Services,
58 request: &mut Request,
59 json_body: Option<&CanonicalJsonValue>,
60 token: Token,
61 route: TypeId,
62 ) -> Result<Auth> {
63 <NoAuthentication as AuthDispatch>::dispatch(services, request, json_body, token, route)
64 .await
65 }
66}
67
68impl AuthDispatch for NoAuthentication {
69 const SCHEME: Scheme = Scheme::None;
70
71 async fn dispatch(
72 services: &Services,
73 request: &mut Request,
74 _json_body: Option<&CanonicalJsonValue>,
75 token: Token,
76 route: TypeId,
77 ) -> Result<Auth> {
78 match token {
79 | Token::Invalid
80 if request.query.access_token.is_some()
81 && route == TypeId::of::<get_openid_userinfo::v1::Request>() =>
82 {
83 Ok(Auth::default())
87 },
88
89 | Token::Invalid => unknown_token(),
90 | Token::Expired((user_id, device_id)) =>
91 expired_token(services, user_id, device_id).await,
92
93 | Token::User(user) => Ok(Auth {
94 sender_user: Some(user.0),
95 sender_device: Some(user.1),
96 _expires_at: user.2,
97 ..Auth::default()
98 }),
99
100 | Token::Appservice(info) => Ok(Auth {
101 appservice_info: Some(*info),
102 ..Auth::default()
103 }),
104
105 | Token::None => Ok(Auth::default()),
106 }
107 }
108}
109
110impl AuthDispatch for AccessToken {
111 const SCHEME: Scheme = Scheme::AccessToken;
112
113 async fn dispatch(
114 services: &Services,
115 request: &mut Request,
116 _json_body: Option<&CanonicalJsonValue>,
117 token: Token,
118 route: TypeId,
119 ) -> Result<Auth> {
120 match token {
121 | Token::Invalid => unknown_token(),
122 | Token::Expired((user_id, device_id)) =>
123 expired_token(services, user_id, device_id).await,
124 | Token::Appservice(info) => Ok(auth_appservice(services, request, info).await?),
125 | Token::User(user) => Ok(Auth {
126 sender_user: Some(user.0),
127 sender_device: Some(user.1),
128 _expires_at: user.2,
129 ..Auth::default()
130 }),
131 | Token::None
132 if route == TypeId::of::<get_turn_server_info::v3::Request>()
133 && services.server.config.turn_allow_guests =>
134 Ok(Auth::default()),
135
136 | Token::None => Err!(Request(MissingToken("Missing access token."))),
137 }
138 }
139}
140
141impl AuthDispatch for AccessTokenOptional {
142 const SCHEME: Scheme = Scheme::AccessTokenOptional;
143
144 async fn dispatch(
145 services: &Services,
146 _request: &mut Request,
147 _json_body: Option<&CanonicalJsonValue>,
148 token: Token,
149 _route: TypeId,
150 ) -> Result<Auth> {
151 match token {
152 | Token::Invalid => unknown_token(),
153 | Token::Expired((user_id, device_id)) =>
154 expired_token(services, user_id, device_id).await,
155 | Token::User(user) => Ok(Auth {
156 sender_user: Some(user.0),
157 sender_device: Some(user.1),
158 _expires_at: user.2,
159 ..Auth::default()
160 }),
161 | Token::Appservice(info) => Ok(Auth {
162 appservice_info: Some(*info),
163 ..Auth::default()
164 }),
165 | Token::None => Ok(Auth::default()),
166 }
167 }
168}
169
170impl AuthDispatch for AppserviceToken {
171 const SCHEME: Scheme = Scheme::AppserviceToken;
172
173 async fn dispatch(
174 services: &Services,
175 _request: &mut Request,
176 _json_body: Option<&CanonicalJsonValue>,
177 token: Token,
178 _route: TypeId,
179 ) -> Result<Auth> {
180 match token {
181 | Token::Invalid => unknown_token(),
182 | Token::Expired((user_id, device_id)) =>
183 expired_token(services, user_id, device_id).await,
184 | Token::User(_) =>
185 Err!(Request(Unauthorized("Appservice tokens must be used on this endpoint."))),
186 | Token::Appservice(info) => Ok(Auth {
187 appservice_info: Some(*info),
188 ..Auth::default()
189 }),
190 | Token::None => Err!(Request(MissingToken("Missing access token."))),
191 }
192 }
193}
194
195impl AuthDispatch for AppserviceTokenOptional {
196 const SCHEME: Scheme = Scheme::AppserviceTokenOptional;
197
198 async fn dispatch(
199 services: &Services,
200 _request: &mut Request,
201 _json_body: Option<&CanonicalJsonValue>,
202 token: Token,
203 _route: TypeId,
204 ) -> Result<Auth> {
205 match token {
206 | Token::Invalid => unknown_token(),
207 | Token::Expired((user_id, device_id)) =>
208 expired_token(services, user_id, device_id).await,
209 | Token::User(user) => Ok(Auth {
210 sender_user: Some(user.0),
211 sender_device: Some(user.1),
212 _expires_at: user.2,
213 ..Auth::default()
214 }),
215 | Token::Appservice(info) => Ok(Auth {
216 appservice_info: Some(*info),
217 ..Auth::default()
218 }),
219 | Token::None => Ok(Auth::default()),
220 }
221 }
222}
223
224impl AuthDispatch for ServerSignatures {
225 const SCHEME: Scheme = Scheme::ServerSignatures;
226
227 async fn dispatch(
228 services: &Services,
229 request: &mut Request,
230 json_body: Option<&CanonicalJsonValue>,
231 token: Token,
232 _route: TypeId,
233 ) -> Result<Auth> {
234 match token {
235 | Token::Invalid => unknown_token(),
236 | Token::Expired((user_id, device_id)) =>
237 expired_token(services, user_id, device_id).await,
238 | Token::Appservice(_) | Token::User(_) =>
239 Err!(Request(Unauthorized("Server signatures must be used on this endpoint."))),
240 | Token::None => Ok(auth_server(services, request, json_body).await?),
241 }
242 }
243}
244
245fn unknown_token() -> Result<Auth> {
246 Err(Error::BadRequest(
247 ErrorKind::UnknownToken(UnknownTokenErrorData::new()),
248 "Unknown access token.",
249 ))
250}
251
252async fn expired_token(
253 services: &Services,
254 user_id: OwnedUserId,
255 device_id: OwnedDeviceId,
256) -> Result<Auth> {
257 services
258 .users
259 .remove_access_token(&user_id, &device_id)
260 .await
261 .log_debug_err()
262 .ok();
263
264 Err(Error::BadRequest(
265 ErrorKind::UnknownToken(UnknownTokenErrorData { soft_logout: true }),
266 "Expired access token.",
267 ))
268}