armature_auth/lib.rs
1//! Authentication and authorization for Armature.
2//!
3//! This crate provides comprehensive authentication and authorization
4//! capabilities including JWT, OAuth2, SAML, password hashing, and guards.
5//!
6//! ## Features
7//!
8//! - 🔐 **JWT Authentication** - Token-based auth with `armature-jwt`
9//! - 🌐 **OAuth2** - Google, Auth0, Microsoft, AWS Cognito, Okta
10//! - 🔑 **SAML** - Enterprise SAML 2.0 authentication (requires `saml` feature)
11//! - 🔒 **Password Hashing** - Secure bcrypt-based hashing
12//! - 🛡️ **Guards** - Route protection with auth and role guards
13//! - 👤 **User Context** - Request-scoped user information
14//!
15//! ## Cargo Features
16//!
17//! - `default` - Core authentication (JWT, OAuth2, password hashing)
18//! - `saml` - SAML 2.0 support (requires openssl/xmlsec1 system libraries)
19//!
20//! ### SAML System Requirements
21//!
22//! The `saml` feature requires system libraries for XML signature verification:
23//! - **Ubuntu/Debian**: `apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl`
24//! - **macOS**: `brew install libxmlsec1`
25//! - **Windows**: Not recommended (complex setup)
26//!
27//! ## Quick Start - Password Authentication
28//!
29//! ```
30//! use armature_auth::{AuthService, PasswordHasher, PasswordVerifier};
31//!
32//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
33//! let auth_service = AuthService::new();
34//!
35//! // Hash a password
36//! let hash = auth_service.hash_password("secret123")?;
37//!
38//! // Verify password
39//! let is_valid = auth_service.verify_password("secret123", &hash)?;
40//! assert!(is_valid);
41//! # Ok(())
42//! # }
43//! ```
44//!
45//! ## JWT Authentication
46//!
47//! ```no_run
48//! use armature_auth::AuthService;
49//! use armature_jwt::{JwtConfig, JwtManager};
50//!
51//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
52//! // Create JWT manager
53//! let jwt_config = JwtConfig::new("your-secret-key".to_string());
54//! let jwt_manager = JwtManager::new(jwt_config)?;
55//!
56//! // Create auth service with JWT
57//! let auth_service = AuthService::with_jwt(jwt_manager);
58//!
59//! // JWT manager is now available
60//! assert!(auth_service.jwt_manager().is_some());
61//! # Ok(())
62//! # }
63//! ```
64//!
65//! ## OAuth2 - Google Example
66//!
67//! ```no_run
68//! use armature_auth::{GoogleProvider, providers::GoogleConfig, OAuth2Provider};
69//!
70//! # #[tokio::main]
71//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
72//! let config = GoogleConfig::new(
73//! "client-id".to_string(),
74//! "client-secret".to_string(),
75//! "https://myapp.com/callback".to_string(),
76//! );
77//!
78//! let provider = GoogleProvider::new(config)?;
79//!
80//! // Generate authorization URL
81//! let (auth_url, state) = provider.authorization_url()?;
82//! println!("Redirect user to: {}", auth_url);
83//! println!("State: {}", state.secret());
84//!
85//! // After callback, exchange code for token
86//! let token = provider.exchange_code("auth-code".to_string()).await?;
87//! println!("Access token: {}", token.access_token);
88//! # Ok(())
89//! # }
90//! ```
91//!
92//! ## Route Guards
93//!
94//! ```ignore
95//! use armature_auth::{AuthGuard, RoleGuard};
96//! use armature_core::{Controller, Get};
97//!
98//! #[controller("/admin")]
99//! struct AdminController;
100//!
101//! impl AdminController {
102//! // Require authentication
103//! #[get("/dashboard")]
104//! #[guard(AuthGuard)]
105//! async fn dashboard(&self) -> Result<HttpResponse, Error> {
106//! Ok(HttpResponse::ok())
107//! }
108//!
109//! // Require specific role
110//! #[get("/users")]
111//! #[guard(RoleGuard::new("admin"))]
112//! async fn users(&self) -> Result<HttpResponse, Error> {
113//! Ok(HttpResponse::ok())
114//! }
115//! }
116//! ```
117//!
118//! ## SAML Authentication
119//!
120//! ```ignore
121//! use armature_auth::{SamlServiceProvider, SamlConfig, IdpMetadata};
122//!
123//! # #[tokio::main]
124//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
125//! let config = SamlConfig {
126//! entity_id: "https://myapp.com".to_string(),
127//! acs_url: "https://myapp.com/callback".to_string(),
128//! sls_url: None,
129//! idp_metadata: IdpMetadata::Url("https://idp.example.com/metadata".to_string()),
130//! sp_certificate: None,
131//! sp_private_key: None,
132//! contact_person: None,
133//! allow_unsigned_assertions: false,
134//! required_attributes: vec![],
135//! };
136//!
137//! let provider = SamlServiceProvider::new(config);
138//!
139//! // Generate SAML auth request
140//! let auth_request = provider.create_auth_request()?;
141//! println!("SAML Request: {}", auth_request.saml_request);
142//! # Ok(())
143//! # }
144//! ```
145
146pub mod api_key;
147pub mod error;
148pub mod guard;
149pub mod oauth2;
150pub mod password;
151pub mod passwordless;
152pub mod providers;
153#[cfg(feature = "saml")]
154pub mod saml;
155pub mod strategy;
156#[cfg(feature = "two-factor")]
157pub mod two_factor;
158pub mod user;
159
160pub use api_key::{ApiKey, ApiKeyError, ApiKeyManager, ApiKeyStore};
161pub use error::{AuthError, Result};
162pub use guard::{AuthGuard, Guard, RoleGuard};
163pub use oauth2::{OAuth2Provider, OAuth2Token, OAuth2UserInfo};
164pub use password::{PasswordHasher, PasswordVerifier};
165pub use passwordless::{MagicLinkToken, PasswordlessError, WebAuthnManager};
166#[cfg(feature = "saml")]
167pub use saml::{
168 ContactInfo, IdpMetadata, SamlAssertion, SamlAuthRequest, SamlConfig, SamlProvider,
169 SamlServiceProvider,
170};
171pub use strategy::{AuthStrategy, JwtStrategy, LocalStrategy};
172#[cfg(feature = "two-factor")]
173pub use two_factor::{BackupCodes, TotpSecret, TwoFactorError};
174pub use user::{AuthUser, UserContext};
175
176// Re-export providers
177pub use providers::{
178 Auth0Provider, AwsCognitoProvider, DiscordProvider, DiscordUser, GitHubProvider, GitHubUser,
179 GitLabProvider, GitLabUser, GoogleProvider, LinkedInProvider, LinkedInUser,
180 MicrosoftEntraProvider, OktaProvider,
181};
182
183use armature_jwt::JwtManager;
184use armature_log::{debug, trace};
185use std::sync::Arc;
186
187/// Authentication service for managing user authentication.
188///
189/// Provides password hashing, verification, and optional JWT token management.
190///
191/// # Examples
192///
193/// Basic password authentication:
194///
195/// ```
196/// use armature_auth::{AuthService, PasswordVerifier};
197///
198/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
199/// let service = AuthService::new();
200///
201/// // Hash password
202/// let hash = service.hash_password("mypassword")?;
203///
204/// // Verify password
205/// assert!(service.verify_password("mypassword", &hash)?);
206/// assert!(!service.verify_password("wrongpassword", &hash)?);
207/// # Ok(())
208/// # }
209/// ```
210#[derive(Clone)]
211pub struct AuthService {
212 jwt_manager: Option<Arc<JwtManager>>,
213 password_hasher: PasswordHasher,
214}
215
216impl AuthService {
217 /// Create a new authentication service
218 pub fn new() -> Self {
219 debug!("Creating new AuthService");
220 Self {
221 jwt_manager: None,
222 password_hasher: PasswordHasher::default(),
223 }
224 }
225
226 /// Create with JWT manager
227 pub fn with_jwt(jwt_manager: JwtManager) -> Self {
228 debug!("Creating AuthService with JWT manager");
229 Self {
230 jwt_manager: Some(Arc::new(jwt_manager)),
231 password_hasher: PasswordHasher::default(),
232 }
233 }
234
235 /// Set password hasher
236 pub fn with_password_hasher(mut self, hasher: PasswordHasher) -> Self {
237 debug!("Setting password hasher");
238 self.password_hasher = hasher;
239 self
240 }
241
242 /// Hash a password
243 pub fn hash_password(&self, password: &str) -> Result<String> {
244 trace!("Hashing password");
245 self.password_hasher.hash(password)
246 }
247
248 /// Verify a password
249 pub fn verify_password(&self, password: &str, hash: &str) -> Result<bool> {
250 trace!("Verifying password");
251 self.password_hasher.verify(password, hash)
252 }
253
254 /// Get JWT manager
255 pub fn jwt_manager(&self) -> Option<&JwtManager> {
256 self.jwt_manager.as_deref()
257 }
258
259 /// Validate authentication
260 pub fn validate<T: AuthUser>(&self, user: &T) -> Result<()> {
261 debug!("Validating user authentication");
262 if !user.is_active() {
263 debug!("User is inactive");
264 return Err(AuthError::InactiveUser);
265 }
266
267 trace!("User validation successful");
268 Ok(())
269 }
270}
271
272impl Default for AuthService {
273 fn default() -> Self {
274 Self::new()
275 }
276}
277
278/// Prelude for common imports.
279///
280/// ```
281/// use armature_auth::prelude::*;
282/// ```
283pub mod prelude {
284 pub use crate::AuthService;
285 pub use crate::api_key::{ApiKey, ApiKeyManager, ApiKeyStore};
286 pub use crate::error::{AuthError, Result};
287 pub use crate::guard::{AuthGuard, Guard, RoleGuard};
288 pub use crate::oauth2::{OAuth2Provider, OAuth2Token, OAuth2UserInfo};
289 pub use crate::password::{PasswordHasher, PasswordVerifier};
290 pub use crate::strategy::{AuthStrategy, JwtStrategy, LocalStrategy};
291 pub use crate::user::{AuthUser, UserContext};
292
293 // OAuth2 providers
294 pub use crate::providers::{
295 Auth0Provider, AwsCognitoProvider, DiscordProvider, GitHubProvider, GitLabProvider,
296 GoogleProvider, LinkedInProvider, MicrosoftEntraProvider, OktaProvider,
297 };
298
299 #[cfg(feature = "saml")]
300 pub use crate::saml::{SamlConfig, SamlProvider, SamlServiceProvider};
301
302 #[cfg(feature = "two-factor")]
303 pub use crate::two_factor::{BackupCodes, TotpSecret, TwoFactorError};
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309
310 #[test]
311 fn test_password_hashing() {
312 let service = AuthService::new();
313
314 let password = "test-password-123";
315 let hash = service.hash_password(password).unwrap();
316
317 assert!(service.verify_password(password, &hash).unwrap());
318 assert!(!service.verify_password("wrong-password", &hash).unwrap());
319 }
320}