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 db::set_request_did(&state.db, &form.request_uri, &delegated_did)
92 .await
93 .is_err()
94 {
95 tracing::warn!("Failed to set delegated DID on authorization request");
96 }
97
98 let grant =
99 match delegation::get_delegation(&state.db, &delegated_did, &form.controller_did).await {
100 Ok(Some(g)) => g,
101 Ok(None) => {
102 return Json(DelegationAuthResponse {
103 success: false,
104 needs_totp: None,
105 redirect_uri: None,
106 error: Some("No delegation grant found for this controller".to_string()),
107 })
108 .into_response();
109 }
110 Err(_) => {
111 return Json(DelegationAuthResponse {
112 success: false,
113 needs_totp: None,
114 redirect_uri: None,
115 error: Some("Server error".to_string()),
116 })
117 .into_response();
118 }
119 };
120
121 let controller = match sqlx::query!(
122 r#"
123 SELECT id, did, password_hash, deactivated_at, takedown_ref,
124 email_verified, discord_verified, telegram_verified, signal_verified
125 FROM users
126 WHERE did = $1
127 "#,
128 form.controller_did
129 )
130 .fetch_optional(&state.db)
131 .await
132 {
133 Ok(Some(u)) => u,
134 Ok(None) => {
135 return Json(DelegationAuthResponse {
136 success: false,
137 needs_totp: None,
138 redirect_uri: None,
139 error: Some("Controller account not found".to_string()),
140 })
141 .into_response();
142 }
143 Err(_) => {
144 return Json(DelegationAuthResponse {
145 success: false,
146 needs_totp: None,
147 redirect_uri: None,
148 error: Some("Server error".to_string()),
149 })
150 .into_response();
151 }
152 };
153
154 if controller.deactivated_at.is_some() {
155 return Json(DelegationAuthResponse {
156 success: false,
157 needs_totp: None,
158 redirect_uri: None,
159 error: Some("Controller account is deactivated".to_string()),
160 })
161 .into_response();
162 }
163
164 if controller.takedown_ref.is_some() {
165 return Json(DelegationAuthResponse {
166 success: false,
167 needs_totp: None,
168 redirect_uri: None,
169 error: Some("Controller account has been taken down".to_string()),
170 })
171 .into_response();
172 }
173
174 let password_valid = controller
175 .password_hash
176 .as_ref()
177 .map(|hash| bcrypt::verify(&form.password, hash).unwrap_or_default())
178 .unwrap_or_default();
179
180 if !password_valid {
181 return Json(DelegationAuthResponse {
182 success: false,
183 needs_totp: None,
184 redirect_uri: None,
185 error: Some("Invalid password".to_string()),
186 })
187 .into_response();
188 }
189
190 if db::set_controller_did(&state.db, &form.request_uri, &form.controller_did)
191 .await
192 .is_err()
193 {
194 return Json(DelegationAuthResponse {
195 success: false,
196 needs_totp: None,
197 redirect_uri: None,
198 error: Some("Failed to update authorization request".to_string()),
199 })
200 .into_response();
201 }
202
203 let has_totp = crate::api::server::has_totp_enabled(&state, &form.controller_did).await;
204 if has_totp {
205 return Json(DelegationAuthResponse {
206 success: true,
207 needs_totp: Some(true),
208 redirect_uri: Some(format!(
209 "/app/oauth/delegation-totp?request_uri={}",
210 urlencoding::encode(&form.request_uri)
211 )),
212 error: None,
213 })
214 .into_response();
215 }
216
217 let ip = extract_client_ip(&headers);
218 let user_agent = headers
219 .get("user-agent")
220 .and_then(|v| v.to_str().ok())
221 .map(|s| s.to_string());
222
223 let _ = delegation::log_delegation_action(
224 &state.db,
225 &delegated_did,
226 &form.controller_did,
227 Some(&form.controller_did),
228 delegation::DelegationActionType::TokenIssued,
229 Some(serde_json::json!({
230 "client_id": request.client_id,
231 "granted_scopes": grant.granted_scopes
232 })),
233 Some(&ip),
234 user_agent.as_deref(),
235 )
236 .await;
237
238 Json(DelegationAuthResponse {
239 success: true,
240 needs_totp: None,
241 redirect_uri: Some(format!(
242 "/app/oauth/consent?request_uri={}",
243 urlencoding::encode(&form.request_uri)
244 )),
245 error: None,
246 })
247 .into_response()
248}
249
250#[derive(Debug, Deserialize)]
251pub struct DelegationTotpSubmit {
252 pub request_uri: String,
253 pub code: String,
254}
255
256pub async fn delegation_totp_verify(
257 State(state): State<AppState>,
258 headers: HeaderMap,
259 Json(form): Json<DelegationTotpSubmit>,
260) -> Response {
261 let client_ip = extract_client_ip(&headers);
262 if !state
263 .check_rate_limit(RateLimitKind::TotpVerify, &client_ip)
264 .await
265 {
266 return (
267 StatusCode::TOO_MANY_REQUESTS,
268 Json(DelegationAuthResponse {
269 success: false,
270 needs_totp: None,
271 redirect_uri: None,
272 error: Some("Too many verification attempts. Please try again later.".to_string()),
273 }),
274 )
275 .into_response();
276 }
277
278 let request = match db::get_authorization_request(&state.db, &form.request_uri).await {
279 Ok(Some(r)) => r,
280 Ok(None) => {
281 return Json(DelegationAuthResponse {
282 success: false,
283 needs_totp: None,
284 redirect_uri: None,
285 error: Some("Authorization request not found".to_string()),
286 })
287 .into_response();
288 }
289 Err(_) => {
290 return Json(DelegationAuthResponse {
291 success: false,
292 needs_totp: None,
293 redirect_uri: None,
294 error: Some("Server error".to_string()),
295 })
296 .into_response();
297 }
298 };
299
300 let controller_did = match &request.controller_did {
301 Some(did) => did.clone(),
302 None => {
303 return Json(DelegationAuthResponse {
304 success: false,
305 needs_totp: None,
306 redirect_uri: None,
307 error: Some("Controller not authenticated".to_string()),
308 })
309 .into_response();
310 }
311 };
312
313 let delegated_did = match &request.did {
314 Some(did) => did.clone(),
315 None => {
316 return Json(DelegationAuthResponse {
317 success: false,
318 needs_totp: None,
319 redirect_uri: None,
320 error: Some("No delegated account".to_string()),
321 })
322 .into_response();
323 }
324 };
325
326 let grant = match delegation::get_delegation(&state.db, &delegated_did, &controller_did).await {
327 Ok(Some(g)) => g,
328 _ => {
329 return Json(DelegationAuthResponse {
330 success: false,
331 needs_totp: None,
332 redirect_uri: None,
333 error: Some("Delegation grant not found".to_string()),
334 })
335 .into_response();
336 }
337 };
338
339 let totp_valid =
340 crate::api::server::verify_totp_or_backup_for_user(&state, &controller_did, &form.code)
341 .await;
342 if !totp_valid {
343 return Json(DelegationAuthResponse {
344 success: false,
345 needs_totp: Some(true),
346 redirect_uri: None,
347 error: Some("Invalid TOTP code".to_string()),
348 })
349 .into_response();
350 }
351
352 let ip = extract_client_ip(&headers);
353 let user_agent = headers
354 .get("user-agent")
355 .and_then(|v| v.to_str().ok())
356 .map(|s| s.to_string());
357
358 let _ = delegation::log_delegation_action(
359 &state.db,
360 &delegated_did,
361 &controller_did,
362 Some(&controller_did),
363 delegation::DelegationActionType::TokenIssued,
364 Some(serde_json::json!({
365 "client_id": request.client_id,
366 "granted_scopes": grant.granted_scopes
367 })),
368 Some(&ip),
369 user_agent.as_deref(),
370 )
371 .await;
372
373 Json(DelegationAuthResponse {
374 success: true,
375 needs_totp: None,
376 redirect_uri: Some(format!(
377 "/app/oauth/consent?request_uri={}",
378 urlencoding::encode(&form.request_uri)
379 )),
380 error: None,
381 })
382 .into_response()
383}