| Crates.io | reinhardt-auth |
| lib.rs | reinhardt-auth |
| version | 0.1.0-alpha.1 |
| created_at | 2026-01-23 05:46:22.321879+00 |
| updated_at | 2026-01-23 05:46:22.321879+00 |
| description | Authentication and authorization system |
| homepage | |
| repository | https://github.com/kent8192/reinhardt-rs |
| max_upload_size | |
| id | 2063559 |
| size | 944,290 |
Authentication and authorization system for Reinhardt framework.
Comprehensive authentication and authorization system inspired by Django and Django REST Framework. Provides JWT tokens, permission classes, user models, and password hashing with Argon2.
Add reinhardt to your Cargo.toml:
[dependencies]
reinhardt = { version = "0.1.0-alpha.1", features = ["auth"] }
# Or use a preset:
# reinhardt = { version = "0.1.0-alpha.1", features = ["standard"] } # Recommended
# reinhardt = { version = "0.1.0-alpha.1", features = ["full"] } # All features
Then import authentication features:
use reinhardt::auth::{User, SimpleUser, AnonymousUser};
use reinhardt::auth::{JwtAuth, HttpBasicAuth, AuthenticationBackend};
use reinhardt::auth::{AllowAny, IsAuthenticated, IsAuthenticatedOrReadOnly};
Note: Authentication features are included in the standard and full feature presets.
Claims struct with user identification, expiration,
and issue timesuse reinhardt::auth::jwt::{JwtAuth, Claims};
use chrono::Duration;
let jwt_auth = JwtAuth::new(b"my-secret-key");
let token = jwt_auth.generate_token("user123".to_string(), "john_doe".to_string()).unwrap();
let claims = jwt_auth.verify_token(&token).unwrap();
use reinhardt::auth::{HttpBasicAuth, AuthenticationBackend};
let mut auth = HttpBasicAuth::new();
auth.add_user("alice", "secret123");
// Request with Basic auth header will be authenticated
let result = auth.authenticate(&request).unwrap();
id(), username(), get_username() methodsis_authenticated(), is_active(), is_admin()
checksuse reinhardt::auth::{User, SimpleUser, AnonymousUser};
use uuid::Uuid;
let user = SimpleUser {
id: Uuid::new_v4(),
username: "john".to_string(),
email: "john@example.com".to_string(),
is_active: true,
is_admin: false,
};
assert!(user.is_authenticated());
assert!(!user.is_admin());
type Hasher: PasswordHasher + Default = Argon2Hasherset_password(): Automatically hashes with configured hashercheck_password(): Verifies password against hashset_unusable_password(): Marks password as unusable (for OAuth-only
accounts)has_usable_password(): Checks if user can log in with passwordget_session_auth_hash() for session invalidation
on password changeuse reinhardt::auth::BaseUser;
use uuid::Uuid;
use chrono::Utc;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct MyUser {
id: Uuid,
email: String,
password_hash: Option<String>,
last_login: Option<chrono::DateTime<Utc>>,
is_active: bool,
}
impl BaseUser for MyUser {
type PrimaryKey = Uuid;
// type Hasher = Argon2Hasher; // Default, can be omitted or customized
fn get_username_field() -> &'static str { "email" }
fn get_username(&self) -> &str { &self.email }
fn password_hash(&self) -> Option<&str> { self.password_hash.as_deref() }
fn set_password_hash(&mut self, hash: String) { self.password_hash = Some(hash); }
fn last_login(&self) -> Option<chrono::DateTime<Utc>> { self.last_login }
fn set_last_login(&mut self, time: chrono::DateTime<Utc>) { self.last_login = Some(time); }
fn is_active(&self) -> bool { self.is_active }
}
let mut user = MyUser {
id: Uuid::new_v4(),
email: "alice@example.com".to_string(),
password_hash: None,
last_login: None,
is_active: true,
};
// Password is automatically hashed with Argon2id
user.set_password("securepass123").unwrap();
assert!(user.check_password("securepass123").unwrap());
username(): Unique username for loginemail(): Email addressfirst_name() / last_name(): User's nameis_staff(): Can access admin interfaceis_superuser(): Has all permissionsdate_joined(): Account creation timestampget_full_name(): Combines first and last nameget_short_name(): Returns first name onlyuse reinhardt::auth::{BaseUser, FullUser, DefaultUser};
use uuid::Uuid;
use chrono::Utc;
let mut user = DefaultUser {
id: Uuid::new_v4(),
username: "alice".to_string(),
email: "alice@example.com".to_string(),
first_name: "Alice".to_string(),
last_name: "Smith".to_string(),
password_hash: None,
last_login: None,
is_active: true,
is_staff: false,
is_superuser: false,
date_joined: Utc::now(),
user_permissions: Vec::new(),
groups: Vec::new(),
};
assert_eq!(user.get_full_name(), "Alice Smith");
assert_eq!(user.get_short_name(), "Alice");
has_perm(perm): Check if user has specific permissionhas_module_perms(app_label): Check if user has any permission for appget_all_permissions(): Get all user and group permissions"app_label.permission_name"use reinhardt::auth::{DefaultUser, PermissionsMixin};
use uuid::Uuid;
use chrono::Utc;
let user = DefaultUser {
id: Uuid::new_v4(),
username: "bob".to_string(),
email: "bob@example.com".to_string(),
first_name: "Bob".to_string(),
last_name: "Johnson".to_string(),
password_hash: None,
last_login: None,
is_active: true,
is_staff: true,
is_superuser: false,
date_joined: Utc::now(),
user_permissions: vec![
"blog.add_post".to_string(),
"blog.change_post".to_string(),
],
groups: vec!["editors".to_string()],
};
assert!(user.has_perm("blog.add_post"));
assert!(user.has_module_perms("blog"));
#[derive(Model)] for ORM integrationauth_user (Django-compatible)use reinhardt::auth::{BaseUser, DefaultUser, DefaultUserManager};
use std::collections::HashMap;
# tokio_test::block_on(async {
let mut manager = DefaultUserManager::new();
// Create a regular user
let user = manager.create_user(
"alice",
Some("securepass123"),
HashMap::new()
).await.unwrap();
assert_eq!(user.username, "alice");
assert!(user.is_active);
assert!(!user.is_staff);
// Create a superuser
let admin = manager.create_superuser(
"admin",
Some("adminsecret"),
HashMap::new()
).await.unwrap();
assert!(admin.is_staff);
assert!(admin.is_superuser);
# })
create_user() and create_superuser() methodsnormalize_email() static methodArc<RwLock<HashMap>> for concurrent accessget_by_id() and get_by_username() methodsuse reinhardt::auth::{Argon2Hasher, PasswordHasher};
let hasher = Argon2Hasher::new();
let hash = hasher.hash("my_password").unwrap();
assert!(hasher.verify("my_password", &hash).unwrap());
async_traitauthenticate(username, password) methodget_user(user_id) for session restorationuse reinhardt::auth::CompositeAuthBackend;
let mut composite = CompositeAuthBackend::new();
composite.add_backend(Box::new(database_backend));
composite.add_backend(Box::new(ldap_backend));
// Will try database first, then LDAP
let user = composite.authenticate("alice", "password").await;
has_permission() method with contextuse reinhardt::auth::{Permission, IsAuthenticated, PermissionContext};
let permission = IsAuthenticated;
let context = PermissionContext {
request: &request,
is_authenticated: true,
is_admin: false,
is_active: true,
};
assert!(permission.has_permission(&context).await);
AuthenticationErrorstd::error::ErrorSession struct with HashMap-based data storageload(): Retrieve session by IDsave(): Persist session datadelete(): Remove sessionuse reinhardt::auth::{SessionAuthentication, Authentication};
use reinhardt::auth::sessions::backends::InMemorySessionBackend;
let session_backend = InMemorySessionBackend::new();
let auth = SessionAuthentication::new(session_backend);
// Authenticate user from request (checks session cookie)
let user = auth.authenticate(&request).await?;
// Get user by ID
if let Some(user) = auth.get_user("user_id").await? {
println!("User: {}", user.get_username());
}
use reinhardt::auth::MFAAuthentication;
let mfa = MFAAuthentication::new("MyApp");
// Register user for MFA
let totp_url = mfa.register_user("alice").await?;
// User scans QR code generated from totp_url
// Verify code during login
let code = "123456"; // from user's authenticator app
assert!(mfa.verify_code("alice", code).await?);
OAuth2Application with client credentialsOAuth2Token with access and refresh tokensuse reinhardt::auth::{OAuth2Authentication, GrantType, InMemoryOAuth2Store};
let store = InMemoryOAuth2Store::new();
let oauth2 = OAuth2Authentication::new(store);
// Register OAuth2 application
oauth2.register_application(
"client123",
"secret456",
"https://example.com/callback",
vec![GrantType::AuthorizationCode]
).await?;
// Authorization code flow
let code = oauth2.generate_authorization_code("client123", "user123", vec!["read", "write"]).await?;
let token = oauth2.exchange_code(&code, "client123").await?;
// Use access token
let claims = oauth2.verify_token(&token.access_token).await?;
Logout: User-initiated logoutCompromised: Security incidentManualRevoke: Admin revocationRotated: Automatic token rotationuse reinhardt::auth::{
TokenBlacklist, InMemoryBlacklist, BlacklistReason,
TokenRotationManager, InMemoryRefreshStore
};
// Token blacklist
let blacklist = InMemoryBlacklist::new();
use chrono::{Utc, Duration};
let expires_at = Utc::now() + Duration::hours(24);
blacklist.blacklist("old_token", expires_at, BlacklistReason::Logout).await?;
assert!(blacklist.is_blacklisted("old_token").await?);
// Token rotation
let refresh_store = InMemoryRefreshStore::new();
let rotation_manager = TokenRotationManager::new(blacklist, refresh_store);
let new_token = rotation_manager.rotate_token("old_refresh_token", "user123").await?;
REMOTE_USER)use reinhardt::auth::RemoteUserAuthentication;
// Standard configuration
let auth = RemoteUserAuthentication::new("REMOTE_USER");
// With force logout
let auth = RemoteUserAuthentication::new("REMOTE_USER").force_logout_if_no_header(true);
// Authenticate from request
let user = auth.authenticate(&request).await?;
use reinhardt::auth::{
JwtAuth, HttpBasicAuth, AuthBackend,
SimpleUser, User, Argon2Hasher, PasswordHasher,
Permission, IsAuthenticated, PermissionContext
};
// 1. Set up JWT authentication
let jwt_auth = JwtAuth::new(b"secret-key");
// 2. Set up Basic authentication with a user
let mut basic_auth = HttpBasicAuth::new();
basic_auth.add_user("alice", "password123");
// 3. Authenticate user and generate JWT
let user = basic_auth.authenticate(&request).unwrap().unwrap();
let token = jwt_auth.generate_token(
user.id(),
user.username().to_string()
).unwrap();
// 4. Verify token on subsequent requests
let claims = jwt_auth.verify_token(&token).unwrap();
// 5. Check permissions
let permission = IsAuthenticated;
let context = PermissionContext {
request: &request,
is_authenticated: true,
is_admin: user.is_admin(),
is_active: user.is_active(),
};
if permission.has_permission(&context).await {
// Grant access
}
use reinhardt::auth::{AuthBackend, SimpleUser, Argon2Hasher, PasswordHasher};
use async_trait::async_trait;
use std::collections::HashMap;
struct MyAuthBackend {
users: HashMap<String, (String, SimpleUser)>,
hasher: Argon2Hasher,
}
#[async_trait]
impl AuthBackend for MyAuthBackend {
type User = SimpleUser;
async fn authenticate(
&self,
username: &str,
password: &str,
) -> Result<Option<Self::User>, reinhardt_exception::Error> {
if let Some((hash, user)) = self.users.get(username) {
if self.hasher.verify(password, hash)? {
return Ok(Some(user.clone()));
}
}
Ok(None)
}
async fn get_user(&self, user_id: &str)
-> Result<Option<Self::User>, reinhardt_exception::Error> {
Ok(self.users.values()
.find(|(_, u)| u.id.to_string() == user_id)
.map(|(_, u)| u.clone()))
}
}
serde supportInMemoryCache
Cache trait implementationreinhardt-di for dependency injectionB: SessionBackendget(), set(), delete(), contains_key()keys(), values(), items()clear()mark_modified(), mark_unmodified()is_modified(), is_accessed()get_or_create_key(), generate_key()flush() (clear and new key), cycle_key() (keep data, new key)save() method with TTL support (default: 3600 seconds)database) - Persistent session storage in database
cleanup_expired()create_table()file) - File-based session storage
/tmp/reinhardt_sessions)fs2 for concurrent access safetycookie) - Encrypted session data in cookies
middleware) - HTTP middleware for session management
cleanup_expired() in DatabaseSessionBackendcycle_key() and flush() in Session APIserde_jsonmessagepack) - Compact binary format, cross-platform via rmp-serdecbor) - RFC 7049 compliant binary format via ciboriumbincode) - Fastest for Rust-to-Rust communicationcompression) - Automatic compression wrapper
compression-zstd) - Best balance of speed and ratiocompression-gzip) - Wide compatibilitycompression-brotli) - Best compression ratioreplication) - High availability with multi-backend replication
analytics-prometheus) - Prometheus metrics export
session_created_total - Total sessions createdsession_accessed_total - Total session accessessession_access_latency_seconds - Access latency histogramsession_size_bytes - Session data size histogramsession_deleted_total - Deletions by reason (explicit, expired, flushed)session_expired_total - Total expired sessionstenant) - Tenant-specific session namespacing
tenant:{tenant_id}:session:{session_id}list_sessions(), count_sessions(), delete_all_sessions()Licensed under either of:
at your option.