axum_gate/credentials/mod.rs
1//! User credential types and verification abstractions.
2//!
3//! This module provides the [`Credentials`] type for handling user login data
4//! and the [`CredentialsVerifier`] trait for validating credentials against
5//! stored authentication secrets.
6//!
7//! # Quick Start
8//!
9//! ```rust
10//! use axum_gate::prelude::Credentials;
11//!
12//! // Create credentials from user input
13//! let credentials = Credentials::new(&"user@example.com".to_string(), "password123");
14//!
15//! // Use with JSON APIs
16//! use serde_json;
17//! let json = r#"{"id": "admin@company.com", "secret": "admin_pass"}"#;
18//! let creds: Credentials<String> = serde_json::from_str(json)?;
19//! # Ok::<(), Box<dyn std::error::Error>>(())
20//! ```
21//!
22//! # Security Considerations
23//!
24//! **⚠️ Important Security Notes:**
25//!
26//! - **Contains plaintext secrets** - Handle credentials with extreme care
27//! - **Never log credentials** - They contain sensitive password data
28//! - **Minimize lifetime** - Process and discard credentials quickly
29//! - **Use HTTPS/TLS** - Always encrypt credentials during transmission
30//! - **Timing attack protection** - Built-in when using axum-gate login services
31//!
32//! # Integration with Authentication
33//!
34//! Credentials integrate seamlessly with axum-gate's authentication system:
35//!
36//! ```rust
37//! use axum::{Json, extract::State, http::StatusCode};
38//! use axum_gate::prelude::Credentials;
39//! use axum_gate::route_handlers::login;
40//! use axum_extra::extract::CookieJar;
41//!
42//! async fn login_endpoint(
43//! cookie_jar: CookieJar,
44//! Json(credentials): Json<Credentials<String>>,
45//! // State with repositories and JWT codec...
46//! ) -> Result<CookieJar, StatusCode> {
47//! // Use the pre-built login handler
48//! // login(cookie_jar, credentials, claims, secret_repo, account_repo, codec, template).await
49//! # Ok(cookie_jar)
50//! }
51//! ```
52
53#[cfg(feature = "server")]
54pub use self::credentials_verifier::CredentialsVerifier;
55use serde::{Deserialize, Serialize};
56
57#[cfg(feature = "server")]
58mod credentials_verifier;
59
60/// Authentication credentials containing a user identifier and plaintext secret.
61///
62/// This type represents user login data as typically received from client applications,
63/// containing a user identifier (such as email, username, or user ID) paired with a
64/// plaintext secret (password). Credentials are used during the authentication process
65/// to verify user identity against stored account data.
66///
67/// # Generic Parameter
68///
69/// - `Id`: The type of user identifier (commonly [`String`] for emails/usernames, or [`uuid::Uuid`] for user IDs)
70///
71/// # Security Considerations
72///
73/// **⚠️ Critical Security Notes:**
74///
75/// - **Plaintext secrets**: This type contains unencrypted passwords - handle with extreme care
76/// - **Memory safety**: Minimize the lifetime of credential instances in memory
77/// - **Logging**: Never log or print credential values - they contain sensitive data
78/// - **Transport security**: Always use HTTPS/TLS when transmitting credentials
79/// - **Storage**: Never store credentials directly - convert to hashed secrets for persistence
80///
81/// # Authentication Flow Integration
82///
83/// Credentials are typically used as input to authentication services:
84///
85/// ```rust
86/// use axum_gate::prelude::Credentials;
87///
88/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
89/// // 1. Receive credentials from client (e.g., JSON payload)
90/// let client_credentials = Credentials::new(&"user@example.com".to_string(), "user_password");
91///
92/// // 2. Use in authentication service
93/// // The login service will:
94/// // - Look up the user account
95/// // - Retrieve the stored secret hash
96/// // - Verify the plaintext password against the hash
97/// // - Return authentication result
98/// # Ok(())
99/// # }
100/// ```
101///
102/// # Timing Attack Protection
103///
104/// When used with the built-in [`login`](crate::route_handlers::login) handler or [`LoginService`](crate::authn::LoginService),
105/// credentials are processed using constant-time operations to prevent timing-based
106/// user enumeration attacks:
107///
108/// - Authentication takes consistent time regardless of whether the user exists
109/// - Password verification always occurs, even for non-existent users
110/// - Error responses don't distinguish between "user not found" and "wrong password"
111///
112/// # JSON Serialization
113///
114/// Credentials support JSON serialization for API integration:
115///
116/// ```rust
117/// use axum_gate::prelude::Credentials;
118/// use serde_json;
119///
120/// // Deserialize from JSON (typical in REST APIs)
121/// let json = r#"{"id": "user@example.com", "secret": "password123"}"#;
122/// let credentials: Credentials<String> = serde_json::from_str(json)?;
123///
124/// // Serialize to JSON (less common, avoid logging)
125/// let json = serde_json::to_string(&credentials)?;
126/// # Ok::<(), Box<dyn std::error::Error>>(())
127/// ```
128///
129/// # Different Identifier Types
130///
131/// ```rust
132/// use axum_gate::prelude::Credentials;
133/// use uuid::Uuid;
134///
135/// // String-based identifiers (email, username)
136/// let email_creds = Credentials::new(&"user@domain.com".to_string(), "password");
137/// let username_creds = Credentials::new(&"johndoe".to_string(), "secret123");
138///
139/// // UUID-based identifiers
140/// let user_id = Uuid::now_v7();
141/// let uuid_creds = Credentials::new(&user_id, "user_password");
142///
143/// // Custom identifier types work with any Clone type
144/// #[derive(Clone)]
145/// struct UserId(u64);
146///
147/// let user_id = UserId(12345);
148/// let custom_creds = Credentials::new(&user_id, "password");
149/// ```
150///
151/// # Integration with axum Extractors
152///
153/// ```rust
154/// use axum::{Json, extract::State, http::StatusCode};
155/// use axum_gate::prelude::Credentials;
156///
157/// // Extract credentials from JSON request body
158/// async fn login_endpoint(
159/// Json(credentials): Json<Credentials<String>>,
160/// ) -> Result<String, StatusCode> {
161/// // Process credentials...
162/// Ok("Login successful".to_string())
163/// }
164///
165/// // Extract credentials from form data
166/// use axum::extract::Form;
167/// async fn form_login(
168/// Form(credentials): Form<Credentials<String>>,
169/// ) -> Result<String, StatusCode> {
170/// // Process form-submitted credentials...
171/// Ok("Login successful".to_string())
172/// }
173/// ```
174#[derive(Serialize, Deserialize, Debug, Clone)]
175pub struct Credentials<Id> {
176 /// The identification of the user, eg. a username.
177 pub id: Id,
178 /// The secret of the user, eg. a password.
179 pub secret: String,
180}
181
182impl<Id> Credentials<Id> {
183 /// Creates new credentials with the specified identifier and plaintext secret.
184 ///
185 /// This constructor creates a new [`Credentials`] instance containing a user identifier
186 /// and plaintext password. The identifier is cloned and the secret is converted to an
187 /// owned string for storage within the credentials.
188 ///
189 /// # Parameters
190 ///
191 /// - `id`: User identifier (email, username, UUID, etc.) - must implement [`ToOwned`]
192 /// - `secret`: Plaintext password or secret as received from the user
193 ///
194 /// # Security Warning
195 ///
196 /// The created credentials contain plaintext secrets. Ensure proper security practices:
197 /// - Use credentials immediately for authentication
198 /// - Avoid storing credentials in logs or persistent storage
199 /// - Clear credentials from memory when no longer needed
200 /// - Transmit only over secure channels (HTTPS/TLS)
201 ///
202 /// # Examples
203 ///
204 /// ## String-based Authentication
205 ///
206 /// ```rust
207 /// use axum_gate::prelude::Credentials;
208 ///
209 /// let credentials = Credentials::new(&"user@example.com".to_string(), "secure_password");
210 /// assert_eq!(credentials.id, "user@example.com");
211 /// assert_eq!(credentials.secret, "secure_password");
212 /// ```
213 ///
214 /// ## UUID-based Authentication
215 ///
216 /// ```rust
217 /// use axum_gate::prelude::Credentials;
218 /// use uuid::Uuid;
219 ///
220 /// let user_id = Uuid::now_v7();
221 /// let credentials = Credentials::new(&user_id, "user_secret");
222 /// assert_eq!(credentials.id, user_id);
223 /// assert_eq!(credentials.secret, "user_secret");
224 /// ```
225 ///
226 /// ## Usage in Authentication Flow
227 ///
228 /// ```rust
229 /// use axum_gate::prelude::Credentials;
230 ///
231 /// // Typically created from user input
232 /// let user_input_email = "admin@company.com";
233 /// let user_input_password = "admin_password";
234 ///
235 /// let credentials = Credentials::new(
236 /// &user_input_email.to_string(),
237 /// user_input_password
238 /// );
239 ///
240 /// // Credentials are now ready for authentication verification
241 /// // Pass to login service or authentication handler
242 /// ```
243 ///
244 /// # Generic Type Requirements
245 ///
246 /// The identifier type `Id` must implement [`ToOwned`] to allow the credentials
247 /// to take ownership of the identifier value. This is automatically satisfied by
248 /// common types like [`String`], [`uuid::Uuid`], and most primitive types.
249 pub fn new(id: &Id, secret: &str) -> Self
250 where
251 Id: ToOwned<Owned = Id>,
252 {
253 Self {
254 id: id.to_owned(),
255 secret: secret.to_string(),
256 }
257 }
258}