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