+5
-5
pocket/src/server.rs
+5
-5
pocket/src/server.rs
···
108
tag = "ApiTags::Pocket"
109
)]
110
async fn app_bsky_get_prefs(&self, XrpcAuth(auth): XrpcAuth) -> GetBskyPrefsResponse {
111
-
let did = match self
112
.verifier
113
.verify("app.bsky.actor.getPreferences", &auth.token)
114
.await
···
116
Ok(d) => d,
117
Err(e) => return GetBskyPrefsResponse::BadRequest(xrpc_error("boooo", e.to_string())),
118
};
119
-
log::info!("verified did: {did}");
120
// TODO: fetch from storage
121
GetBskyPrefsResponse::Ok(Json(GetBskyPrefsResponseObject::example()))
122
}
···
134
XrpcAuth(auth): XrpcAuth,
135
Json(prefs): Json<Value>,
136
) -> PutBskyPrefsResponse {
137
-
let did = match self
138
.verifier
139
.verify("app.bsky.actor.getPreferences", &auth.token)
140
.await
···
142
Ok(d) => d,
143
Err(e) => return PutBskyPrefsResponse::BadRequest(xrpc_error("boooo", e.to_string())),
144
};
145
-
log::info!("verified did: {did}");
146
log::warn!("received prefs: {prefs:?}");
147
// TODO: put prefs into storage
148
PutBskyPrefsResponse::Ok(PlainText("hiiiiii".to_string()))
···
184
}
185
186
pub async fn serve(domain: &str) -> () {
187
-
let verifier = TokenVerifier::new(domain);
188
let api_service = OpenApiService::new(Xrpc { verifier }, "Pocket", env!("CARGO_PKG_VERSION"))
189
.server(domain)
190
.url_prefix("/xrpc")
···
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
···
116
Ok(d) => d,
117
Err(e) => return GetBskyPrefsResponse::BadRequest(xrpc_error("boooo", e.to_string())),
118
};
119
+
log::info!("verified did: {did}/{aud}");
120
// TODO: fetch from storage
121
GetBskyPrefsResponse::Ok(Json(GetBskyPrefsResponseObject::example()))
122
}
···
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
···
142
Ok(d) => d,
143
Err(e) => return PutBskyPrefsResponse::BadRequest(xrpc_error("boooo", e.to_string())),
144
};
145
+
log::info!("verified did: {did}/{aud}");
146
log::warn!("received prefs: {prefs:?}");
147
// TODO: put prefs into storage
148
PutBskyPrefsResponse::Ok(PlainText("hiiiiii".to_string()))
···
184
}
185
186
pub async fn serve(domain: &str) -> () {
187
+
let verifier = TokenVerifier::default();
188
let api_service = OpenApiService::new(Xrpc { verifier }, "Pocket", env!("CARGO_PKG_VERSION"))
189
.server(domain)
190
.url_prefix("/xrpc")
+20
-11
pocket/src/token.rs
+20
-11
pocket/src/token.rs
···
21
}
22
23
pub struct TokenVerifier {
24
-
domain: String,
25
client: reqwest::Client,
26
}
27
28
impl TokenVerifier {
29
-
pub fn new(domain: &str) -> Self {
30
let client = reqwest::Client::builder()
31
.user_agent(format!(
32
"microcosm pocket v{} (dev: @bad-example.com)",
···
36
.timeout(Duration::from_secs(12)) // slingshot timeout is 10s
37
.build()
38
.unwrap();
39
-
Self {
40
-
client,
41
-
domain: domain.to_string(),
42
-
}
43
}
44
45
-
pub async fn verify(&self, expected_lxm: &str, token: &str) -> Result<String, VerifyError> {
46
let untrusted = UntrustedToken::new(token).unwrap();
47
48
// danger! unfortunately we need to decode the DID from the jwt body before we have a public key to verify the jwt with
···
118
let Some(aud) = claims.custom.get("aud") else {
119
return Err(VerifyError::VerificationFailed("missing aud"));
120
};
121
-
if *aud != format!("did:web:{}#bsky_appview", self.domain) {
122
-
return Err(VerifyError::VerificationFailed("wrong aud"));
123
-
}
124
let Some(lxm) = claims.custom.get("lxm") else {
125
return Err(VerifyError::VerificationFailed("missing lxm"));
126
};
···
128
return Err(VerifyError::VerificationFailed("wrong lxm"));
129
}
130
131
-
Ok(did.to_string())
132
}
133
}
···
21
}
22
23
pub struct TokenVerifier {
24
client: reqwest::Client,
25
}
26
27
impl TokenVerifier {
28
+
pub fn new() -> Self {
29
let client = reqwest::Client::builder()
30
.user_agent(format!(
31
"microcosm pocket v{} (dev: @bad-example.com)",
···
35
.timeout(Duration::from_secs(12)) // slingshot timeout is 10s
36
.build()
37
.unwrap();
38
+
Self { client }
39
}
40
41
+
pub async fn verify(
42
+
&self,
43
+
expected_lxm: &str,
44
+
token: &str,
45
+
) -> Result<(String, String), VerifyError> {
46
let untrusted = UntrustedToken::new(token).unwrap();
47
48
// danger! unfortunately we need to decode the DID from the jwt body before we have a public key to verify the jwt with
···
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
};
···
131
return Err(VerifyError::VerificationFailed("wrong lxm"));
132
}
133
134
+
Ok((did.to_string(), aud.to_string()))
135
+
}
136
+
}
137
+
138
+
impl Default for TokenVerifier {
139
+
fn default() -> Self {
140
+
Self::new()
141
}
142
}