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}