link-assistant-router 1.4.6

Link.Assistant.Router — Claude MAX OAuth proxy and token gateway for Anthropic APIs
Documentation
use super::*;
use axum::Json;
use axum::extract::Request;
use axum::response::IntoResponse as _;
use axum::routing::any;

#[test]
fn loopback_authorization_uses_the_official_codex_originator() {
    let url = authorize_url(
        "https://auth.test",
        "client",
        "http://127.0.0.1:1455/auth/callback",
        "state",
        "challenge",
    );
    let parsed = reqwest::Url::parse(&url).expect("authorization URL");
    let originator = parsed
        .query_pairs()
        .find_map(|(key, value)| (key == "originator").then(|| value.into_owned()));
    assert_eq!(
        originator.as_deref(),
        Some(crate::codex_identity::ORIGINATOR)
    );
    assert!(!url.contains("link_assistant_router"));
}

#[test]
fn loopback_authorization_matches_the_versioned_codex_query_contract() {
    let fixture: serde_json::Value = serde_json::from_str(include_str!(
        "../tests/fixtures/clients/codex-authorize-contract.json"
    ))
    .unwrap();
    assert_eq!(
        fixture["client_version"],
        crate::codex_identity::DEFAULT_CLIENT_VERSION
    );
    assert_eq!(fixture["client_id"], CODEX_CLIENT_ID);

    let url = authorize_url(
        "https://auth.openai.com",
        CODEX_CLIENT_ID,
        "http://localhost:1455/auth/callback",
        "masked-state",
        "masked-challenge",
    );
    let parsed = reqwest::Url::parse(&url).unwrap();
    assert_eq!(parsed.path(), fixture["path"]);
    let actual = parsed
        .query_pairs()
        .map(|(key, value)| {
            (
                key.into_owned(),
                serde_json::Value::String(value.into_owned()),
            )
        })
        .collect::<serde_json::Map<_, _>>();
    assert_eq!(serde_json::Value::Object(actual), fixture["query"]);
}

#[test]
fn device_polling_exposes_only_stable_secret_free_outcomes() {
    assert!(matches!(
        classify_device_error(StatusCode::FORBIDDEN, ""),
        Ok(DevicePoll::Pending)
    ));
    assert!(matches!(
        classify_device_error(StatusCode::BAD_REQUEST, r#"{"error":"slow_down"}"#),
        Ok(DevicePoll::SlowDown)
    ));
    let expired = classify_device_error(
        StatusCode::FORBIDDEN,
        r#"{"error":"expired_token","error_description":"leaked-code"}"#,
    )
    .err()
    .expect("expired device authorization");
    assert!(expired.contains("expired_token"));
    assert!(!expired.contains("leaked-code"));
}

#[tokio::test]
async fn native_codex_rejection_preserves_the_working_primary() {
    let app = Router::new().fallback(any(|request: Request| async move {
        match (request.method().as_str(), request.uri().path()) {
            ("POST", "/oauth/token") => Json(serde_json::json!({
                "id_token": "rotated.header.sig",
                "access_token": "rotated-access",
                "refresh_token": "rotated-refresh",
                "expires_in": 3600
            }))
            .into_response(),
            ("GET", "/models") => StatusCode::UNAUTHORIZED.into_response(),
            _ => StatusCode::NOT_FOUND.into_response(),
        }
    }));
    let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
    let issuer = format!("http://{}", listener.local_addr().unwrap());
    let server = tokio::spawn(async move { axum::serve(listener, app).await.unwrap() });
    let home = tempfile::tempdir().unwrap();
    let data = tempfile::tempdir().unwrap();
    let primary = home.path().join("auth.json");
    let original = br#"{"auth_mode":"chatgpt","tokens":{"access_token":"working","refresh_token":"working-refresh"}}"#;
    std::fs::write(&primary, original).unwrap();
    let config = CodexAuthConfig {
        issuer,
        client_id: CODEX_CLIENT_ID.to_string(),
        port: 1455,
        codex_home: home.path().to_path_buf(),
        timeout: Duration::from_secs(3),
        bind_host: "127.0.0.1".to_string(),
    };
    let tokens = TokenResponse {
        id_token: "native.header.sig".to_string(),
        access_token: "native-access".to_string(),
        refresh_token: "native-refresh".to_string(),
    };

    let error = persist_codex_auth(&config, &tokens, data.path())
        .await
        .expect_err("catalog rejection must block promotion");

    assert!(error.contains("rejected"), "{error}");
    assert!(error.contains("transaction"), "{error}");
    assert!(!error.contains("rotated-access"), "{error}");
    assert_eq!(std::fs::read(primary).unwrap(), original);
    server.abort();
}

#[tokio::test]
async fn native_codex_token_errors_never_echo_provider_bodies_or_pkce_values() {
    let app = Router::new().route(
        "/oauth/token",
        axum::routing::post(|| async {
            (
                StatusCode::BAD_REQUEST,
                "access_token=leaked-access refresh_token=leaked-refresh id_token=leaked-id",
            )
        }),
    );
    let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
    let issuer = format!("http://{}", listener.local_addr().unwrap());
    let server = tokio::spawn(async move { axum::serve(listener, app).await.unwrap() });
    let home = tempfile::tempdir().unwrap();
    let data = tempfile::tempdir().unwrap();
    let config = CodexAuthConfig {
        issuer,
        client_id: CODEX_CLIENT_ID.to_string(),
        port: 1455,
        codex_home: home.path().to_path_buf(),
        timeout: Duration::from_secs(3),
        bind_host: "127.0.0.1".to_string(),
    };

    let error = exchange_and_store(
        &config,
        "http://localhost:1455/auth/callback",
        "leaked-verifier",
        "leaked-code",
        data.path(),
    )
    .await
    .expect_err("provider rejection");

    assert!(error.contains("400"), "{error}");
    for secret in [
        "leaked-access",
        "leaked-refresh",
        "leaked-id",
        "leaked-verifier",
        "leaked-code",
    ] {
        assert!(!error.contains(secret), "leaked {secret}: {error}");
    }
    assert!(!home.path().join("auth.json").exists());
    server.abort();
}