tangled
alpha
login
or
join now
malpercio.dev
/
ezpds
0
fork
atom
An easy-to-host PDS on the ATProtocol, MacOS. Grandma-approved.
0
fork
atom
overview
issues
pulls
pipelines
feat(db): add oauth_signing_key DB functions
malpercio.dev
5 days ago
8cff5aa9
3cec7847
+75
1 changed file
expand all
collapse all
unified
split
crates
relay
src
db
oauth.rs
+75
crates/relay/src/db/oauth.rs
reviewed
···
113
113
Ok(row.map(|(did,)| did))
114
114
}
115
115
116
116
+
/// A row from the `oauth_signing_key` table.
117
117
+
pub struct OAuthSigningKeyRow {
118
118
+
pub id: String,
119
119
+
pub public_key_jwk: String,
120
120
+
pub private_key_encrypted: String,
121
121
+
}
122
122
+
123
123
+
/// Load the server's OAuth signing key row. Returns `None` if no key has been generated yet.
124
124
+
pub async fn get_oauth_signing_key(
125
125
+
pool: &SqlitePool,
126
126
+
) -> Result<Option<OAuthSigningKeyRow>, sqlx::Error> {
127
127
+
let row: Option<(String, String, String)> = sqlx::query_as(
128
128
+
"SELECT id, public_key_jwk, private_key_encrypted FROM oauth_signing_key LIMIT 1",
129
129
+
)
130
130
+
.fetch_optional(pool)
131
131
+
.await?;
132
132
+
133
133
+
Ok(row.map(|(id, public_key_jwk, private_key_encrypted)| OAuthSigningKeyRow {
134
134
+
id,
135
135
+
public_key_jwk,
136
136
+
private_key_encrypted,
137
137
+
}))
138
138
+
}
139
139
+
140
140
+
/// Persist a newly generated OAuth signing key.
141
141
+
///
142
142
+
/// `id` is a UUID string. `public_key_jwk` is a JWK JSON string for the P-256 public key.
143
143
+
/// `private_key_encrypted` is the AES-256-GCM-encrypted private key (base64, 80 chars).
144
144
+
pub async fn store_oauth_signing_key(
145
145
+
pool: &SqlitePool,
146
146
+
id: &str,
147
147
+
public_key_jwk: &str,
148
148
+
private_key_encrypted: &str,
149
149
+
) -> Result<(), sqlx::Error> {
150
150
+
sqlx::query(
151
151
+
"INSERT INTO oauth_signing_key (id, public_key_jwk, private_key_encrypted, created_at) \
152
152
+
VALUES (?, ?, ?, datetime('now'))",
153
153
+
)
154
154
+
.bind(id)
155
155
+
.bind(public_key_jwk)
156
156
+
.bind(private_key_encrypted)
157
157
+
.execute(pool)
158
158
+
.await?;
159
159
+
Ok(())
160
160
+
}
161
161
+
116
162
#[cfg(test)]
117
163
mod tests {
118
164
use super::*;
···
243
289
244
290
let result = get_single_account_did(&pool).await.unwrap();
245
291
assert_eq!(result.as_deref(), Some(did));
292
292
+
}
293
293
+
294
294
+
#[tokio::test]
295
295
+
async fn store_and_retrieve_oauth_signing_key() {
296
296
+
let pool = test_pool().await;
297
297
+
store_oauth_signing_key(
298
298
+
&pool,
299
299
+
"test-key-uuid-01",
300
300
+
r#"{"kty":"EC","crv":"P-256","x":"abc","y":"def","kid":"test-key-uuid-01"}"#,
301
301
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
302
302
+
)
303
303
+
.await
304
304
+
.unwrap();
305
305
+
306
306
+
let row = get_oauth_signing_key(&pool)
307
307
+
.await
308
308
+
.unwrap()
309
309
+
.expect("key should exist after storage");
310
310
+
311
311
+
assert_eq!(row.id, "test-key-uuid-01");
312
312
+
assert!(!row.public_key_jwk.is_empty());
313
313
+
assert!(!row.private_key_encrypted.is_empty());
314
314
+
}
315
315
+
316
316
+
#[tokio::test]
317
317
+
async fn get_oauth_signing_key_returns_none_when_empty() {
318
318
+
let pool = test_pool().await;
319
319
+
let result = get_oauth_signing_key(&pool).await.unwrap();
320
320
+
assert!(result.is_none());
246
321
}
247
322
}