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}