Skip to main content

shopify_sdk/clients/storefront/
token.rs

1//! Storefront API token types for authentication.
2//!
3//! This module provides the [`StorefrontToken`] enum for type-safe authentication
4//! with the Shopify Storefront API.
5//!
6//! # Token Types
7//!
8//! The Storefront API supports two types of access tokens:
9//!
10//! - **Public tokens**: Used for client-side applications and have limited access.
11//!   These use the `X-Shopify-Storefront-Access-Token` header.
12//!
13//! - **Private tokens**: Used for server-side applications with elevated access.
14//!   These use the `Shopify-Storefront-Private-Token` header.
15//!
16//! # Security
17//!
18//! The [`StorefrontToken`] type implements a custom [`Debug`] trait that masks
19//! the token value, preventing accidental exposure in logs.
20//!
21//! # Example
22//!
23//! ```rust
24//! use shopify_sdk::StorefrontToken;
25//!
26//! // Public token (client-side)
27//! let public_token = StorefrontToken::Public("public-access-token".to_string());
28//! assert_eq!(public_token.header_name(), "X-Shopify-Storefront-Access-Token");
29//!
30//! // Private token (server-side)
31//! let private_token = StorefrontToken::Private("private-access-token".to_string());
32//! assert_eq!(private_token.header_name(), "Shopify-Storefront-Private-Token");
33//!
34//! // Debug output masks the token value
35//! let debug_output = format!("{:?}", public_token);
36//! assert!(debug_output.contains("*****"));
37//! assert!(!debug_output.contains("public-access-token"));
38//! ```
39
40use std::fmt;
41
42/// HTTP header name for public storefront access tokens.
43pub const PUBLIC_HEADER_NAME: &str = "X-Shopify-Storefront-Access-Token";
44
45/// HTTP header name for private storefront access tokens.
46pub const PRIVATE_HEADER_NAME: &str = "Shopify-Storefront-Private-Token";
47
48/// A Shopify Storefront API access token.
49///
50/// This enum provides type-safe representation of storefront access tokens,
51/// automatically selecting the correct HTTP header based on the token type.
52///
53/// # Token Types
54///
55/// - [`Public`](Self::Public): For client-side applications with limited access
56/// - [`Private`](Self::Private): For server-side applications with elevated access
57///
58/// # Security
59///
60/// The [`Debug`] implementation masks token values to prevent accidental exposure:
61///
62/// ```rust
63/// use shopify_sdk::StorefrontToken;
64///
65/// let token = StorefrontToken::Public("secret-token".to_string());
66/// let debug = format!("{:?}", token);
67/// assert_eq!(debug, "StorefrontToken::Public(*****)");
68/// ```
69///
70/// # Example
71///
72/// ```rust
73/// use shopify_sdk::StorefrontToken;
74///
75/// let token = StorefrontToken::Public("my-token".to_string());
76///
77/// // Get the correct header name
78/// assert_eq!(token.header_name(), "X-Shopify-Storefront-Access-Token");
79///
80/// // Get the token value
81/// assert_eq!(token.header_value(), "my-token");
82/// ```
83#[derive(Clone)]
84pub enum StorefrontToken {
85    /// Public storefront access token for client-side applications.
86    ///
87    /// Uses the `X-Shopify-Storefront-Access-Token` header.
88    /// These tokens have limited access and are safe to expose in client-side code.
89    Public(String),
90
91    /// Private storefront access token for server-side applications.
92    ///
93    /// Uses the `Shopify-Storefront-Private-Token` header.
94    /// These tokens have elevated access and should only be used server-side.
95    Private(String),
96}
97
98impl StorefrontToken {
99    /// Returns the HTTP header name for this token type.
100    ///
101    /// - [`Public`](Self::Public): `X-Shopify-Storefront-Access-Token`
102    /// - [`Private`](Self::Private): `Shopify-Storefront-Private-Token`
103    ///
104    /// # Example
105    ///
106    /// ```rust
107    /// use shopify_sdk::StorefrontToken;
108    ///
109    /// let public = StorefrontToken::Public("token".to_string());
110    /// assert_eq!(public.header_name(), "X-Shopify-Storefront-Access-Token");
111    ///
112    /// let private = StorefrontToken::Private("token".to_string());
113    /// assert_eq!(private.header_name(), "Shopify-Storefront-Private-Token");
114    /// ```
115    #[must_use]
116    pub const fn header_name(&self) -> &'static str {
117        match self {
118            Self::Public(_) => PUBLIC_HEADER_NAME,
119            Self::Private(_) => PRIVATE_HEADER_NAME,
120        }
121    }
122
123    /// Returns the token value as a string slice.
124    ///
125    /// This is used to set the HTTP header value when making requests.
126    ///
127    /// # Example
128    ///
129    /// ```rust
130    /// use shopify_sdk::StorefrontToken;
131    ///
132    /// let token = StorefrontToken::Public("my-access-token".to_string());
133    /// assert_eq!(token.header_value(), "my-access-token");
134    /// ```
135    #[must_use]
136    pub fn header_value(&self) -> &str {
137        match self {
138            Self::Public(token) | Self::Private(token) => token,
139        }
140    }
141}
142
143impl fmt::Debug for StorefrontToken {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        match self {
146            Self::Public(_) => f.write_str("StorefrontToken::Public(*****)"),
147            Self::Private(_) => f.write_str("StorefrontToken::Private(*****)"),
148        }
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    // === Header Name Tests ===
157
158    #[test]
159    fn test_public_token_returns_correct_header_name() {
160        let token = StorefrontToken::Public("test-token".to_string());
161        assert_eq!(token.header_name(), "X-Shopify-Storefront-Access-Token");
162    }
163
164    #[test]
165    fn test_private_token_returns_correct_header_name() {
166        let token = StorefrontToken::Private("test-token".to_string());
167        assert_eq!(token.header_name(), "Shopify-Storefront-Private-Token");
168    }
169
170    // === Header Value Tests ===
171
172    #[test]
173    fn test_public_token_header_value_returns_token_string() {
174        let token = StorefrontToken::Public("my-public-token".to_string());
175        assert_eq!(token.header_value(), "my-public-token");
176    }
177
178    #[test]
179    fn test_private_token_header_value_returns_token_string() {
180        let token = StorefrontToken::Private("my-private-token".to_string());
181        assert_eq!(token.header_value(), "my-private-token");
182    }
183
184    // === Debug Masking Tests ===
185
186    #[test]
187    fn test_debug_masks_public_token_value() {
188        let token = StorefrontToken::Public("super-secret-token".to_string());
189        let debug_output = format!("{:?}", token);
190
191        assert_eq!(debug_output, "StorefrontToken::Public(*****)");
192        assert!(!debug_output.contains("super-secret-token"));
193    }
194
195    #[test]
196    fn test_debug_masks_private_token_value() {
197        let token = StorefrontToken::Private("super-secret-token".to_string());
198        let debug_output = format!("{:?}", token);
199
200        assert_eq!(debug_output, "StorefrontToken::Private(*****)");
201        assert!(!debug_output.contains("super-secret-token"));
202    }
203
204    // === Clone Tests ===
205
206    #[test]
207    fn test_public_token_clone_works_correctly() {
208        let original = StorefrontToken::Public("cloneable-token".to_string());
209        let cloned = original.clone();
210
211        assert_eq!(cloned.header_value(), "cloneable-token");
212        assert_eq!(cloned.header_name(), original.header_name());
213    }
214
215    #[test]
216    fn test_private_token_clone_works_correctly() {
217        let original = StorefrontToken::Private("cloneable-token".to_string());
218        let cloned = original.clone();
219
220        assert_eq!(cloned.header_value(), "cloneable-token");
221        assert_eq!(cloned.header_name(), original.header_name());
222    }
223
224    // === Constant Header Name Tests ===
225
226    #[test]
227    fn test_public_header_name_constant() {
228        assert_eq!(PUBLIC_HEADER_NAME, "X-Shopify-Storefront-Access-Token");
229    }
230
231    #[test]
232    fn test_private_header_name_constant() {
233        assert_eq!(PRIVATE_HEADER_NAME, "Shopify-Storefront-Private-Token");
234    }
235}