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}