this repo has no description
1use crate::delegation;
2use crate::oauth::db;
3use crate::state::{AppState, RateLimitKind};
4use crate::util::extract_client_ip;
5use axum::{
6 Json,
7 extract::State,
8 http::{HeaderMap, StatusCode},
9 response::{IntoResponse, Response},
10};
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Deserialize)]
14pub struct DelegationAuthSubmit {
15 pub request_uri: String,
16 pub delegated_did: Option<String>,
17 pub controller_did: String,
18 pub password: String,
19 #[serde(default)]
20 pub remember_device: bool,
21}
22
23#[derive(Debug, Serialize)]
24pub struct DelegationAuthResponse {
25 pub success: bool,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub needs_totp: Option<bool>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub redirect_uri: Option<String>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub error: Option<String>,
32}
33
34pub async fn delegation_auth(
35 State(state): State<AppState>,
36 headers: HeaderMap,
37 Json(form): Json<DelegationAuthSubmit>,
38) -> Response {
39 let client_ip = extract_client_ip(&headers);
40 if !state
41 .check_rate_limit(RateLimitKind::Login, &client_ip)
42 .await
43 {
44 return (
45 StatusCode::TOO_MANY_REQUESTS,
46 Json(DelegationAuthResponse {
47 success: false,
48 needs_totp: None,
49 redirect_uri: None,
50 error: Some("Too many login attempts. Please try again later.".to_string()),
51 }),
52 )
53 .into_response();
54 }
55
56 let request = match db::get_authorization_request(&state.db, &form.request_uri).await {
57 Ok(Some(r)) => r,
58 Ok(None) => {
59 return Json(DelegationAuthResponse {
60 success: false,
61 needs_totp: None,
62 redirect_uri: None,
63 error: Some("Authorization request not found".to_string()),
64 })
65 .into_response();
66 }
67 Err(_) => {
68 return Json(DelegationAuthResponse {
69 success: false,
70 needs_totp: None,
71 redirect_uri: None,
72 error: Some("Server error".to_string()),
73 })
74 .into_response();
75 }
76 };
77
78 let delegated_did = match form.delegated_did.as_ref().or(request.did.as_ref()) {
79 Some(did) => did.clone(),
80 None => {
81 return Json(DelegationAuthResponse {
82 success: false,
83 needs_totp: None,
84 redirect_uri: None,
85 error: Some("No delegated account selected".to_string()),
86 })
87 .into_response();
88 }
89 };
90
91 if let Err(_) = db::set_request_did(&state.db, &form.request_uri, &delegated_did).await {
92 tracing::warn!("Failed to set delegated DID on authorization request");
93 }
94
95 let grant =
96 match delegation::get_delegation(&state.db, &delegated_did, &form.controller_did).await {
97 Ok(Some(g)) => g,
98 Ok(None) => {
99 return Json(DelegationAuthResponse {
100 success: false,
101 needs_totp: None,
102 redirect_uri: None,
103 error: Some("No delegation grant found for this controller".to_string()),
104 })
105 .into_response();
106 }
107 Err(_) => {
108 return Json(DelegationAuthResponse {
109 success: false,
110 needs_totp: None,
111 redirect_uri: None,
112 error: Some("Server error".to_string()),
113 })
114 .into_response();
115 }
116 };
117
118 let controller = match sqlx::query!(
119 r#"
120 SELECT id, did, password_hash, deactivated_at, takedown_ref,
121 email_verified, discord_verified, telegram_verified, signal_verified
122 FROM users
123 WHERE did = $1
124 "#,
125 form.controller_did
126 )
127 .fetch_optional(&state.db)
128 .await
129 {
130 Ok(Some(u)) => u,
131 Ok(None) => {
132 return Json(DelegationAuthResponse {
133 success: false,
134 needs_totp: None,
135 redirect_uri: None,
136 error: Some("Controller account not found".to_string()),
137 })
138 .into_response();
139 }
140 Err(_) => {
141 return Json(DelegationAuthResponse {
142 success: false,
143 needs_totp: None,
144 redirect_uri: None,
145 error: Some("Server error".to_string()),
146 })
147 .into_response();
148 }
149 };
150
151 if controller.deactivated_at.is_some() {
152 return Json(DelegationAuthResponse {
153 success: false,
154 needs_totp: None,
155 redirect_uri: None,
156 error: Some("Controller account is deactivated".to_string()),
157 })
158 .into_response();
159 }
160
161 if controller.takedown_ref.is_some() {
162 return Json(DelegationAuthResponse {
163 success: false,
164 needs_totp: None,
165 redirect_uri: None,
166 error: Some("Controller account has been taken down".to_string()),
167 })
168 .into_response();
169 }
170
171 let password_valid = match &controller.password_hash {
172 Some(hash) => match bcrypt::verify(&form.password, hash) {
173 Ok(valid) => valid,
174 Err(_) => false,
175 },
176 None => false,
177 };
178
179 if !password_valid {
180 return Json(DelegationAuthResponse {
181 success: false,
182 needs_totp: None,
183 redirect_uri: None,
184 error: Some("Invalid password".to_string()),
185 })
186 .into_response();
187 }
188
189 if let Err(_) = db::set_controller_did(&state.db, &form.request_uri, &form.controller_did).await
190 {
191 return Json(DelegationAuthResponse {
192 success: false,
193 needs_totp: None,
194 redirect_uri: None,
195 error: Some("Failed to update authorization request".to_string()),
196 })
197 .into_response();
198 }
199
200 let has_totp = crate::api::server::has_totp_enabled(&state, &form.controller_did).await;
201 if has_totp {
202 return Json(DelegationAuthResponse {
203 success: true,
204 needs_totp: Some(true),
205 redirect_uri: Some(format!(
206 "/#/oauth/delegation-totp?request_uri={}",
207 urlencoding::encode(&form.request_uri)
208 )),
209 error: None,
210 })
211 .into_response();
212 }
213
214 let ip = extract_client_ip(&headers);
215 let user_agent = headers
216 .get("user-agent")
217 .and_then(|v| v.to_str().ok())
218 .map(|s| s.to_string());
219
220 let _ = delegation::log_delegation_action(
221 &state.db,
222 &delegated_did,
223 &form.controller_did,
224 Some(&form.controller_did),
225 delegation::DelegationActionType::TokenIssued,
226 Some(serde_json::json!({
227 "client_id": request.client_id,
228 "granted_scopes": grant.granted_scopes
229 })),
230 Some(&ip),
231 user_agent.as_deref(),
232 )
233 .await;
234
235 Json(DelegationAuthResponse {
236 success: true,
237 needs_totp: None,
238 redirect_uri: Some(format!(
239 "/#/oauth/consent?request_uri={}",
240 urlencoding::encode(&form.request_uri)
241 )),
242 error: None,
243 })
244 .into_response()
245}
246
247#[derive(Debug, Deserialize)]
248pub struct DelegationTotpSubmit {
249 pub request_uri: String,
250 pub code: String,
251}
252
253pub async fn delegation_totp_verify(
254 State(state): State<AppState>,
255 headers: HeaderMap,
256 Json(form): Json<DelegationTotpSubmit>,
257) -> Response {
258 let client_ip = extract_client_ip(&headers);
259 if !state
260 .check_rate_limit(RateLimitKind::TotpVerify, &client_ip)
261 .await
262 {
263 return (
264 StatusCode::TOO_MANY_REQUESTS,
265 Json(DelegationAuthResponse {
266 success: false,
267 needs_totp: None,
268 redirect_uri: None,
269 error: Some("Too many verification attempts. Please try again later.".to_string()),
270 }),
271 )
272 .into_response();
273 }
274
275 let request = match db::get_authorization_request(&state.db, &form.request_uri).await {
276 Ok(Some(r)) => r,
277 Ok(None) => {
278 return Json(DelegationAuthResponse {
279 success: false,
280 needs_totp: None,
281 redirect_uri: None,
282 error: Some("Authorization request not found".to_string()),
283 })
284 .into_response();
285 }
286 Err(_) => {
287 return Json(DelegationAuthResponse {
288 success: false,
289 needs_totp: None,
290 redirect_uri: None,
291 error: Some("Server error".to_string()),
292 })
293 .into_response();
294 }
295 };
296
297 let controller_did = match &request.controller_did {
298 Some(did) => did.clone(),
299 None => {
300 return Json(DelegationAuthResponse {
301 success: false,
302 needs_totp: None,
303 redirect_uri: None,
304 error: Some("Controller not authenticated".to_string()),
305 })
306 .into_response();
307 }
308 };
309
310 let delegated_did = match &request.did {
311 Some(did) => did.clone(),
312 None => {
313 return Json(DelegationAuthResponse {
314 success: false,
315 needs_totp: None,
316 redirect_uri: None,
317 error: Some("No delegated account".to_string()),
318 })
319 .into_response();
320 }
321 };
322
323 let grant = match delegation::get_delegation(&state.db, &delegated_did, &controller_did).await {
324 Ok(Some(g)) => g,
325 _ => {
326 return Json(DelegationAuthResponse {
327 success: false,
328 needs_totp: None,
329 redirect_uri: None,
330 error: Some("Delegation grant not found".to_string()),
331 })
332 .into_response();
333 }
334 };
335
336 let totp_valid =
337 crate::api::server::verify_totp_or_backup_for_user(&state, &controller_did, &form.code)
338 .await;
339 if !totp_valid {
340 return Json(DelegationAuthResponse {
341 success: false,
342 needs_totp: Some(true),
343 redirect_uri: None,
344 error: Some("Invalid TOTP code".to_string()),
345 })
346 .into_response();
347 }
348
349 let ip = extract_client_ip(&headers);
350 let user_agent = headers
351 .get("user-agent")
352 .and_then(|v| v.to_str().ok())
353 .map(|s| s.to_string());
354
355 let _ = delegation::log_delegation_action(
356 &state.db,
357 &delegated_did,
358 &controller_did,
359 Some(&controller_did),
360 delegation::DelegationActionType::TokenIssued,
361 Some(serde_json::json!({
362 "client_id": request.client_id,
363 "granted_scopes": grant.granted_scopes
364 })),
365 Some(&ip),
366 user_agent.as_deref(),
367 )
368 .await;
369
370 Json(DelegationAuthResponse {
371 success: true,
372 needs_totp: None,
373 redirect_uri: Some(format!(
374 "/#/oauth/consent?request_uri={}",
375 urlencoding::encode(&form.request_uri)
376 )),
377 error: None,
378 })
379 .into_response()
380}