···2323#[cfg(test)]
2424pub(super) use bearer::extract_bearer_token;
2525#[cfg(test)]
2626-pub(super) use dpop::{dpop_alg_from_str, jwk_thumbprint, validate_and_consume_nonce, validate_dpop};
2626+pub(super) use dpop::{
2727+ dpop_alg_from_str, jwk_thumbprint, validate_and_consume_nonce, validate_dpop,
2828+};
2729#[cfg(test)]
2830pub(super) use jwt::{parse_scope, peek_jwt_typ, verify_access_token};
2931
+2-8
crates/relay/src/auth/rate_limit.rs
···3434}
35353636/// Record a new failed attempt timestamp for `identifier`.
3737-pub(crate) fn record_failure(
3838- attempts: &mut HashMap<String, VecDeque<Instant>>,
3939- identifier: &str,
4040-) {
3737+pub(crate) fn record_failure(attempts: &mut HashMap<String, VecDeque<Instant>>, identifier: &str) {
4138 attempts
4239 .entry(identifier.to_string())
4340 .or_default()
···4542}
46434744/// Clear the failure history for `identifier` on successful authentication.
4848-pub(crate) fn clear_failures(
4949- attempts: &mut HashMap<String, VecDeque<Instant>>,
5050- identifier: &str,
5151-) {
4545+pub(crate) fn clear_failures(attempts: &mut HashMap<String, VecDeque<Instant>>, identifier: &str) {
5246 attempts.remove(identifier);
5347}
+9-7
crates/relay/src/db/accounts.rs
···5252 ApiError::new(ErrorCode::InternalError, "failed to load account")
5353 })?;
54545555- Ok(row.map(|(email, email_confirmed_at, handle, did_doc)| SessionAccountRow {
5656- did: did.to_string(),
5757- email,
5858- email_confirmed: email_confirmed_at.is_some(),
5959- handle,
6060- did_doc,
6161- }))
5555+ Ok(row.map(
5656+ |(email, email_confirmed_at, handle, did_doc)| SessionAccountRow {
5757+ did: did.to_string(),
5858+ email,
5959+ email_confirmed: email_confirmed_at.is_some(),
6060+ handle,
6161+ did_doc,
6262+ },
6363+ ))
6264}
63656466/// Resolve a handle or DID to an active (non-deactivated) account.
+19-14
crates/relay/src/db/oauth.rs
···542542 .unwrap()
543543 .expect("PAR request should be found on first consume");
544544545545- assert_eq!(row.client_id, "https://app.example.com/client-metadata.json");
545545+ assert_eq!(
546546+ row.client_id,
547547+ "https://app.example.com/client-metadata.json"
548548+ );
546549 assert!(row.request_parameters.contains("redirect_uri"));
547550548551 // Second consume must return None — single-use enforcement (RFC 9126 §4).
549549- let second = consume_par_request(&pool, "urn:ietf:params:oauth:request_uri:test-token-abc123")
550550- .await
551551- .unwrap();
552552- assert!(second.is_none(), "consumed PAR request must not be found again");
552552+ let second =
553553+ consume_par_request(&pool, "urn:ietf:params:oauth:request_uri:test-token-abc123")
554554+ .await
555555+ .unwrap();
556556+ assert!(
557557+ second.is_none(),
558558+ "consumed PAR request must not be found again"
559559+ );
553560 }
554561555562 #[tokio::test]
···584591 .await
585592 .unwrap();
586593587587- let result =
588588- consume_par_request(&pool, "urn:ietf:params:oauth:request_uri:expired-token")
589589- .await
590590- .unwrap();
594594+ let result = consume_par_request(&pool, "urn:ietf:params:oauth:request_uri:expired-token")
595595+ .await
596596+ .unwrap();
591597 assert!(result.is_none(), "expired PAR request must return None");
592598 }
593599···626632627633 cleanup_expired_par_requests(&pool).await.unwrap();
628634629629- let count: (i64,) =
630630- sqlx::query_as("SELECT COUNT(*) FROM oauth_par_requests")
631631- .fetch_one(&pool)
632632- .await
633633- .unwrap();
635635+ let count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM oauth_par_requests")
636636+ .fetch_one(&pool)
637637+ .await
638638+ .unwrap();
634639 assert_eq!(count.0, 1, "only the valid PAR request should remain");
635640 }
636641
+3-2
crates/relay/src/routes/create_session.rs
···215215 }
216216217217 // ATProto spec: "handle.invalid" is the sentinel for accounts without a resolvable handle.
218218- let handle = account.handle.unwrap_or_else(|| "handle.invalid".to_string());
218218+ let handle = account
219219+ .handle
220220+ .unwrap_or_else(|| "handle.invalid".to_string());
219221220222 Ok((
221223 StatusCode::OK,
···228230 }),
229231 ))
230232}
231231-232233233234/// Sign an HS256 access JWT with a 2-hour lifetime.
234235fn issue_access_jwt(secret: &[u8; 32], did: &str, aud: &str, now: u64) -> Result<String, ApiError> {
+46-13
crates/relay/src/routes/get_session.rs
···5959 });
60606161 // ATProto spec: "handle.invalid" is the sentinel for accounts without a resolvable handle.
6262- let handle = account.handle.unwrap_or_else(|| "handle.invalid".to_string());
6262+ let handle = account
6363+ .handle
6464+ .unwrap_or_else(|| "handle.invalid".to_string());
63656466 Ok(Json(GetSessionResponse {
6567 did: account.did,
···197199 #[tokio::test]
198200 async fn valid_token_returns_session_info() {
199201 let state = test_state().await;
200200- insert_account(&state.db, "did:plc:alice", "alice.test.example.com", "alice@example.com")
201201- .await;
202202+ insert_account(
203203+ &state.db,
204204+ "did:plc:alice",
205205+ "alice.test.example.com",
206206+ "alice@example.com",
207207+ )
208208+ .await;
202209 let token = access_jwt(&state.jwt_secret, "did:plc:alice");
203210204211 let response = app(state)
···212219 assert_eq!(json["handle"], "alice.test.example.com");
213220 assert_eq!(json["email"], "alice@example.com");
214221 assert_eq!(json["emailConfirmed"], false);
215215- assert!(json.get("didDoc").is_none(), "didDoc absent when no document stored");
222222+ assert!(
223223+ json.get("didDoc").is_none(),
224224+ "didDoc absent when no document stored"
225225+ );
216226 }
217227218228 #[tokio::test]
219229 async fn confirmed_email_returns_true() {
220230 let state = test_state().await;
221221- insert_account(&state.db, "did:plc:confirmed", "conf.test.example.com", "conf@example.com")
222222- .await;
231231+ insert_account(
232232+ &state.db,
233233+ "did:plc:confirmed",
234234+ "conf.test.example.com",
235235+ "conf@example.com",
236236+ )
237237+ .await;
223238 sqlx::query("UPDATE accounts SET email_confirmed_at = datetime('now') WHERE did = ?")
224239 .bind("did:plc:confirmed")
225240 .execute(&state.db)
···240255 #[tokio::test]
241256 async fn did_doc_included_when_present() {
242257 let state = test_state().await;
243243- insert_account(&state.db, "did:plc:withdoc", "doc.test.example.com", "doc@example.com")
244244- .await;
258258+ insert_account(
259259+ &state.db,
260260+ "did:plc:withdoc",
261261+ "doc.test.example.com",
262262+ "doc@example.com",
263263+ )
264264+ .await;
245265 let doc = serde_json::json!({"id": "did:plc:withdoc", "@context": ["https://www.w3.org/ns/did/v1"]});
246266 insert_did_doc(&state.db, "did:plc:withdoc", doc.clone()).await;
247267 let token = access_jwt(&state.jwt_secret, "did:plc:withdoc");
···304324 #[tokio::test]
305325 async fn refresh_token_returns_401() {
306326 let state = test_state().await;
307307- insert_account(&state.db, "did:plc:refresh", "refresh.test.example.com", "r@example.com")
308308- .await;
327327+ insert_account(
328328+ &state.db,
329329+ "did:plc:refresh",
330330+ "refresh.test.example.com",
331331+ "r@example.com",
332332+ )
333333+ .await;
309334 let token = refresh_jwt(&state.jwt_secret, "did:plc:refresh");
310335311336 let response = app(state)
···321346 #[tokio::test]
322347 async fn deactivated_account_returns_401_with_invalid_token_code() {
323348 let state = test_state().await;
324324- insert_account(&state.db, "did:plc:deact", "deact.test.example.com", "deact@example.com")
325325- .await;
349349+ insert_account(
350350+ &state.db,
351351+ "did:plc:deact",
352352+ "deact.test.example.com",
353353+ "deact@example.com",
354354+ )
355355+ .await;
326356 sqlx::query("UPDATE accounts SET deactivated_at = datetime('now') WHERE did = ?")
327357 .bind("did:plc:deact")
328358 .execute(&state.db)
···432462433463 assert_eq!(response.status(), StatusCode::OK);
434464 let json = body_json(response).await;
435435- assert!(json.get("didDoc").is_none(), "malformed didDoc must be omitted");
465465+ assert!(
466466+ json.get("didDoc").is_none(),
467467+ "malformed didDoc must be omitted"
468468+ );
436469 }
437470438471 #[tokio::test]
+8-3
crates/relay/src/routes/oauth_authorize.rs
···1919use crate::auth::password::{verify_password, VerifyResult, TIMING_DUMMY_HASH};
2020use crate::auth::rate_limit::{clear_failures, is_rate_limited, record_failure};
2121use crate::db::accounts::resolve_identifier;
2222-use crate::db::oauth::{consume_par_request, get_oauth_client, store_authorization_code, StoredPARParams};
2222+use crate::db::oauth::{
2323+ consume_par_request, get_oauth_client, store_authorization_code, StoredPARParams,
2424+};
2325use crate::routes::oauth_templates::{
2426 encode_param, error_page, error_redirect, render_consent_page,
2527};
···116118 if let Some(uri) = raw.request_uri {
117119 let row = match consume_par_request(&state.db, &uri).await {
118120 Ok(Some(r)) => r,
119119- Ok(None) => return Err(ResolveError::Client("request_uri is invalid or has expired")),
121121+ Ok(None) => {
122122+ return Err(ResolveError::Client(
123123+ "request_uri is invalid or has expired",
124124+ ))
125125+ }
120126 Err(e) => {
121127 tracing::error!(error = %e, "db error consuming PAR request");
122128 return Err(ResolveError::Server(
···566572 );
567573 Redirect::to(&redirect_url).into_response()
568574}
569569-570575571576#[cfg(test)]
572577mod tests {
+19-12
crates/relay/src/routes/oauth_par.rs
···8989pub async fn post_par(State(state): State<AppState>, Form(form): Form<PARForm>) -> Response {
9090 let client_id = match form.client_id.as_deref().filter(|s| !s.is_empty()) {
9191 Some(id) => id.to_string(),
9292- None => {
9393- return PARError::new("invalid_request", "client_id is required").into_response()
9494- }
9292+ None => return PARError::new("invalid_request", "client_id is required").into_response(),
9593 };
96949795 let redirect_uri = match form.redirect_uri.as_deref().filter(|s| !s.is_empty()) {
···108106 }
109107 };
110108111111- let code_challenge_method = match form.code_challenge_method.as_deref().filter(|s| !s.is_empty()) {
109109+ let code_challenge_method = match form
110110+ .code_challenge_method
111111+ .as_deref()
112112+ .filter(|s| !s.is_empty())
113113+ {
112114 Some(m) => m.to_string(),
113115 None => {
114116 return PARError::new("invalid_request", "code_challenge_method is required")
···185187 }
186188187189 if code_challenge_method != "S256" {
188188- return PARError::new(
189189- "invalid_request",
190190- "code_challenge_method must be S256",
191191- )
192192- .into_response();
190190+ return PARError::new("invalid_request", "code_challenge_method must be S256")
191191+ .into_response();
193192 }
194193195194 let params = StoredPARParams {
···315314 .unwrap();
316315 let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
317316318318- let request_uri = json["request_uri"].as_str().expect("request_uri must be present");
317317+ let request_uri = json["request_uri"]
318318+ .as_str()
319319+ .expect("request_uri must be present");
319320 assert!(
320321 request_uri.starts_with("urn:ietf:params:oauth:request_uri:"),
321322 "request_uri must use the OAuth PAR URN scheme"
···358359 .method("POST")
359360 .uri("/oauth/par")
360361 .header("content-type", "application/x-www-form-urlencoded")
361361- .body(Body::from(par_body(&[("redirect_uri", "https://evil.example.com/cb")])))
362362+ .body(Body::from(par_body(&[(
363363+ "redirect_uri",
364364+ "https://evil.example.com/cb",
365365+ )])))
362366 .unwrap(),
363367 )
364368 .await
···604608 let uri1 = call_par(state1, par_body(&[])).await;
605609 let uri2 = call_par(state2, par_body(&[])).await;
606610607607- assert_ne!(uri1, uri2, "each PAR call must produce a unique request_uri");
611611+ assert_ne!(
612612+ uri1, uri2,
613613+ "each PAR call must produce a unique request_uri"
614614+ );
608615 }
609616610617 #[tokio::test]