An easy-to-host PDS on the ATProtocol, MacOS. Grandma-approved.

feat(app): add oauth_signing_keypair and dpop_nonces to AppState

+25
+21
crates/relay/src/app.rs
··· 12 12 use tower_http::{cors::CorsLayer, trace::TraceLayer}; 13 13 use tracing_opentelemetry::OpenTelemetrySpanExt; 14 14 15 + use crate::auth::{new_nonce_store, DpopNonceStore, OAuthSigningKey}; 15 16 use crate::dns::{DnsProvider, TxtResolver}; 16 17 use crate::routes::claim_codes::claim_codes; 17 18 use crate::routes::create_account::create_account; ··· 101 102 /// HS256 signing secret for JWT access/refresh tokens. 102 103 /// Generated randomly at startup via OsRng (ephemeral — rotates on restart). 103 104 pub jwt_secret: [u8; 32], 105 + /// Persistent ES256 keypair for signing OAuth access tokens. 106 + /// Loaded at startup from `oauth_signing_key` table (or generated + stored on first boot). 107 + pub oauth_signing_keypair: OAuthSigningKey, 108 + /// In-memory store for server-issued DPoP nonces. Shared across all token endpoint requests. 109 + pub dpop_nonces: DpopNonceStore, 104 110 } 105 111 106 112 /// Build the Axum router with middleware and routes. ··· 162 168 pub async fn test_state_with_plc_url(plc_directory_url: String) -> AppState { 163 169 use crate::db::{open_pool, run_migrations}; 164 170 use common::{BlobsConfig, IrohConfig, OAuthConfig, TelemetryConfig}; 171 + use p256::pkcs8::EncodePrivateKey; 172 + use rand_core::OsRng; 165 173 use std::path::PathBuf; 166 174 use std::time::Duration; 167 175 ··· 173 181 .build() 174 182 .expect("test http client"); 175 183 184 + // Generate a fresh ephemeral P-256 keypair for tests (no DB persistence needed). 185 + let test_signing_key = { 186 + let sk = p256::ecdsa::SigningKey::random(&mut OsRng); 187 + let pkcs8 = sk.to_pkcs8_der().expect("PKCS#8 encoding must succeed for test key"); 188 + OAuthSigningKey { 189 + key_id: "test-oauth-key-01".to_string(), 190 + encoding_key: jsonwebtoken::EncodingKey::from_ec_der(pkcs8.as_bytes()), 191 + } 192 + }; 193 + let dpop_nonces = new_nonce_store(); 194 + 176 195 AppState { 177 196 config: Arc::new(Config { 178 197 bind_address: "127.0.0.1".to_string(), ··· 200 219 well_known_resolver: None, 201 220 // Fixed key for tests — predictable JWTs in unit tests. 202 221 jwt_secret: [0x42u8; 32], 222 + oauth_signing_keypair: test_signing_key, 223 + dpop_nonces, 203 224 } 204 225 } 205 226
+4
crates/relay/src/routes/create_signing_key.rs
··· 129 129 txt_resolver: base.txt_resolver, 130 130 well_known_resolver: base.well_known_resolver, 131 131 jwt_secret: base.jwt_secret, 132 + oauth_signing_keypair: base.oauth_signing_keypair, 133 + dpop_nonces: base.dpop_nonces, 132 134 } 133 135 } 134 136 ··· 383 385 txt_resolver: base.txt_resolver, 384 386 well_known_resolver: base.well_known_resolver, 385 387 jwt_secret: base.jwt_secret, 388 + oauth_signing_keypair: base.oauth_signing_keypair, 389 + dpop_nonces: base.dpop_nonces, 386 390 }; 387 391 388 392 let response = app(state)