Our Personal Data Server from scratch!
0

Configure Feed

Select the types of activity you want to include in your feed.

1use super::*; 2 3#[derive(Debug, Serialize)] 4pub struct ScopeInfo { 5 pub scope: String, 6 pub category: String, 7 pub required: bool, 8 pub description: String, 9 pub display_name: String, 10 pub granted: Option<bool>, 11} 12 13#[derive(Debug, Serialize)] 14pub struct ConsentResponse { 15 pub request_uri: String, 16 pub client_id: ClientId, 17 pub client_name: Option<String>, 18 pub client_uri: Option<String>, 19 pub logo_uri: Option<String>, 20 pub scopes: Vec<ScopeInfo>, 21 pub show_consent: bool, 22 pub did: Did, 23 #[serde(skip_serializing_if = "Option::is_none")] 24 pub handle: Option<String>, 25 #[serde(skip_serializing_if = "Option::is_none")] 26 pub is_delegation: Option<bool>, 27 #[serde(skip_serializing_if = "Option::is_none")] 28 pub controller_did: Option<String>, 29 #[serde(skip_serializing_if = "Option::is_none")] 30 pub controller_handle: Option<String>, 31 #[serde(skip_serializing_if = "Option::is_none")] 32 pub delegation_level: Option<String>, 33} 34 35#[derive(Debug, Deserialize)] 36pub struct ConsentQuery { 37 pub request_uri: String, 38} 39 40#[derive(Debug, Deserialize)] 41pub struct ConsentSubmit { 42 pub request_uri: String, 43 pub approved_scopes: Vec<String>, 44 pub remember: bool, 45} 46 47pub async fn consent_get( 48 State(state): State<AppState>, 49 Query(query): Query<ConsentQuery>, 50) -> Response { 51 let consent_request_id = RequestId::from(query.request_uri.clone()); 52 let request_data = match state 53 .repos 54 .oauth 55 .get_authorization_request(&consent_request_id) 56 .await 57 { 58 Ok(Some(data)) => data, 59 Ok(None) => { 60 return json_error( 61 StatusCode::BAD_REQUEST, 62 "invalid_request", 63 "Invalid or expired request_uri", 64 ); 65 } 66 Err(e) => { 67 return json_error( 68 StatusCode::INTERNAL_SERVER_ERROR, 69 "server_error", 70 &format!("Database error: {:?}", e), 71 ); 72 } 73 }; 74 let flow_with_user = match AuthFlow::from_request_data(request_data.clone()) { 75 Ok(flow) => match flow.require_user() { 76 Ok(u) => u, 77 Err(_) => { 78 return json_error(StatusCode::FORBIDDEN, "access_denied", "Not authenticated"); 79 } 80 }, 81 Err(_) => { 82 return json_error( 83 StatusCode::BAD_REQUEST, 84 "expired_request", 85 "Authorization request has expired", 86 ); 87 } 88 }; 89 90 let did = flow_with_user.did().clone(); 91 let client_cache = ClientMetadataCache::new(3600); 92 let client_metadata = client_cache 93 .get(&request_data.parameters.client_id) 94 .await 95 .ok(); 96 let requested_scope_str = request_data 97 .parameters 98 .scope 99 .as_deref() 100 .filter(|s| !s.trim().is_empty()) 101 .unwrap_or("atproto"); 102 103 let controller_did_parsed: Option<Did> = request_data 104 .controller_did 105 .as_ref() 106 .and_then(|s| s.parse().ok()); 107 let delegation_grant = if let Some(ref ctrl_did) = controller_did_parsed { 108 state 109 .repos 110 .delegation 111 .get_delegation(&did, ctrl_did) 112 .await 113 .ok() 114 .flatten() 115 } else { 116 None 117 }; 118 119 let effective_scope_str = if let Some(ref grant) = delegation_grant { 120 tranquil_pds::delegation::intersect_scopes( 121 requested_scope_str, 122 grant.granted_scopes.as_str(), 123 ) 124 } else { 125 requested_scope_str.to_string() 126 }; 127 128 let expanded_scope_str = match expand_include_scopes(&effective_scope_str).await { 129 Ok(s) => s, 130 Err(e) => { 131 return json_error( 132 StatusCode::BAD_REQUEST, 133 "invalid_scope", 134 &format!("Failed to expand permission set: {e}"), 135 ); 136 } 137 }; 138 let requested_scopes: Vec<&str> = expanded_scope_str.split_whitespace().collect(); 139 let preferences = state 140 .repos 141 .oauth 142 .get_scope_preferences(&did, &request_data.parameters.client_id) 143 .await 144 .unwrap_or_default(); 145 let pref_map: std::collections::HashMap<_, _> = preferences 146 .iter() 147 .map(|p| (p.scope.as_str(), p.granted)) 148 .collect(); 149 let requested_scope_strings: Vec<String> = 150 requested_scopes.iter().map(|s| s.to_string()).collect(); 151 let show_consent = should_show_consent( 152 state.repos.oauth.as_ref(), 153 &did, 154 &request_data.parameters.client_id, 155 &requested_scope_strings, 156 ) 157 .await 158 .unwrap_or(true); 159 let has_granular_scopes = requested_scopes.iter().any(|s| is_granular_scope(s)); 160 let scopes: Vec<ScopeInfo> = requested_scopes 161 .iter() 162 .map(|scope| { 163 let (category, required, description, display_name) = if let Some(def) = 164 tranquil_pds::oauth::scopes::SCOPE_DEFINITIONS.get(*scope) 165 { 166 let desc = if *scope == "atproto" && has_granular_scopes { 167 "AT Protocol baseline scope (permissions determined by selected options below)" 168 .to_string() 169 } else { 170 def.description.to_string() 171 }; 172 let name = if *scope == "atproto" && has_granular_scopes { 173 "AT Protocol Access".to_string() 174 } else { 175 def.display_name.to_string() 176 }; 177 ( 178 def.category.display_name().to_string(), 179 def.required, 180 desc, 181 name, 182 ) 183 } else if scope.starts_with("ref:") { 184 ( 185 "Reference".to_string(), 186 false, 187 "Referenced scope".to_string(), 188 scope.to_string(), 189 ) 190 } else { 191 ( 192 "Other".to_string(), 193 false, 194 format!("Access to {}", scope), 195 scope.to_string(), 196 ) 197 }; 198 let granted = pref_map.get(*scope).copied(); 199 ScopeInfo { 200 scope: scope.to_string(), 201 category, 202 required, 203 description, 204 display_name, 205 granted, 206 } 207 }) 208 .collect(); 209 210 let account_handle = state 211 .repos 212 .user 213 .get_handle_by_did(&did) 214 .await 215 .ok() 216 .flatten() 217 .map(|h| h.to_string()); 218 219 let (is_delegation, controller_did_resp, controller_handle, delegation_level) = 220 if let Some(ref ctrl_did) = controller_did_parsed { 221 let ctrl_handle = state 222 .repos 223 .user 224 .get_handle_by_did(ctrl_did) 225 .await 226 .ok() 227 .flatten() 228 .map(|h| h.to_string()); 229 230 let level = if let Some(ref grant) = delegation_grant { 231 let preset = tranquil_pds::delegation::SCOPE_PRESETS 232 .iter() 233 .find(|p| p.scopes == grant.granted_scopes.as_str()); 234 preset 235 .map(|p| p.label.to_string()) 236 .unwrap_or_else(|| "Custom".to_string()) 237 } else { 238 "Unknown".to_string() 239 }; 240 241 ( 242 Some(true), 243 Some(ctrl_did.to_string()), 244 ctrl_handle, 245 Some(level), 246 ) 247 } else { 248 (None, None, None, None) 249 }; 250 251 Json(ConsentResponse { 252 request_uri: query.request_uri.clone(), 253 client_id: request_data.parameters.client_id.clone(), 254 client_name: client_metadata.as_ref().and_then(|m| m.client_name.clone()), 255 client_uri: client_metadata.as_ref().and_then(|m| m.client_uri.clone()), 256 logo_uri: client_metadata.as_ref().and_then(|m| m.logo_uri.clone()), 257 scopes, 258 show_consent, 259 did: did.clone(), 260 handle: account_handle, 261 is_delegation, 262 controller_did: controller_did_resp, 263 controller_handle, 264 delegation_level, 265 }) 266 .into_response() 267} 268 269pub async fn consent_post( 270 State(state): State<AppState>, 271 Json(form): Json<ConsentSubmit>, 272) -> Response { 273 tracing::info!( 274 "consent_post: approved_scopes={:?}, remember={}", 275 form.approved_scopes, 276 form.remember 277 ); 278 let consent_post_request_id = RequestId::from(form.request_uri.clone()); 279 let request_data = match state 280 .repos 281 .oauth 282 .get_authorization_request(&consent_post_request_id) 283 .await 284 { 285 Ok(Some(data)) => data, 286 Ok(None) => { 287 return json_error( 288 StatusCode::BAD_REQUEST, 289 "invalid_request", 290 "Invalid or expired request_uri", 291 ); 292 } 293 Err(e) => { 294 return json_error( 295 StatusCode::INTERNAL_SERVER_ERROR, 296 "server_error", 297 &format!("Database error: {:?}", e), 298 ); 299 } 300 }; 301 let flow_with_user = match AuthFlow::from_request_data(request_data.clone()) { 302 Ok(flow) => match flow.require_user() { 303 Ok(u) => u, 304 Err(_) => { 305 return json_error(StatusCode::FORBIDDEN, "access_denied", "Not authenticated"); 306 } 307 }, 308 Err(_) => { 309 let _ = state 310 .repos 311 .oauth 312 .delete_authorization_request(&consent_post_request_id) 313 .await; 314 return json_error( 315 StatusCode::BAD_REQUEST, 316 "invalid_request", 317 "Authorization request has expired", 318 ); 319 } 320 }; 321 322 let did = flow_with_user.did().clone(); 323 let original_scope_str = request_data 324 .parameters 325 .scope 326 .as_deref() 327 .unwrap_or("atproto"); 328 329 let controller_did_parsed: Option<Did> = request_data 330 .controller_did 331 .as_ref() 332 .and_then(|s| s.parse().ok()); 333 334 let delegation_grant = match controller_did_parsed.as_ref() { 335 Some(ctrl_did) => state 336 .repos 337 .delegation 338 .get_delegation(&did, ctrl_did) 339 .await 340 .ok() 341 .flatten(), 342 None => None, 343 }; 344 345 let effective_scope_str = if let Some(ref grant) = delegation_grant { 346 tranquil_pds::delegation::intersect_scopes( 347 original_scope_str, 348 grant.granted_scopes.as_str(), 349 ) 350 } else { 351 original_scope_str.to_string() 352 }; 353 let requested_scopes: Vec<&str> = effective_scope_str.split_whitespace().collect(); 354 let atproto_was_requested = requested_scopes.contains(&"atproto"); 355 if atproto_was_requested && !form.approved_scopes.contains(&"atproto".to_string()) { 356 return json_error( 357 StatusCode::BAD_REQUEST, 358 "invalid_request", 359 "The atproto scope was requested and must be approved", 360 ); 361 } 362 let final_approved: Vec<String> = form.approved_scopes.clone(); 363 if final_approved.is_empty() { 364 return json_error( 365 StatusCode::BAD_REQUEST, 366 "invalid_request", 367 "At least one scope must be approved", 368 ); 369 } 370 let approved_scope_str = final_approved.join(" "); 371 let has_valid_scope = final_approved.iter().all(|s| is_valid_scope(s)); 372 if !has_valid_scope { 373 return json_error( 374 StatusCode::BAD_REQUEST, 375 "invalid_request", 376 "Invalid scope format", 377 ); 378 } 379 if form.remember { 380 let preferences: Vec<ScopePreference> = requested_scopes 381 .iter() 382 .map(|s| ScopePreference { 383 scope: s.to_string(), 384 granted: form.approved_scopes.contains(&s.to_string()), 385 }) 386 .collect(); 387 let _ = state 388 .repos 389 .oauth 390 .upsert_scope_preferences(&did, &request_data.parameters.client_id, &preferences) 391 .await; 392 } 393 if let Err(e) = state 394 .repos 395 .oauth 396 .update_request_scope(&consent_post_request_id, &approved_scope_str) 397 .await 398 { 399 tracing::warn!("Failed to update request scope: {:?}", e); 400 } 401 let code = AuthorizationCode::generate(); 402 if state 403 .repos 404 .oauth 405 .update_authorization_request( 406 &consent_post_request_id, 407 &did, 408 request_data.device_id.as_ref(), 409 &code, 410 ) 411 .await 412 .is_err() 413 { 414 return json_error( 415 StatusCode::INTERNAL_SERVER_ERROR, 416 "server_error", 417 "Failed to complete authorization", 418 ); 419 } 420 let redirect_uri = &request_data.parameters.redirect_uri; 421 let intermediate_url = build_intermediate_redirect_url( 422 redirect_uri, 423 code.as_str(), 424 request_data.parameters.state.as_deref(), 425 request_data.parameters.response_mode.map(|m| m.as_str()), 426 ); 427 tracing::info!( 428 intermediate_url = %intermediate_url, 429 client_redirect = %redirect_uri, 430 "consent_post returning JSON with intermediate URL (for 303 redirect)" 431 ); 432 Json(serde_json::json!({ "redirect_uri": intermediate_url })).into_response() 433} 434 435#[derive(Debug, Deserialize)] 436pub struct RenewRequest { 437 pub request_uri: String, 438} 439 440pub async fn authorize_renew( 441 State(state): State<AppState>, 442 _rate_limit: OAuthRateLimited<OAuthAuthorizeLimit>, 443 Json(form): Json<RenewRequest>, 444) -> Response { 445 let request_id = RequestId::from(form.request_uri.clone()); 446 let request_data = match state 447 .repos 448 .oauth 449 .get_authorization_request(&request_id) 450 .await 451 { 452 Ok(Some(data)) => data, 453 Ok(None) => { 454 return json_error( 455 StatusCode::BAD_REQUEST, 456 "invalid_request", 457 "Unknown authorization request", 458 ); 459 } 460 Err(_) => { 461 return json_error( 462 StatusCode::INTERNAL_SERVER_ERROR, 463 "server_error", 464 "Database error", 465 ); 466 } 467 }; 468 469 if request_data.did.is_none() { 470 return json_error( 471 StatusCode::BAD_REQUEST, 472 "invalid_request", 473 "Authorization request not yet authenticated", 474 ); 475 } 476 477 let now = Utc::now(); 478 if request_data.expires_at >= now { 479 return Json(serde_json::json!({ 480 "request_uri": form.request_uri, 481 "renewed": false 482 })) 483 .into_response(); 484 } 485 486 let staleness = now - request_data.expires_at; 487 if staleness.num_seconds() > MAX_RENEWAL_STALENESS_SECONDS { 488 let _ = state 489 .repos 490 .oauth 491 .delete_authorization_request(&request_id) 492 .await; 493 return json_error( 494 StatusCode::BAD_REQUEST, 495 "invalid_request", 496 "Authorization request expired too long ago to renew", 497 ); 498 } 499 500 let new_expires_at = now + chrono::Duration::seconds(RENEW_EXPIRY_SECONDS); 501 match state 502 .repos 503 .oauth 504 .extend_authorization_request_expiry(&request_id, new_expires_at) 505 .await 506 { 507 Ok(true) => Json(serde_json::json!({ 508 "request_uri": form.request_uri, 509 "renewed": true 510 })) 511 .into_response(), 512 Ok(false) => json_error( 513 StatusCode::BAD_REQUEST, 514 "invalid_request", 515 "Authorization request could not be renewed", 516 ), 517 Err(_) => json_error( 518 StatusCode::INTERNAL_SERVER_ERROR, 519 "server_error", 520 "Database error", 521 ), 522 } 523}