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