this repo has no description
1mod common;
2mod helpers;
3
4use reqwest::StatusCode;
5use serde_json::{json, Value};
6use sqlx::PgPool;
7use helpers::verify_new_account;
8
9async fn get_pool() -> PgPool {
10 let conn_str = common::get_db_connection_string().await;
11 sqlx::postgres::PgPoolOptions::new()
12 .max_connections(5)
13 .connect(&conn_str)
14 .await
15 .expect("Failed to connect to test database")
16}
17
18#[tokio::test]
19async fn test_reserve_signing_key_without_did() {
20 let client = common::client();
21 let base_url = common::base_url().await;
22
23 let res = client
24 .post(format!(
25 "{}/xrpc/com.atproto.server.reserveSigningKey",
26 base_url
27 ))
28 .json(&json!({}))
29 .send()
30 .await
31 .expect("Failed to send request");
32
33 assert_eq!(res.status(), StatusCode::OK);
34 let body: Value = res.json().await.expect("Response was not valid JSON");
35
36 assert!(body["signingKey"].is_string());
37 let signing_key = body["signingKey"].as_str().unwrap();
38 assert!(
39 signing_key.starts_with("did:key:z"),
40 "Signing key should be in did:key format with multibase prefix"
41 );
42}
43
44#[tokio::test]
45async fn test_reserve_signing_key_with_did() {
46 let client = common::client();
47 let base_url = common::base_url().await;
48 let pool = get_pool().await;
49
50 let target_did = "did:plc:test123456";
51 let res = client
52 .post(format!(
53 "{}/xrpc/com.atproto.server.reserveSigningKey",
54 base_url
55 ))
56 .json(&json!({ "did": target_did }))
57 .send()
58 .await
59 .expect("Failed to send request");
60
61 assert_eq!(res.status(), StatusCode::OK);
62 let body: Value = res.json().await.expect("Response was not valid JSON");
63
64 let signing_key = body["signingKey"].as_str().unwrap();
65 assert!(signing_key.starts_with("did:key:z"));
66
67 let row = sqlx::query!(
68 "SELECT did, public_key_did_key FROM reserved_signing_keys WHERE public_key_did_key = $1",
69 signing_key
70 )
71 .fetch_one(&pool)
72 .await
73 .expect("Reserved key not found in database");
74
75 assert_eq!(row.did.as_deref(), Some(target_did));
76 assert_eq!(row.public_key_did_key, signing_key);
77}
78
79#[tokio::test]
80async fn test_reserve_signing_key_stores_private_key() {
81 let client = common::client();
82 let base_url = common::base_url().await;
83 let pool = get_pool().await;
84
85 let res = client
86 .post(format!(
87 "{}/xrpc/com.atproto.server.reserveSigningKey",
88 base_url
89 ))
90 .json(&json!({}))
91 .send()
92 .await
93 .expect("Failed to send request");
94
95 assert_eq!(res.status(), StatusCode::OK);
96 let body: Value = res.json().await.expect("Response was not valid JSON");
97 let signing_key = body["signingKey"].as_str().unwrap();
98
99 let row = sqlx::query!(
100 "SELECT private_key_bytes, expires_at, used_at FROM reserved_signing_keys WHERE public_key_did_key = $1",
101 signing_key
102 )
103 .fetch_one(&pool)
104 .await
105 .expect("Reserved key not found in database");
106
107 assert_eq!(row.private_key_bytes.len(), 32, "Private key should be 32 bytes for secp256k1");
108 assert!(row.used_at.is_none(), "Reserved key should not be marked as used yet");
109 assert!(row.expires_at > chrono::Utc::now(), "Key should expire in the future");
110}
111
112#[tokio::test]
113async fn test_reserve_signing_key_unique_keys() {
114 let client = common::client();
115 let base_url = common::base_url().await;
116
117 let res1 = client
118 .post(format!(
119 "{}/xrpc/com.atproto.server.reserveSigningKey",
120 base_url
121 ))
122 .json(&json!({}))
123 .send()
124 .await
125 .expect("Failed to send request 1");
126 assert_eq!(res1.status(), StatusCode::OK);
127 let body1: Value = res1.json().await.unwrap();
128 let key1 = body1["signingKey"].as_str().unwrap();
129
130 let res2 = client
131 .post(format!(
132 "{}/xrpc/com.atproto.server.reserveSigningKey",
133 base_url
134 ))
135 .json(&json!({}))
136 .send()
137 .await
138 .expect("Failed to send request 2");
139 assert_eq!(res2.status(), StatusCode::OK);
140 let body2: Value = res2.json().await.unwrap();
141 let key2 = body2["signingKey"].as_str().unwrap();
142
143 assert_ne!(key1, key2, "Each call should generate a unique signing key");
144}
145
146#[tokio::test]
147async fn test_reserve_signing_key_is_public() {
148 let client = common::client();
149 let base_url = common::base_url().await;
150
151 let res = client
152 .post(format!(
153 "{}/xrpc/com.atproto.server.reserveSigningKey",
154 base_url
155 ))
156 .json(&json!({}))
157 .send()
158 .await
159 .expect("Failed to send request");
160
161 assert_eq!(
162 res.status(),
163 StatusCode::OK,
164 "reserveSigningKey should work without authentication"
165 );
166}
167
168#[tokio::test]
169async fn test_create_account_with_reserved_signing_key() {
170 let client = common::client();
171 let base_url = common::base_url().await;
172 let pool = get_pool().await;
173
174 let res = client
175 .post(format!(
176 "{}/xrpc/com.atproto.server.reserveSigningKey",
177 base_url
178 ))
179 .json(&json!({}))
180 .send()
181 .await
182 .expect("Failed to reserve signing key");
183 assert_eq!(res.status(), StatusCode::OK);
184 let body: Value = res.json().await.unwrap();
185 let signing_key = body["signingKey"].as_str().unwrap();
186
187 let handle = format!("reserved_key_user_{}", uuid::Uuid::new_v4());
188 let res = client
189 .post(format!(
190 "{}/xrpc/com.atproto.server.createAccount",
191 base_url
192 ))
193 .json(&json!({
194 "handle": handle,
195 "email": format!("{}@example.com", handle),
196 "password": "password",
197 "signingKey": signing_key
198 }))
199 .send()
200 .await
201 .expect("Failed to create account");
202
203 assert_eq!(res.status(), StatusCode::OK);
204 let body: Value = res.json().await.unwrap();
205 assert!(body["did"].is_string());
206 let did = body["did"].as_str().unwrap();
207
208 let access_jwt = verify_new_account(&client, did).await;
209 assert!(!access_jwt.is_empty());
210
211 let reserved = sqlx::query!(
212 "SELECT used_at FROM reserved_signing_keys WHERE public_key_did_key = $1",
213 signing_key
214 )
215 .fetch_one(&pool)
216 .await
217 .expect("Reserved key not found");
218 assert!(
219 reserved.used_at.is_some(),
220 "Reserved key should be marked as used"
221 );
222}
223
224#[tokio::test]
225async fn test_create_account_with_invalid_signing_key() {
226 let client = common::client();
227 let base_url = common::base_url().await;
228
229 let handle = format!("bad_key_user_{}", uuid::Uuid::new_v4());
230 let res = client
231 .post(format!(
232 "{}/xrpc/com.atproto.server.createAccount",
233 base_url
234 ))
235 .json(&json!({
236 "handle": handle,
237 "email": format!("{}@example.com", handle),
238 "password": "password",
239 "signingKey": "did:key:zNonExistentKey12345"
240 }))
241 .send()
242 .await
243 .expect("Failed to send request");
244
245 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
246 let body: Value = res.json().await.unwrap();
247 assert_eq!(body["error"], "InvalidSigningKey");
248}
249
250#[tokio::test]
251async fn test_create_account_cannot_reuse_signing_key() {
252 let client = common::client();
253 let base_url = common::base_url().await;
254
255 let res = client
256 .post(format!(
257 "{}/xrpc/com.atproto.server.reserveSigningKey",
258 base_url
259 ))
260 .json(&json!({}))
261 .send()
262 .await
263 .expect("Failed to reserve signing key");
264 assert_eq!(res.status(), StatusCode::OK);
265 let body: Value = res.json().await.unwrap();
266 let signing_key = body["signingKey"].as_str().unwrap();
267
268 let handle1 = format!("reuse_key_user1_{}", uuid::Uuid::new_v4());
269 let res = client
270 .post(format!(
271 "{}/xrpc/com.atproto.server.createAccount",
272 base_url
273 ))
274 .json(&json!({
275 "handle": handle1,
276 "email": format!("{}@example.com", handle1),
277 "password": "password",
278 "signingKey": signing_key
279 }))
280 .send()
281 .await
282 .expect("Failed to create first account");
283 assert_eq!(res.status(), StatusCode::OK);
284
285 let handle2 = format!("reuse_key_user2_{}", uuid::Uuid::new_v4());
286 let res = client
287 .post(format!(
288 "{}/xrpc/com.atproto.server.createAccount",
289 base_url
290 ))
291 .json(&json!({
292 "handle": handle2,
293 "email": format!("{}@example.com", handle2),
294 "password": "password",
295 "signingKey": signing_key
296 }))
297 .send()
298 .await
299 .expect("Failed to send second request");
300
301 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
302 let body: Value = res.json().await.unwrap();
303 assert_eq!(body["error"], "InvalidSigningKey");
304 assert!(body["message"]
305 .as_str()
306 .unwrap()
307 .contains("already used"));
308}
309
310#[tokio::test]
311async fn test_reserved_key_tokens_work() {
312 let client = common::client();
313 let base_url = common::base_url().await;
314
315 let res = client
316 .post(format!(
317 "{}/xrpc/com.atproto.server.reserveSigningKey",
318 base_url
319 ))
320 .json(&json!({}))
321 .send()
322 .await
323 .expect("Failed to reserve signing key");
324 assert_eq!(res.status(), StatusCode::OK);
325 let body: Value = res.json().await.unwrap();
326 let signing_key = body["signingKey"].as_str().unwrap();
327
328 let handle = format!("token_test_user_{}", uuid::Uuid::new_v4());
329 let res = client
330 .post(format!(
331 "{}/xrpc/com.atproto.server.createAccount",
332 base_url
333 ))
334 .json(&json!({
335 "handle": handle,
336 "email": format!("{}@example.com", handle),
337 "password": "password",
338 "signingKey": signing_key
339 }))
340 .send()
341 .await
342 .expect("Failed to create account");
343 assert_eq!(res.status(), StatusCode::OK);
344 let body: Value = res.json().await.unwrap();
345 let did = body["did"].as_str().unwrap();
346
347 let access_jwt = verify_new_account(&client, did).await;
348
349 let res = client
350 .get(format!(
351 "{}/xrpc/com.atproto.server.getSession",
352 base_url
353 ))
354 .bearer_auth(&access_jwt)
355 .send()
356 .await
357 .expect("Failed to get session");
358
359 assert_eq!(res.status(), StatusCode::OK);
360 let body: Value = res.json().await.unwrap();
361 assert_eq!(body["handle"], handle);
362}