~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/zkwid

0
0
main
135 lines · 4.8 KB Raw History
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<String>, 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");
}