use atproto_identity::key::{KeyType, generate_key, to_public}; use axum::body::Body; use axum::http::{Request, StatusCode, header}; use http_body_util::BodyExt; use serde_json::Value; use std::path::PathBuf; use tower::ServiceExt; async fn app() -> (axum::Router, zkwid_n::AppState) { let config = zkwid_n::Config { base_url: "https://zkwid.test".into(), did: "did:web:zkwid.test".into(), signing_key: generate_key(KeyType::P256Private).unwrap(), db_path: PathBuf::from(":memory:"), plc_url: "http://127.0.0.1:1".into(), }; let state = zkwid_n::state(config).await.unwrap(); (zkwid_n::router(state.clone()), state) } async fn get(router: &axum::Router, uri: &str) -> (StatusCode, Option, Value) { let response = router .clone() .oneshot(Request::get(uri).body(Body::empty()).unwrap()) .await .unwrap(); let status = response.status(); let content_type = response .headers() .get(header::CONTENT_TYPE) .map(|value| value.to_str().unwrap().to_string()); let bytes = response.into_body().collect().await.unwrap().to_bytes(); ( status, content_type, serde_json::from_slice(&bytes).unwrap_or(Value::Null), ) } #[tokio::test] async fn did_document() { let (router, state) = app().await; let (status, content_type, doc) = get(&router, "/.well-known/did.json").await; assert_eq!(status, StatusCode::OK); assert_eq!(content_type.as_deref(), Some("application/json")); assert_eq!(doc["id"], "did:web:zkwid.test"); let method = &doc["verificationMethod"][0]; assert_eq!(method["id"], "did:web:zkwid.test#atproto"); assert_eq!(method["type"], "Multikey"); assert_eq!(method["controller"], "did:web:zkwid.test"); let public = to_public(&state.config.signing_key).unwrap().to_string(); assert_eq!( format!("did:key:{}", method["publicKeyMultibase"].as_str().unwrap()), public ); assert_eq!(doc["service"][0]["id"], "#zkwid"); assert_eq!(doc["service"][0]["type"], "ZkwidService"); assert_eq!(doc["service"][0]["serviceEndpoint"], "https://zkwid.test"); let parsed: atproto_identity::model::Document = serde_json::from_value(doc).unwrap(); assert_eq!( parsed.verification_method_multibase("atproto"), public.strip_prefix("did:key:") ); assert_eq!(parsed.service_endpoint("zkwid"), Some("https://zkwid.test")); } #[tokio::test] async fn n_client_metadata() { let (router, state) = app().await; let (status, content_type, meta) = get(&router, "/oauth-client-metadata.json").await; assert_eq!(status, StatusCode::OK); assert_eq!(content_type.as_deref(), Some("application/json")); assert_eq!( meta["client_id"], "https://zkwid.test/oauth-client-metadata.json" ); assert_eq!(meta["redirect_uris"][0], "https://zkwid.test/callback"); assert_eq!(meta["scope"], "atproto repo:at.zkwid.key"); assert_eq!(meta["token_endpoint_auth_method"], "private_key_jwt"); assert_eq!(meta["dpop_bound_access_tokens"], true); let jwk = &meta["jwks"]["keys"][0]; assert_eq!(jwk["kty"], "EC"); assert_eq!(jwk["crv"], "P-256"); assert!(jwk.get("d").is_none()); let key = state .store .client_key("https://zkwid.test/oauth-client-metadata.json") .unwrap(); assert_eq!(jwk["kid"], to_public(&key).unwrap().to_string()); let (_, _, again) = get(&router, "/oauth-client-metadata.json").await; assert_eq!(again, meta); } #[tokio::test] async fn bad_requests_are_xrpc_errors() { let (router, _) = app().await; let (status, _, body) = get(&router, "/login").await; assert_eq!(status, StatusCode::BAD_REQUEST); assert_eq!(body["error"], "InvalidRequest"); let (status, _, body) = get(&router, "/callback?code=c&state=nope&iss=x").await; assert_eq!(status, StatusCode::BAD_REQUEST); assert_eq!(body["error"], "InvalidRequest"); } #[tokio::test] async fn pds_call_without_session_is_not_authorized() { let (_, state) = app().await; let error = zkwid_n::oauth::pds_call( &state, "did:plc:nobody", zkwid_n::store::N_CLIENT, axum::http::Method::GET, "/xrpc/com.atproto.repo.describeRepo", None, &axum::http::HeaderMap::new(), ) .await .err() .unwrap(); let error = zkwid_n::error::XrpcError::from(error); assert_eq!(error.status, StatusCode::FORBIDDEN); assert_eq!(error.error, "NotAuthorized"); } #[tokio::test] async fn unresolvable_workload_metadata_is_404() { let (router, _) = app().await; let (status, _, body) = get( &router, "/did:plc:aaaaaaaaaaaaaaaaaaaaaaaa/demo/oauth-client-metadata.json", ) .await; assert_eq!(status, StatusCode::NOT_FOUND); assert_eq!(body["error"], "WorkloadNotFound"); }