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}