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}