rustauth-plugins 0.2.0

Official RustAuth plugin modules.
Documentation
use std::sync::Arc;

use http::{Method, StatusCode};
use rustauth_core::db::MemoryAdapter;
use rustauth_plugins::api_key::{
    api_key, ApiKeyConfiguration, ApiKeyOptions, ApiKeyRateLimitOptions,
};
use serde_json::json;
use time::Duration;

use super::helpers::{request_json, server_request_json, sign_up, test_router};

#[tokio::test]
async fn create_applies_explicit_rate_limit_enabled_false() -> Result<(), Box<dyn std::error::Error>>
{
    let router = test_router(
        Arc::new(MemoryAdapter::new()),
        api_key(
            ApiKeyOptions::builder()
                .configuration(ApiKeyConfiguration::default())
                .build()?,
        )?,
    )?;
    let user = sign_up(&router, "Rae", "rae-rate-limit@example.com").await?;

    let created = server_request_json(
        &router,
        Method::POST,
        "/api/auth/api-key/create",
        json!({"userId": user.user_id, "rateLimitEnabled": false}),
        None,
        None,
    )
    .await?;
    assert_eq!(created.status, StatusCode::OK);
    assert_eq!(created.body["rateLimitEnabled"], false);
    Ok(())
}

#[tokio::test]
async fn create_defaults_rate_limit_enabled_true_when_omitted(
) -> Result<(), Box<dyn std::error::Error>> {
    let router = test_router(
        Arc::new(MemoryAdapter::new()),
        api_key(
            ApiKeyOptions::builder()
                .configuration(ApiKeyConfiguration::default())
                .build()?,
        )?,
    )?;
    let user = sign_up(&router, "Rem", "rem-rate-limit@example.com").await?;

    let created = server_request_json(
        &router,
        Method::POST,
        "/api/auth/api-key/create",
        json!({"userId": user.user_id}),
        None,
        None,
    )
    .await?;
    assert_eq!(created.status, StatusCode::OK);
    assert_eq!(created.body["rateLimitEnabled"], true);
    Ok(())
}

#[tokio::test]
async fn create_respects_disabled_rate_limit_from_plugin_options(
) -> Result<(), Box<dyn std::error::Error>> {
    let router = test_router(
        Arc::new(MemoryAdapter::new()),
        api_key(
            ApiKeyOptions::builder()
                .configuration(ApiKeyConfiguration {
                    enable_metadata: true,
                    rate_limit: ApiKeyRateLimitOptions {
                        enabled: false,
                        time_window: Duration::seconds(1),
                        max_requests: 10,
                    },
                    ..ApiKeyConfiguration::default()
                })
                .build()?,
        )?,
    )?;
    let user = sign_up(&router, "Rio", "rio-rate-limit@example.com").await?;

    let created = server_request_json(
        &router,
        Method::POST,
        "/api/auth/api-key/create",
        json!({"userId": user.user_id}),
        None,
        None,
    )
    .await?;
    assert_eq!(created.status, StatusCode::OK);
    assert_eq!(created.body["rateLimitEnabled"], false);
    assert_eq!(created.body["rateLimitTimeWindow"], 1_000);
    assert_eq!(created.body["rateLimitMax"], 10);
    Ok(())
}

#[tokio::test]
async fn verify_skips_rate_limit_when_rate_limit_enabled_is_false(
) -> Result<(), Box<dyn std::error::Error>> {
    let router = test_router(
        Arc::new(MemoryAdapter::new()),
        api_key(
            ApiKeyOptions::builder()
                .configuration(ApiKeyConfiguration::default())
                .build()?,
        )?,
    )?;
    let user = sign_up(&router, "Ryn", "ryn-rate-limit@example.com").await?;

    let created = server_request_json(
        &router,
        Method::POST,
        "/api/auth/api-key/create",
        json!({
            "userId": user.user_id,
            "rateLimitEnabled": false,
            "rateLimitMax": 1,
            "rateLimitTimeWindow": 60_000
        }),
        None,
        None,
    )
    .await?;
    assert_eq!(created.status, StatusCode::OK);
    let key = created.body["key"].as_str().ok_or("missing api key")?;

    for _ in 0..3 {
        let verified = request_json(
            &router,
            Method::POST,
            "/api/auth/api-key/verify",
            json!({"key": key}),
            None,
            None,
        )
        .await?;
        assert_eq!(verified.status, StatusCode::OK);
        assert_eq!(verified.body["valid"], true);
    }
    Ok(())
}