Skip to main content

webgates_core/
credentials.rs

1//! User credential types and verification abstractions.
2//!
3//! This module defines the authentication boundary for `webgates-core`.
4//! It intentionally stays small: the crate represents user-supplied credentials,
5//! but leaves verification strategy and storage details to higher-level code.
6//!
7//! This module exposes:
8//!
9//! - [`Credentials`] stores a caller-provided identifier and plaintext secret
10//! - [`credentials_verifier::CredentialsVerifier`] defines the async verification contract used by
11//!   higher-level authentication services
12//!
13//! Import [`Credentials`] directly from `webgates_core::credentials` and
14//! [`credentials_verifier::CredentialsVerifier`] from the owning submodule.
15//!
16//! # Quick Start
17//!
18//! ```rust
19//! use webgates_core::credentials::Credentials;
20//!
21//! let credentials = Credentials::new(&"user@example.com".to_string(), "password123");
22//!
23//! let json = r#"{"id":"admin@company.com","secret":"admin_pass"}"#;
24//! let parsed: Credentials<String> = serde_json::from_str(json)?;
25//!
26//! assert_eq!(parsed.id, "admin@company.com");
27//! # let _ = credentials;
28//! # Ok::<(), Box<dyn std::error::Error>>(())
29//! ```
30//!
31//! # Verification boundary
32//!
33//! Concrete verifier implementations belong in higher-level crates or in your
34//! application code. `webgates-core` only defines the shared types and the
35//! verification trait that those layers implement.
36//!
37//! # Security Considerations
38//!
39//! - Credentials contain plaintext secrets and should be short-lived.
40//! - Never log or persist raw credentials.
41//! - Only transmit credentials over secure channels such as HTTPS/TLS.
42//! - Verification backends should avoid leaking identifier existence through
43//!   observable timing or error details.
44use serde::{Deserialize, Serialize};
45
46/// Async verification contract for credential backends.
47pub mod credentials_verifier;
48
49/// Authentication credentials containing a user identifier and plaintext secret.
50///
51/// This type represents raw login input at the authentication boundary. It
52/// stores the caller-provided identifier together with a plaintext secret so a
53/// verifier can compare it against stored authentication material.
54///
55/// # Type Parameter
56///
57/// - `Id`: Identifier type used by the calling application, commonly [`String`]
58///   or [`uuid::Uuid`]
59///
60/// # Security
61///
62/// - The `secret` field contains plaintext sensitive data.
63/// - Keep instances short-lived and avoid cloning them unnecessarily.
64/// - Never log, persist, or expose the raw secret.
65/// - Use secure transport when credentials cross process or network boundaries.
66///
67/// # Examples
68///
69/// Create credentials from application input:
70///
71/// ```rust
72/// use webgates_core::credentials::Credentials;
73///
74/// let credentials = Credentials::new(&"user@example.com".to_string(), "user_password");
75///
76/// assert_eq!(credentials.id, "user@example.com");
77/// assert_eq!(credentials.secret, "user_password");
78/// ```
79///
80/// Deserialize credentials from JSON:
81///
82/// ```rust
83/// use webgates_core::credentials::Credentials;
84///
85/// let json = r#"{"id":"user@example.com","secret":"password123"}"#;
86/// let credentials: Credentials<String> = serde_json::from_str(json)?;
87///
88/// assert_eq!(credentials.id, "user@example.com");
89/// # Ok::<(), Box<dyn std::error::Error>>(())
90/// ```
91///
92/// Use a UUID identifier:
93///
94/// ```rust
95/// use uuid::Uuid;
96/// use webgates_core::credentials::Credentials;
97///
98/// let user_id = Uuid::now_v7();
99/// let credentials = Credentials::new(&user_id, "user_password");
100///
101/// assert_eq!(credentials.id, user_id);
102/// ```
103#[derive(Serialize, Deserialize, Debug, Clone)]
104pub struct Credentials<Id> {
105    /// User identifier, such as a username, email address, or UUID.
106    pub id: Id,
107    /// Plaintext secret supplied by the caller, such as a password.
108    pub secret: String,
109}
110
111impl<Id> Credentials<Id> {
112    /// Creates credentials from an identifier and a plaintext secret.
113    ///
114    /// The identifier is cloned into the returned value and the secret is stored
115    /// as an owned [`String`].
116    ///
117    /// # Parameters
118    ///
119    /// - `id`: User identifier to copy into the credentials
120    /// - `secret`: Plaintext secret supplied by the caller
121    ///
122    /// # Security
123    ///
124    /// The returned value contains plaintext sensitive data. Callers should keep
125    /// it short-lived and avoid logging or persisting it.
126    ///
127    /// # Examples
128    ///
129    /// ```rust
130    /// use webgates_core::credentials::Credentials;
131    ///
132    /// let credentials = Credentials::new(&"admin@company.com".to_string(), "admin_password");
133    ///
134    /// assert_eq!(credentials.id, "admin@company.com");
135    /// assert_eq!(credentials.secret, "admin_password");
136    /// ```
137    ///
138    /// ```rust
139    /// use uuid::Uuid;
140    /// use webgates_core::credentials::Credentials;
141    ///
142    /// let user_id = Uuid::now_v7();
143    /// let credentials = Credentials::new(&user_id, "user_secret");
144    ///
145    /// assert_eq!(credentials.id, user_id);
146    /// ```
147    pub fn new(id: &Id, secret: &str) -> Self
148    where
149        Id: ToOwned<Owned = Id>,
150    {
151        Self {
152            id: id.to_owned(),
153            secret: secret.to_string(),
154        }
155    }
156}