openauth-telemetry 0.0.4

Telemetry support for OpenAuth.
Documentation

OpenAuth

OpenAuth is a Rust authentication toolkit.

Status

OpenAuth is experimental. APIs, crate boundaries, and behavior may change before the project reaches a stable release.

Rate Limiting

OpenAuth rate limiting is route-aware and uses an async atomic consume contract. The default Memory backend is a Governor-backed local limiter and is best for development, tests, and single-instance deployments.

use openauth::{OpenAuth, RateLimitOptions};

let auth = OpenAuth::builder()
    .secret("secret-a-at-least-32-chars-long!!")
    .rate_limit(RateLimitOptions::memory().enabled(true).window(60).max(100))
    .build()?;

For multi-instance deployments, use a distributed RateLimitStore instead of local memory. openauth-sqlx provides SQLx-backed stores when the application already depends on a SQL database. For Postgres without SQLx, openauth-deadpool-postgres is the recommended pooled production adapter, while openauth-tokio-postgres is the minimal no-pool adapter for applications that already own a tokio_postgres::Client. openauth-redis provides a Redis/Valkey store for higher-throughput shared enforcement. openauth-fred provides the same rate-limit storage contract for projects that prefer the fred client. Both Redis crates use RESP-compatible servers, Lua scripting for the atomic consume operation, and core commands shared by Redis and Valkey. valkey:// and valkeys:// URLs are accepted as aliases for redis:// and rediss:// before connecting. Very high traffic deployments can opt into hybrid mode, which runs a local Governor prefilter before the SQLx or Redis/Valkey store while keeping the distributed decision authoritative.

use openauth::{HybridRateLimitOptions, OpenAuth, RateLimitOptions};

let auth = OpenAuth::builder()
    .secret("secret-a-at-least-32-chars-long!!")
    .rate_limit(
        RateLimitOptions::secondary_storage(redis_store)
            .window(60)
            .max(100)
            .hybrid(HybridRateLimitOptions::enabled().local_multiplier(2)),
    )
    .build()?;

Using Fred directly:

use openauth::{OpenAuth, RateLimitOptions};
use openauth_fred::FredRateLimitStore;

let store = FredRateLimitStore::connect_valkey("valkey://127.0.0.1:6379").await?;

let auth = OpenAuth::builder()
    .secret("secret-a-at-least-32-chars-long!!")
    .rate_limit(
        RateLimitOptions::secondary_storage(store)
            .enabled(true)
            .window(60)
            .max(100),
    )
    .build()?;

Custom stores should implement RateLimitStore::consume atomically. The legacy RateLimitStorage get/set adapter is kept for compatibility, but it is not safe for distributed enforcement unless the underlying implementation provides its own atomicity.

The existing initializer helpers remain available:

use openauth::{open_auth, OpenAuthOptions};

let auth = open_auth(OpenAuthOptions::new()
    .secret("secret-a-at-least-32-chars-long!!"))?;

Passkeys

openauth-passkey provides the server-side passkey plugin. It uses webauthn-rs, contributes a passkeys table with snake_case columns, and keeps WebAuthn ceremony state in server-side verification storage referenced by a signed short-lived cookie. Registration/authentication option JSON follows the Better Auth server behavior for authenticator selection hints and extensions, while verification remains delegated to webauthn-rs.

use openauth::{OpenAuth, OpenAuthOptions};
use openauth_passkey::{passkey, PasskeyOptions};

let auth = OpenAuth::builder()
    .secret("secret-a-at-least-32-chars-long!!")
    .base_url("https://app.example.com")
    .plugin(passkey(PasskeyOptions::default()))
    .build()?;

If you prefer the top-level openauth crate, enable its passkey feature and use openauth::passkey.

Axum

openauth-axum mounts the framework-neutral OpenAuth HTTP core in an Axum application. It uses a catch-all route under the configured auth base path, so core routes, custom endpoints, and plugin-provided endpoints are all handled by OpenAuth's router.

use openauth::OpenAuth;
use openauth_axum::router;

let auth = OpenAuth::builder()
    .secret("secret-a-at-least-32-chars-long!!")
    .base_url("https://app.example.com/api/auth")
    .build()?;

let app = router(auth)?;

When Axum is exposed directly to clients, run the app with connection info so OpenAuth can use the real peer socket IP for rate limiting:

let app = openauth_axum::router(auth)?;

axum::serve(
    listener,
    app.into_make_service_with_connect_info::<std::net::SocketAddr>(),
).await?;

When OpenAuth is behind a trusted proxy or load balancer, configure the proxy to overwrite forwarded IP headers and configure OpenAuth to trust only those headers. Do not enable this for traffic that can reach Axum directly, because clients can spoof forwarded headers.

use openauth::{AdvancedOptions, IpAddressOptions, OpenAuthOptions};

let options = OpenAuthOptions::new().advanced(
    AdvancedOptions::new().ip_address(
        IpAddressOptions::new().headers(["x-forwarded-for"])
    )
);

For manual composition, nest the unmounted routes at the same path as OpenAuthOptions.base_path:

use axum::Router;
use openauth_axum::OpenAuthAxumExt;

let app = Router::new().nest("/api/auth", auth.into_routes());

The adapter has its own request body limit. The default is 10 MiB and can be overridden without changing core OpenAuth options. Requests that exceed this limit return 413 Payload Too Large.

use openauth_axum::{router_with_options, OpenAuthAxumOptions};

let app = router_with_options(
    auth,
    OpenAuthAxumOptions::new().body_limit(2 * 1024 * 1024),
)?;

License

OpenAuth is licensed under the MIT License. See LICENSE.