Microservice to bring 2FA to self hosted PDSes

little clean up

Changed files
+19 -75
src
+4 -6
src/xrpc/com_atproto_server.rs
··· 120 120 Ok(proxied) 121 121 } 122 122 AuthResult::TokenCheckFailed(err) => match err { 123 - TokenCheckError::InvalidToken => json_error_response( 124 - StatusCode::BAD_REQUEST, 125 - "InvalidToken", 126 - "Hey this token is invalid and this is a custom message to show it's bot a normal PDS", 127 - ), 123 + TokenCheckError::InvalidToken => { 124 + json_error_response(StatusCode::BAD_REQUEST, "InvalidToken", "Token is invalid") 125 + } 128 126 TokenCheckError::ExpiredToken => { 129 127 json_error_response(StatusCode::BAD_REQUEST, "ExpiredToken", "Token is expired") 130 128 } ··· 141 139 ) -> Result<Response<Body>, StatusCode> { 142 140 //If email auth is not set at all it is a update email address 143 141 let email_auth_not_set = payload.email_auth_factor.is_none(); 144 - //If email aurth is set it is to either turn on or off 2fa 142 + //If email auth is set it is to either turn on or off 2fa 145 143 let email_auth_update = payload.email_auth_factor.unwrap_or(false); 146 144 147 145 // Email update asked for
+15 -69
src/xrpc/helpers.rs
··· 77 77 } 78 78 } 79 79 80 - /// Proxy the incoming request as a POST to the PDS base URL plus the provided path and attempt to parse 81 - /// the successful response body as JSON into `T`. 82 - /// 83 - /// Behavior mirrors `proxy_get_json`: 84 - /// - If the proxied response is non-200, returns Passthrough with the original response. 85 - /// - If the response is 200 but JSON parsing fails, returns Passthrough with the original body and headers. 86 - /// - If parsing succeeds, returns Parsed { value, headers }. 87 - pub async fn _proxy_post_json<T>( 88 - state: &AppState, 89 - mut req: Request, 90 - path: &str, 91 - ) -> Result<ProxiedResult<T>, StatusCode> 92 - where 93 - T: DeserializeOwned, 94 - { 95 - let uri = format!("{}{}", state.pds_base_url, path); 96 - *req.uri_mut() = Uri::try_from(uri).map_err(|_| StatusCode::BAD_REQUEST)?; 97 - *req.method_mut() = Method::POST; 98 - 99 - let result = state 100 - .reverse_proxy_client 101 - .request(req) 102 - .await 103 - .map_err(|_| StatusCode::BAD_REQUEST)? 104 - .into_response(); 105 - 106 - if result.status() != StatusCode::OK { 107 - return Ok(ProxiedResult::Passthrough(result)); 108 - } 109 - 110 - let response_headers = result.headers().clone(); 111 - let body = result.into_body(); 112 - let body_bytes = to_bytes(body, usize::MAX) 113 - .await 114 - .map_err(|_| StatusCode::BAD_REQUEST)?; 115 - 116 - match serde_json::from_slice::<T>(&body_bytes) { 117 - Ok(value) => Ok(ProxiedResult::Parsed { 118 - value, 119 - _headers: response_headers, 120 - }), 121 - Err(err) => { 122 - error!(%err, "failed to parse proxied JSON response (POST); returning original body"); 123 - let mut builder = Response::builder().status(StatusCode::OK); 124 - if let Some(headers) = builder.headers_mut() { 125 - *headers = response_headers; 126 - } 127 - let resp = builder 128 - .body(Body::from(body_bytes)) 129 - .map_err(|_| StatusCode::BAD_REQUEST)?; 130 - Ok(ProxiedResult::Passthrough(resp)) 131 - } 132 - } 133 - } 134 - 135 80 /// Build a JSON error response with the required Content-Type header 136 81 /// Content-Type: application/json;charset=utf-8 137 82 /// Body shape: { "error": string, "message": string } ··· 294 239 } 295 240 //Two factor is required and a taken was provided 296 241 if let Some(two_factor_code) = two_factor_code { 297 - //Seems it sends over a empty on login without it set? As in no input is shown on the ui for first login try 242 + //It seems it sends over a empty on login without it set? As in no input is shown on the ui for the first login try 298 243 if two_factor_code != "" { 299 244 return match assert_valid_token( 300 245 &state.account_pool, ··· 313 258 } 314 259 315 260 return match create_two_factor_token(&state.account_pool, did).await { 261 + //TODO replace unwraps with the mythical ? 316 262 Ok(code) => { 317 263 let mut email_data = Map::new(); 318 264 email_data.insert("token".to_string(), Value::from(code.clone())); ··· 372 318 loop { 373 319 let token = get_random_token(); 374 320 let right_now = Utc::now(); 375 - let query = "INSERT INTO email_token (purpose, did, token, requestedAt) 321 + 322 + let res = sqlx::query( 323 + "INSERT INTO email_token (purpose, did, token, requestedAt) 376 324 VALUES (?, ?, ?, ?) 377 325 ON CONFLICT(purpose, did) DO UPDATE SET 378 326 token=excluded.token, 379 - requestedAt=excluded.requestedAt"; 380 - 381 - let res = sqlx::query(query) 382 - .bind(purpose) 383 - .bind(&did) 384 - .bind(&token) 385 - .bind(right_now) 386 - .execute(account_db) 387 - .await; 327 + requestedAt=excluded.requestedAt", 328 + ) 329 + .bind(purpose) 330 + .bind(&did) 331 + .bind(&token) 332 + .bind(right_now) 333 + .execute(account_db) 334 + .await; 388 335 389 336 return match res { 390 337 Ok(_) => Ok(token), ··· 422 369 .await 423 370 .map_err(|err| { 424 371 log::error!("Error getting the 2fa token: {}", err); 425 - TokenCheckError::InvalidToken 372 + InvalidToken 426 373 })?; 427 374 428 375 match row { 429 - None => Err(TokenCheckError::InvalidToken), 376 + None => Err(InvalidToken), 430 377 Some(row) => { 431 378 // Token lives for 15 minutes 432 379 let expiration_ms = 15 * 60_000; 433 380 434 - // Parse requestedAt; assume RFC3339-like string (as created_by PDS or by our code) 435 381 let requested_at_utc = match chrono::DateTime::parse_from_rfc3339(&row.0) { 436 382 Ok(dt) => dt.with_timezone(&Utc), 437 383 Err(_) => {