···5253 // N.B: We ignore all fields inside of the token up until this point because they can be
54 // attacker-controlled.
55- let (typ, claims) = verify(&state.signing_key.did(), token).map_err(|e| {
56- Error::with_status(
57- StatusCode::UNAUTHORIZED,
58- e.context("failed to verify auth token"),
59- )
60- }).context("token auth should be verify")?;
006162 // Ensure this is an authentication token.
63 if typ != "at+jwt" {
···92 let _status = sqlx::query_scalar!(r#"SELECT status FROM accounts WHERE did = ?"#, did)
93 .fetch_one(&state.db)
94 .await
95- .with_context(|| format!("failed to query account {did}")).context("should fetch account status")?;
09697 Ok(Self {
98 did: did.to_owned(),
···5253 // N.B: We ignore all fields inside of the token up until this point because they can be
54 // attacker-controlled.
55+ let (typ, claims) = verify(&state.signing_key.did(), token)
56+ .map_err(|e| {
57+ Error::with_status(
58+ StatusCode::UNAUTHORIZED,
59+ e.context("failed to verify auth token"),
60+ )
61+ })
62+ .context("token auth should be verify")?;
6364 // Ensure this is an authentication token.
65 if typ != "at+jwt" {
···94 let _status = sqlx::query_scalar!(r#"SELECT status FROM accounts WHERE did = ?"#, did)
95 .fetch_one(&state.db)
96 .await
97+ .with_context(|| format!("failed to query account {did}"))
98+ .context("should fetch account status")?;
99100 Ok(Self {
101 did: did.to_owned(),
+1-2
src/endpoints/identity.rs
···153 .await
154 .context("failed to open did doc")?;
155156- let op_bytes = serde_ipld_dagcbor::to_vec(&op)
157- .context("failed to encode plc op")?;
158159 let plc_cid = CarStore::open(doc)
160 .await
···153 .await
154 .context("failed to open did doc")?;
155156+ let op_bytes = serde_ipld_dagcbor::to_vec(&op).context("failed to encode plc op")?;
0157158 let plc_cid = CarStore::open(doc)
159 .await
+10-32
src/endpoints/repo.rs
···169 if let (Some(blob_type), Some(blob_ref)) = (map.get("$type"), map.get("ref")) {
170 if blob_type == &serde_json::Value::String("blob".to_owned()) {
171 if let Ok(rf) = serde_json::from_value::<BlobRef>(blob_ref.clone()) {
172- cids.push(
173- Cid::from_str(&rf.link)
174- .context("failed to convert cid")?,
175- );
176 }
177 }
178 }
···398 .context("failed to extract key")?;
399 }
400401- let mut tx = db
402- .begin()
403- .await
404- .context("failed to begin transaction")?;
405406 if !swap_commit(
407 &mut *tx,
···638 };
639 Ok(Json(
640 repo::put_record::OutputData {
641- cid: cid
642- .context("missing cid")?,
643 commit: write_result.commit.to_owned(),
644- uri: uri
645- .context("missing uri")?,
646 validation_status: Some("unknown".to_owned()),
647 }
648 .into(),
···710711 let mut tree = repo.tree();
712 let mut it = Box::pin(tree.keys());
713- while let Some(key) = it
714- .try_next()
715- .await
716- .context("failed to iterate repo keys")?
717- {
718 if let Some((collection, _rkey)) = key.split_once('/') {
719 _ = collections.insert(collection.to_owned());
720 }
···764 .await
765 .context("failed to find record")?;
766767- let record: Option<serde_json::Value> = repo
768- .get_raw(&key)
769- .await
770- .context("failed to read record")?;
771772 record.map_or_else(
773 || {
···898 let mut len = 0_usize;
899 let mut sha = Sha256::new();
900 let mut stream = request.into_body().into_data_stream();
901- while let Some(bytes) = stream
902- .try_next()
903- .await
904- .context("failed to receive file")?
905- {
906- len = len
907- .checked_add(bytes.len())
908- .context("size overflow")?;
909910 // Deal with any sneaky end-users trying to bypass size limitations.
911- let len_u64: u64 = len
912- .try_into()
913- .context("failed to convert `len`")?;
914 if len_u64 > config.blob.limit {
915 drop(file);
916 tokio::fs::remove_file(&filename)
···169 if let (Some(blob_type), Some(blob_ref)) = (map.get("$type"), map.get("ref")) {
170 if blob_type == &serde_json::Value::String("blob".to_owned()) {
171 if let Ok(rf) = serde_json::from_value::<BlobRef>(blob_ref.clone()) {
172+ cids.push(Cid::from_str(&rf.link).context("failed to convert cid")?);
000173 }
174 }
175 }
···395 .context("failed to extract key")?;
396 }
397398+ let mut tx = db.begin().await.context("failed to begin transaction")?;
000399400 if !swap_commit(
401 &mut *tx,
···632 };
633 Ok(Json(
634 repo::put_record::OutputData {
635+ cid: cid.context("missing cid")?,
0636 commit: write_result.commit.to_owned(),
637+ uri: uri.context("missing uri")?,
0638 validation_status: Some("unknown".to_owned()),
639 }
640 .into(),
···702703 let mut tree = repo.tree();
704 let mut it = Box::pin(tree.keys());
705+ while let Some(key) = it.try_next().await.context("failed to iterate repo keys")? {
0000706 if let Some((collection, _rkey)) = key.split_once('/') {
707 _ = collections.insert(collection.to_owned());
708 }
···752 .await
753 .context("failed to find record")?;
754755+ let record: Option<serde_json::Value> =
756+ repo.get_raw(&key).await.context("failed to read record")?;
00757758 record.map_or_else(
759 || {
···884 let mut len = 0_usize;
885 let mut sha = Sha256::new();
886 let mut stream = request.into_body().into_data_stream();
887+ while let Some(bytes) = stream.try_next().await.context("failed to receive file")? {
888+ len = len.checked_add(bytes.len()).context("size overflow")?;
000000889890 // Deal with any sneaky end-users trying to bypass size limitations.
891+ let len_u64: u64 = len.try_into().context("failed to convert `len`")?;
00892 if len_u64 > config.blob.limit {
893 drop(file);
894 tokio::fs::remove_file(&filename)
+11-23
src/endpoints/server.rs
···79 State(fhp): State<FirehoseProducer>,
80 Json(input): Json<server::create_account::Input>,
81) -> Result<Json<server::create_account::Output>> {
82- let email = input
83- .email
84- .as_deref()
85- .context("no email provided")?;
86 // Hash the user's password.
87 let pass = Argon2::default()
88 .hash_password(
···124125 // Begin a new transaction to actually create the user's profile.
126 // Unless committed, the transaction will be automatically rolled back.
127- let mut tx = db
128- .begin()
129- .await
130- .context("failed to begin transaction")?;
131132 let _invite = match input.invite_code {
133 Some(ref code) => {
···145 .await
146 .context("failed to check invite code")?;
147148- invite
149- .context("invalid invite code")?
150 }
151 None => {
152 return Err(anyhow!("invite code required").into());
···173 )
174 .await
175 .context("failed to sign genesis op")?;
176- let op_bytes = serde_ipld_dagcbor::to_vec(&op)
177- .context("failed to encode genesis op")?;
178179 let did_hash = {
180 let digest = base32::encode(
···283 .context("failed to create new account")?;
284285 // The account is fully created. Commit the SQL transaction to the database.
286- tx.commit()
287- .await
288- .context("failed to commit transaction")?;
289290 // Broadcast the identity event now that the new identity is resolvable on the public directory.
291 fhp.identity(
···415416 match Argon2::default().verify_password(
417 password.as_bytes(),
418- &PasswordHash::new(account.password.as_str())
419- .context("invalid password hash in db")?,
420 ) {
421 Ok(_) => {}
422 Err(_e) => {
···482 req: Request,
483) -> Result<Json<server::refresh_session::Output>> {
484 // TODO: store hashes of refresh tokens and enforce single-use
485- let auth = req
486 .headers()
487 .get(axum::http::header::AUTHORIZATION)
488- .context("no authorization header provided")?;
489- let token = auth
490 .to_str()
491 .ok()
492 .and_then(|auth| auth.strip_prefix("Bearer "))
493 .context("invalid authentication token")?;
494495- let (typ, claims) = auth::verify(&skey.did(), token)
496- .context("failed to verify refresh token")?;
497 if typ != "refresh+jwt" {
498 return Err(Error::with_status(
499 StatusCode::UNAUTHORIZED,
···630 State(db): State<Db>,
631) -> Result<Json<server::get_session::Output>> {
632 let did = user.did();
633-634 if let Some(user) = sqlx::query!(
635 r#"
636 SELECT a.email, a.status, (
···79 State(fhp): State<FirehoseProducer>,
80 Json(input): Json<server::create_account::Input>,
81) -> Result<Json<server::create_account::Output>> {
82+ let email = input.email.as_deref().context("no email provided")?;
00083 // Hash the user's password.
84 let pass = Argon2::default()
85 .hash_password(
···121122 // Begin a new transaction to actually create the user's profile.
123 // Unless committed, the transaction will be automatically rolled back.
124+ let mut tx = db.begin().await.context("failed to begin transaction")?;
000125126 let _invite = match input.invite_code {
127 Some(ref code) => {
···139 .await
140 .context("failed to check invite code")?;
141142+ invite.context("invalid invite code")?
0143 }
144 None => {
145 return Err(anyhow!("invite code required").into());
···166 )
167 .await
168 .context("failed to sign genesis op")?;
169+ let op_bytes = serde_ipld_dagcbor::to_vec(&op).context("failed to encode genesis op")?;
0170171 let did_hash = {
172 let digest = base32::encode(
···275 .context("failed to create new account")?;
276277 // The account is fully created. Commit the SQL transaction to the database.
278+ tx.commit().await.context("failed to commit transaction")?;
00279280 // Broadcast the identity event now that the new identity is resolvable on the public directory.
281 fhp.identity(
···405406 match Argon2::default().verify_password(
407 password.as_bytes(),
408+ &PasswordHash::new(account.password.as_str()).context("invalid password hash in db")?,
0409 ) {
410 Ok(_) => {}
411 Err(_e) => {
···471 req: Request,
472) -> Result<Json<server::refresh_session::Output>> {
473 // TODO: store hashes of refresh tokens and enforce single-use
474+ let auth_token = req
475 .headers()
476 .get(axum::http::header::AUTHORIZATION)
477+ .context("no authorization header provided")?
0478 .to_str()
479 .ok()
480 .and_then(|auth| auth.strip_prefix("Bearer "))
481 .context("invalid authentication token")?;
482483+ let (typ, claims) =
484+ auth::verify(&skey.did(), auth_token).context("failed to verify refresh token")?;
485 if typ != "refresh+jwt" {
486 return Err(Error::with_status(
487 StatusCode::UNAUTHORIZED,
···618 State(db): State<Db>,
619) -> Result<Json<server::get_session::Output>> {
620 let did = user.did();
621+ #[expect(clippy::shadow_unrelated, reason = "is related")]
622 if let Some(user) = sqlx::query!(
623 r#"
624 SELECT a.email, a.status, (