better-auth-api 1.0.0-alpha.5

Plugin implementations for better-auth
use super::*;
use crate::plugins::test_helpers::{create_test_context, finalize_response};
use std::collections::HashMap;

fn cookies(response: &AuthResponse) -> String {
    response
        .headers
        .get_all("set-cookie")
        .map(|cookie| cookie.split(';').next().unwrap())
        .collect::<Vec<_>>()
        .join("; ")
}

#[tokio::test]
async fn signed_paybin_code_account_info_and_refresh_persist_normal_sqlite_records() {
    let fixture = fixture();
    let sample = &fixture["profileCases"][0];
    let server = Server::start(sample["profile"].clone()).await;
    let seen = Arc::default();
    let mut provider = server.provider();
    provider.map_profile_to_user = Some(Arc::new(Mapper {
        patch: sample["patch"].clone(),
        seen: Arc::clone(&seen),
        error: false,
    }));
    let plugin = OAuthPlugin::new().add_provider("paybin", provider);
    let ctx = create_test_context().await;
    let mut start = AuthRequest::new(HttpMethod::Post, "/sign-in/social");
    start.body = Some(serde_json::to_vec(&json!({"provider":"paybin", "callbackURL":"http://localhost:3000/welcome", "disableRedirect":true})).unwrap());
    let start_response = finalize_response(
        &ctx,
        &start,
        plugin.on_request(&start, &ctx).await.unwrap().unwrap(),
    );
    assert_eq!(start_response.status, 200);
    let start_cookies = cookies(&start_response);
    assert!(!start_cookies.is_empty());
    let start_body: Value = serde_json::from_slice(&start_response.body.bytes().unwrap()).unwrap();
    let url = url::Url::parse(start_body["url"].as_str().unwrap()).unwrap();
    assert_eq!(url.path(), "/oauth2/authorize");
    let query: HashMap<String, String> = url.query_pairs().into_owned().collect();
    assert_eq!(query["code_challenge_method"], "S256");
    let mut callback = AuthRequest::new(HttpMethod::Get, "/callback/paybin");
    callback.query = Some(json!({"code":"ordinary-code", "state":query["state"]}));
    let _ = callback.headers.insert("cookie".into(), start_cookies);
    let response = finalize_response(
        &ctx,
        &callback,
        plugin.on_request(&callback, &ctx).await.unwrap().unwrap(),
    );
    assert_eq!(response.status, 302);
    assert_eq!(
        response.headers.get("location").map(String::as_str),
        Some("http://localhost:3000/welcome")
    );
    assert!(
        response
            .headers
            .get_all("set-cookie")
            .any(|cookie| cookie.starts_with("better-auth.session_token="))
    );
    let session_cookie = cookies(&response);
    let user = ctx
        .database
        .get_user_by_email(sample["result"]["user"]["email"].as_str().unwrap())
        .await
        .unwrap()
        .unwrap();
    let stored_user = serde_json::to_value(&user).unwrap();
    for field in ["name", "email", "image", "emailVerified"] {
        assert_eq!(
            stored_user[field], sample["result"]["user"][field],
            "{field}"
        );
    }
    let user_id = user.id.display_string().unwrap();
    let accounts = ctx.database.get_user_accounts(&user_id).await.unwrap();
    assert_eq!(accounts.len(), 1);
    let account = serde_json::to_value(&accounts[0]).unwrap();
    let code = &fixture["grants"][0]["response"];
    assert_eq!(account["providerId"], "paybin");
    assert_eq!(account["accountId"], server.claims["sub"]);
    assert_eq!(account["accessToken"], code["accessToken"]);
    assert_eq!(account["refreshToken"], code["refreshToken"]);
    assert_eq!(account["idToken"], server.token);
    let scopes: Vec<String> = serde_json::from_value(code["scopes"].clone()).unwrap();
    assert_eq!(account["scope"], scopes.join(","));
    let captured = server.requests.lock().unwrap()[0].clone();
    let fields: HashMap<String, String> =
        url::form_urlencoded::parse(captured["body"].as_str().unwrap().as_bytes())
            .into_owned()
            .collect();
    assert_eq!(fields["grant_type"], "authorization_code");
    assert_eq!(
        fields["client_id"],
        fixture["metadata"]["clientId"].as_str().unwrap()
    );
    assert_eq!(
        fields["client_secret"],
        fixture["metadata"]["clientSecret"].as_str().unwrap()
    );
    assert_eq!(fields["redirect_uri"], query["redirect_uri"]);
    assert_eq!(fields["code"], "ordinary-code");
    assert_eq!(
        base64::engine::general_purpose::URL_SAFE_NO_PAD
            .encode(Sha256::digest(fields["code_verifier"].as_bytes())),
        query["code_challenge"]
    );
    assert!(!fields.contains_key("device_id"));

    let mut info = AuthRequest::new(HttpMethod::Get, "/account-info");
    info.query = Some(json!({"accountId":account["id"]}));
    let _ = info.headers.insert("cookie".into(), session_cookie.clone());
    let response = finalize_response(
        &ctx,
        &info,
        plugin.on_request(&info, &ctx).await.unwrap().unwrap(),
    );
    assert_eq!(response.status, 200);
    let body: Value = serde_json::from_slice(&response.body.bytes().unwrap()).unwrap();
    assert_eq!(body["user"], sample["result"]["user"]);
    assert_eq!(body["data"], server.claims);
    assert_eq!(
        *seen.lock().unwrap(),
        [server.claims.clone(), server.claims.clone()]
    );

    let mut refresh = AuthRequest::new(HttpMethod::Post, "/refresh-token");
    refresh.body = Some(serde_json::to_vec(&json!({"accountId":account["id"]})).unwrap());
    let _ = refresh.headers.insert("cookie".into(), session_cookie);
    let response = finalize_response(
        &ctx,
        &refresh,
        plugin.on_request(&refresh, &ctx).await.unwrap().unwrap(),
    );
    assert_eq!(response.status, 200);
    let body: Value = serde_json::from_slice(&response.body.bytes().unwrap()).unwrap();
    let expected = &fixture["grants"]
        .as_array()
        .unwrap()
        .iter()
        .find(|case| case["name"] == "refresh")
        .unwrap()["response"];
    assert_eq!(body["accessToken"], expected["accessToken"]);
    assert_eq!(body["refreshToken"], expected["refreshToken"]);
    assert_eq!(body["scope"], account["scope"]);
    let accounts = ctx.database.get_user_accounts(&user_id).await.unwrap();
    assert_eq!(accounts.len(), 1);
    let updated = serde_json::to_value(&accounts[0]).unwrap();
    for field in [
        "id",
        "userId",
        "accountId",
        "providerId",
        "scope",
        "idToken",
    ] {
        assert_eq!(updated[field], account[field], "{field}");
    }
    assert_eq!(updated["accessToken"], expected["accessToken"]);
    assert_eq!(updated["refreshToken"], expected["refreshToken"]);
    assert_eq!(
        *server.paths.lock().unwrap(),
        [
            "/oauth2/token",
            "/.well-known/openid-configuration",
            "/keys/active",
            "/.well-known/openid-configuration",
            "/keys/active",
            "/oauth2/token"
        ]
    );
}