Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm

clean up and add size limit to requests

Changed files
+13 -16
pocket
+8 -12
pocket/src/server.rs
··· 4 endpoint::{StaticFileEndpoint, make_sync}, 5 http::Method, 6 listener::TcpListener, 7 - middleware::{CatchPanic, Cors, Tracing}, 8 }; 9 use poem_openapi::{ 10 ApiResponse, ContactObject, ExternalDocumentObject, Object, OpenApi, OpenApiService, ··· 94 verifier: TokenVerifier, 95 } 96 97 - // app.bsky.actor.getPreferences 98 - // com.bad-example.pocket.getPreferences 99 - 100 #[OpenApi] 101 impl Xrpc { 102 /// com.bad-example.pocket.getPreferences 103 /// 104 - /// get stored bluesky prefs 105 #[oai( 106 - path = "/app.bsky.actor.getPreferences", 107 method = "get", 108 tag = "ApiTags::Pocket" 109 )] 110 - async fn app_bsky_get_prefs(&self, XrpcAuth(auth): XrpcAuth) -> GetBskyPrefsResponse { 111 let (did, aud) = match self 112 .verifier 113 - .verify("app.bsky.actor.getPreferences", &auth.token) 114 .await 115 { 116 Ok(d) => d, ··· 129 method = "post", 130 tag = "ApiTags::Pocket" 131 )] 132 - async fn app_bsky_put_prefs( 133 &self, 134 XrpcAuth(auth): XrpcAuth, 135 Json(prefs): Json<Value>, 136 ) -> PutBskyPrefsResponse { 137 let (did, aud) = match self 138 .verifier 139 - .verify("app.bsky.actor.getPreferences", &auth.token) 140 .await 141 { 142 Ok(d) => d, ··· 168 service: [ 169 AppViewService { 170 id: "#pocket_prefs".to_string(), 171 - // id: "#bsky_appview".to_string(), 172 r#type: "PocketPreferences".to_string(), 173 service_endpoint: format!("https://{domain}"), 174 }, 175 AppViewService { 176 id: "#bsky_appview".to_string(), 177 - // id: "#bsky_appview".to_string(), 178 r#type: "BlueskyAppview".to_string(), 179 service_endpoint: format!("https://{domain}"), 180 }, ··· 201 .nest("/xrpc/", api_service) 202 .at("/.well-known/did.json", get_did_doc(domain)) 203 .at("/", StaticFileEndpoint::new("./static/index.html")) 204 .with( 205 Cors::new() 206 .allow_method(Method::GET)
··· 4 endpoint::{StaticFileEndpoint, make_sync}, 5 http::Method, 6 listener::TcpListener, 7 + middleware::{CatchPanic, Cors, SizeLimit, Tracing}, 8 }; 9 use poem_openapi::{ 10 ApiResponse, ContactObject, ExternalDocumentObject, Object, OpenApi, OpenApiService, ··· 94 verifier: TokenVerifier, 95 } 96 97 #[OpenApi] 98 impl Xrpc { 99 /// com.bad-example.pocket.getPreferences 100 /// 101 + /// get stored preferencess 102 #[oai( 103 + path = "/com.bad-example.pocket.getPreferences", 104 method = "get", 105 tag = "ApiTags::Pocket" 106 )] 107 + async fn pocket_get_prefs(&self, XrpcAuth(auth): XrpcAuth) -> GetBskyPrefsResponse { 108 let (did, aud) = match self 109 .verifier 110 + .verify("com.bad-example.pocket.getPreferences", &auth.token) 111 .await 112 { 113 Ok(d) => d, ··· 126 method = "post", 127 tag = "ApiTags::Pocket" 128 )] 129 + async fn pocket_put_prefs( 130 &self, 131 XrpcAuth(auth): XrpcAuth, 132 Json(prefs): Json<Value>, 133 ) -> PutBskyPrefsResponse { 134 let (did, aud) = match self 135 .verifier 136 + .verify("com.bad-example.pocket.putPreferences", &auth.token) 137 .await 138 { 139 Ok(d) => d, ··· 165 service: [ 166 AppViewService { 167 id: "#pocket_prefs".to_string(), 168 r#type: "PocketPreferences".to_string(), 169 service_endpoint: format!("https://{domain}"), 170 }, 171 AppViewService { 172 id: "#bsky_appview".to_string(), 173 r#type: "BlueskyAppview".to_string(), 174 service_endpoint: format!("https://{domain}"), 175 }, ··· 196 .nest("/xrpc/", api_service) 197 .at("/.well-known/did.json", get_did_doc(domain)) 198 .at("/", StaticFileEndpoint::new("./static/index.html")) 199 + .with(SizeLimit::new(100 * 2_usize.pow(10))) 200 .with( 201 Cors::new() 202 .allow_method(Method::GET)
+5 -4
pocket/src/token.rs
··· 118 let Some(aud) = claims.custom.get("aud") else { 119 return Err(VerifyError::VerificationFailed("missing aud")); 120 }; 121 - let Some(aud) = aud.strip_prefix("did:web:") else { 122 return Err(VerifyError::VerificationFailed("expected a did:web aud")); 123 }; 124 - let Some((aud, _)) = aud.split_once("#") else { 125 - return Err(VerifyError::VerificationFailed("aud missing #fragment")); 126 - }; 127 let Some(lxm) = claims.custom.get("lxm") else { 128 return Err(VerifyError::VerificationFailed("missing lxm")); 129 };
··· 118 let Some(aud) = claims.custom.get("aud") else { 119 return Err(VerifyError::VerificationFailed("missing aud")); 120 }; 121 + let Some(mut aud) = aud.strip_prefix("did:web:") else { 122 return Err(VerifyError::VerificationFailed("expected a did:web aud")); 123 }; 124 + if let Some((aud_without_hash, _)) = aud.split_once("#") { 125 + log::warn!("aud claim is missing service id fragment: {aud:?}"); 126 + aud = aud_without_hash; 127 + } 128 let Some(lxm) = claims.custom.get("lxm") else { 129 return Err(VerifyError::VerificationFailed("missing lxm")); 130 };