| Crates.io | libauth-rs |
| lib.rs | libauth-rs |
| version | 0.1.2 |
| created_at | 2025-10-17 15:59:27.519255+00 |
| updated_at | 2025-12-17 16:06:41.464239+00 |
| description | Unified authentication and authorization library with Stytch, Clerk, and MSAL support |
| homepage | https://github.com/your-org/libauth-rs |
| repository | https://github.com/your-org/libauth-rs |
| max_upload_size | |
| id | 1887848 |
| size | 262,172 |
A unified authentication library for Rust with support for multiple auth providers (Clerk, Stytch, MSAL) and Axum middleware integration.
Add to your Cargo.toml:
[dependencies]
libauth-rs = { path = ".", features = ["clerk", "axum"] }
authentication - Core authentication functionalityauthorization - Authorization and permissions (WIP)clerk - Clerk authentication providerstytch - Stytch authentication providermsal - Microsoft Azure AD / MSAL authentication provideraxum - Axum middleware and extractorsall-providers - Enable all authentication providersscim - SCIM supportuse axum::{Router, routing::get, response::IntoResponse};
use libauth_rs::prelude::*;
#[tokio::main]
async fn main() {
// Configure your auth provider
let config = AuthConfig::default(); // Loads from environment variables
let clerk = ClerkProvider::new(&config).await.unwrap();
// Create the auth layer
let auth_layer = AuthLayer::new(vec![Box::new(clerk)])
.required(false); // Set to true to require authentication
// Build your router
let app = Router::new()
.route("/", get(public_handler))
.route("/protected", get(protected_handler))
.layer(auth_layer);
// Run your server
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}
// Public handler - authentication is optional
async fn public_handler(
OptionalAuth(user): OptionalAuth,
) -> impl IntoResponse {
match user {
Some(user) => format!("Hello, {}!", user.user_id),
None => "Hello, anonymous!".to_string(),
}
}
// Protected handler - authentication is required
async fn protected_handler(
AuthExtension(user): AuthExtension,
) -> impl IntoResponse {
format!("Welcome, {}! Your email is: {:?}", user.user_id, user.email)
}
CLERK_SECRET_KEY=sk_test_...
CLERK_PUBLISHABLE_KEY=pk_test_...
STYTCH_PROJECT_ID=project-test-...
STYTCH_SECRET=secret-test-...
AZURE_CLIENT_ID=your-client-id
AZURE_CLIENT_SECRET=your-client-secret
AZURE_TENANT_ID=your-tenant-id
You can configure multiple providers and the library will automatically route tokens to the correct provider:
let config = AuthConfig::default();
let mut providers: Vec<Box<dyn AuthProvider>> = vec![];
// Add Clerk
if let Ok(clerk) = ClerkProvider::new(&config).await {
providers.push(Box::new(clerk));
}
// Add Stytch
if let Ok(stytch) = StytchProvider::new(&config).await {
providers.push(Box::new(stytch));
}
// Add MSAL
if let Ok(msal) = MsalProvider::new(&config).await {
providers.push(Box::new(msal));
}
let auth_layer = AuthLayer::new(providers);
You can also use the providers directly without the middleware:
use libauth_rs::prelude::*;
#[tokio::main]
async fn main() {
let config = AuthConfig::default();
let clerk = ClerkProvider::new(&config).await.unwrap();
// Create a token from an authorization header
let token = AuthToken::bearer("sess_abc123...");
// Authenticate
match clerk.authenticate(&token).await {
Ok(user) => {
println!("Authenticated user: {}", user.user_id);
println!("Email: {:?}", user.email);
println!("Provider: {}", user.provider);
}
Err(e) => {
eprintln!("Authentication failed: {}", e);
}
}
}
The UserContext struct contains all the authenticated user information:
pub struct UserContext {
pub user_id: String,
pub email: Option<String>,
pub name: Option<String>,
pub provider: AuthProvider,
pub session_id: Option<String>,
pub expires_at: Option<DateTime<Utc>>,
pub metadata: HashMap<String, serde_json::Value>,
}
┌─────────────────────────────────────────┐
│ Your Axum App │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ AuthLayer (Middleware) │
│ • Extracts Authorization header │
│ • Routes to correct provider │
│ • Injects UserContext into request │
└─────────────────────────────────────────┘
│
┌─────────┴──────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ Clerk │ │Stytch │ │ MSAL │
│Provider│ │Provider│ │Provider│
└────────┘ └────────┘ └────────┘
libauth-rs supports provider-specific authorization, allowing each provider to implement its own authorization logic:
use libauth_rs::middleware::AuthContext;
async fn admin_handler(auth: AuthContext) -> Result<String, StatusCode> {
// Provider-specific role check
auth.require_role("admin").await?;
// Provider-specific permission check
if !auth.check_permission("resource:write").await.unwrap_or(false) {
return Err(StatusCode::FORBIDDEN);
}
Ok("Admin access granted!".to_string())
}
See AUTHORIZATION.md for complete documentation on authorization features.
You can create separate Axum routers for each authentication provider:
// Clerk-specific routes
let clerk_router = Router::new()
.route("/orgs/:org_id/members", get(clerk_handler))
.layer(AuthLayer::new(vec![Box::new(clerk)]).required(true));
// MSAL-specific routes
let msal_router = Router::new()
.route("/azure/groups", get(msal_handler))
.layer(AuthLayer::new(vec![Box::new(msal)]).required(true));
// Combine routers
let app = Router::new()
.nest("/clerk", clerk_router)
.nest("/msal", msal_router);
This library is designed to be API-agnostic - it doesn't care whether you're building a REST API, GraphQL API, or any other type of service. The middleware simply:
You can then use the AuthExtension, AuthContext, or OptionalAuth extractors in any handler, regardless of what kind of API you're building.
MIT
Contributions welcome! Please open an issue or PR.