supabase_auth/
models.rs

1#![cfg(not(doctest))]
2
3use core::fmt;
4use reqwest::{Client, Url};
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use std::{collections::HashMap, fmt::Display};
8use uuid::Uuid;
9
10/// Supabase Auth Client
11#[derive(Clone)]
12pub struct AuthClient {
13    pub(crate) client: Client,
14    /// REST endpoint for querying and managing your database
15    /// Example: `https://YOUR_PROJECT_ID.supabase.co`
16    pub(crate) project_url: String,
17    /// WARN: The `service role` key has the ability to bypass Row Level Security. Never share it publicly.
18    pub(crate) api_key: String,
19    /// Used to decode your JWTs. You can also use this to mint your own JWTs.
20    pub(crate) jwt_secret: String,
21}
22
23#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
24pub struct Session {
25    /// The oauth provider token. If present, this can be used to make external API requests to the oauth provider used.
26    pub provider_token: Option<String>,
27    /// The oauth provider refresh token. If present, this can be used to refresh the provider_token via the oauth provider's API.
28    ///
29    /// Not all oauth providers return a provider refresh token. If the provider_refresh_token is missing, please refer to the oauth provider's documentation for information on how to obtain the provider refresh token.
30    pub provider_refresh_token: Option<String>,
31    /// The access token jwt. It is recommended to set the JWT_EXPIRY to a shorter expiry value.
32    pub access_token: String,
33    pub token_type: String,
34    /// The number of seconds until the token expires (since it was issued). Returned when a login is confirmed.
35    pub expires_in: i64,
36    /// A timestamp of when the token will expire. Returned when a login is confirmed.
37    pub expires_at: u64,
38    /// A one-time used refresh token that never expires.
39    pub refresh_token: String,
40    pub user: User,
41}
42
43/// User respresents a registered user
44#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
45pub struct User {
46    pub id: Uuid,
47    pub aud: String,
48    pub role: String,
49    pub email: String,
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub invited_at: Option<String>,
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub confirmation_sent_at: Option<String>,
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub email_confirmed_at: Option<String>,
56    pub phone: String,
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub phone_confirmed_at: Option<String>,
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub confirmed_at: Option<String>,
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub recovery_sent_at: Option<String>,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub last_sign_in_at: Option<String>,
65    pub app_metadata: AppMetadata,
66    pub user_metadata: UserMetadata,
67    pub identities: Vec<Identity>,
68    pub created_at: String,
69    pub updated_at: String,
70    pub is_anonymous: bool,
71}
72
73#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)]
74pub struct AppMetadata {
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub provider: Option<String>,
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub providers: Option<Vec<String>>,
79}
80
81#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)]
82pub struct UserMetadata {
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub name: Option<String>,
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub full_name: Option<String>,
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub email: Option<String>,
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub email_verified: Option<bool>,
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub phone_verified: Option<bool>,
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub picture: Option<String>,
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub avatar_url: Option<String>,
97    #[serde(flatten)]
98    pub custom: HashMap<String, Value>,
99}
100
101#[derive(Debug)]
102pub enum EmailSignUpResult {
103    SessionResult(Session),
104    ConfirmationResult(EmailSignUpConfirmation),
105}
106
107#[derive(Clone, Debug, Deserialize, PartialEq, Default)]
108pub struct EmailSignUpConfirmation {
109    pub id: Uuid,
110    pub aud: String,
111    pub role: String,
112    pub email: Option<String>,
113    pub phone: Option<String>,
114    pub confirmation_sent_at: String,
115    pub created_at: String,
116    pub updated_at: String,
117    pub is_anonymous: bool,
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
121pub struct IdTokenCredentials {
122    /// Provider name or OIDC `iss` value identifying which provider should be used to verify the provided token.
123    pub provider: Provider,
124    /// OIDC ID token issued by the specified provider. The iss claim in the ID token must match the supplied provider. Some ID tokens contain an at_hash which require that you provide an access_token value to be accepted properly. If the token contains a nonce claim you must supply the nonce used to obtain the ID token.
125    #[serde(rename = "id_token")]
126    pub token: String,
127    /// If the ID token contains an at_hash claim, then the hash of this value is compared to the value in the ID token.
128    pub access_token: Option<String>,
129    /// If the ID token contains a nonce claim, then the hash of this value is compared to the value in the ID token.
130    pub nonce: Option<String>,
131    /// Optional Object which may contain a captcha token
132    pub gotrue_meta_security: Option<GotrueMetaSecurity>,
133}
134
135#[derive(Debug, Deserialize, Serialize, PartialEq, Default)]
136pub struct LoginWithOAuthOptions {
137    pub query_params: Option<HashMap<String, String>>,
138    pub redirect_to: Option<String>,
139    pub scopes: Option<String>,
140    pub skip_browser_redirect: Option<bool>,
141}
142
143#[derive(Debug, PartialEq)]
144pub struct OAuthResponse {
145    pub url: Url,
146    pub provider: Provider,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
150pub struct GotrueMetaSecurity {
151    /// Verification token received when the user completes the captcha on the site.
152    captcha_token: Option<String>,
153}
154
155#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
156pub struct Identity {
157    pub identity_id: String,
158    pub id: String,
159    pub user_id: String,
160    pub identity_data: IdentityData,
161    pub provider: String,
162    pub last_sign_in_at: String,
163    pub created_at: String,
164    pub updated_at: String,
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub email: Option<String>,
167}
168
169#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
170pub struct IdentityData {
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub email: Option<String>,
173    pub email_verified: bool,
174    pub phone_verified: bool,
175    pub sub: String,
176}
177
178#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179pub enum LoginOptions {
180    Email(String),
181    Phone(String),
182}
183
184#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
185pub(crate) struct LoginWithEmailAndPasswordPayload<'a> {
186    pub(crate) email: &'a str,
187    pub(crate) password: &'a str,
188}
189
190#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
191pub(crate) struct LoginWithPhoneAndPasswordPayload<'a> {
192    pub(crate) phone: &'a str,
193    pub(crate) password: &'a str,
194}
195
196#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
197pub(crate) struct SignUpWithEmailAndPasswordPayload<'a> {
198    pub(crate) email: &'a str,
199    pub(crate) password: &'a str,
200    #[serde(flatten)]
201    #[serde(skip_serializing_if = "Option::is_none")]
202    pub(crate) options: Option<SignUpWithPasswordOptions>,
203}
204
205#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
206pub(crate) struct SignUpWithPhoneAndPasswordPayload<'a> {
207    pub(crate) phone: &'a str,
208    pub(crate) password: &'a str,
209    #[serde(flatten)]
210    #[serde(skip_serializing_if = "Option::is_none")]
211    pub(crate) options: Option<SignUpWithPasswordOptions>,
212}
213
214#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
215pub(crate) struct LoginAnonymouslyPayload {
216    #[serde(flatten)]
217    #[serde(skip_serializing_if = "Option::is_none")]
218    pub(crate) options: Option<LoginAnonymouslyOptions>,
219}
220
221#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
222pub struct SignUpWithPasswordOptions {
223    /// The redirect url embedded in the email link
224    #[serde(skip)]
225    pub email_redirect_to: Option<String>,
226    /// A custom data object to store the user's metadata. This maps to the `auth.users.raw_user_meta_data` column.
227    ///
228    /// The `data` should be a JSON object that includes user-specific info, such as their first and last name.
229    pub data: Option<Value>,
230    /// Verification token received when the user completes the captcha on the site.
231    pub captcha_token: Option<String>,
232}
233
234#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
235pub struct ResetPasswordOptions {
236    /// The redirect url embedded in the email link
237    #[serde(skip)]
238    pub email_redirect_to: Option<String>,
239
240    /// Verification token received when the user completes the captcha on the site.
241    pub captcha_token: Option<String>,
242}
243
244#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
245pub struct LoginAnonymouslyOptions {
246    /// The `data` should be a JSON object that includes user-specific info, such as their first and last name.
247    pub data: Option<Value>,
248    /// Verification token received when the user completes the captcha on the site.
249    pub captcha_token: Option<String>,
250}
251
252#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
253pub(crate) struct RequestMagicLinkPayload<'a> {
254    pub(crate) email: &'a str,
255}
256
257#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
258pub struct UpdatedUser {
259    pub email: Option<String>,
260    pub password: Option<String>,
261    pub data: Option<serde_json::Value>,
262}
263
264#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
265pub(crate) struct SendSMSOtpPayload<'a> {
266    pub phone: &'a str,
267}
268
269#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
270pub struct OTPResponse {
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub message_id: Option<String>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
276#[serde(untagged)]
277pub enum VerifyOtpParams {
278    Mobile(VerifyMobileOtpParams),
279    Email(VerifyEmailOtpParams),
280    TokenHash(VerifyTokenHashParams),
281}
282
283#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
284pub struct VerifyMobileOtpParams {
285    /// The user's phone number.
286    pub phone: String,
287    /// The otp sent to the user's phone number.
288    pub token: String,
289    /// The user's verification type.
290    #[serde(rename = "type")]
291    pub otp_type: OtpType,
292    /// Optional parameters
293    #[serde(skip_serializing_if = "Option::is_none")]
294    pub options: Option<VerifyOtpOptions>,
295}
296
297#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
298pub struct VerifyEmailOtpParams {
299    /// The user's email.
300    pub email: String,
301    /// The otp sent to the user's email.
302    pub token: String,
303    /// The user's verification type.
304    #[serde(rename = "type")]
305    pub otp_type: OtpType,
306    /// Optional parameters
307    #[serde(skip_serializing_if = "Option::is_none")]
308    pub options: Option<VerifyOtpOptions>,
309}
310
311#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
312pub struct VerifyTokenHashParams {
313    /// The user's phone number.
314    pub token_hash: String,
315    /// The user's verification type.
316    #[serde(rename = "type")]
317    pub otp_type: OtpType,
318}
319
320#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
321#[serde(rename_all = "snake_case")]
322pub enum OtpType {
323    #[default]
324    Signup,
325    EmailChange,
326    Sms,
327    Email,
328    PhoneChange,
329    Invite,
330    Magiclink,
331    Recovery,
332}
333
334#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
335pub struct VerifyOtpOptions {
336    /// A URL to send the user to after they are confirmed.
337    #[serde(skip_serializing_if = "Option::is_none")]
338    pub redirect_to: Option<String>,
339}
340
341#[derive(Debug, Serialize, Deserialize, PartialEq, Default)]
342pub(crate) struct LoginWithEmailOtpPayload<'a> {
343    pub email: &'a str,
344    #[serde(flatten)]
345    #[serde(skip_serializing_if = "Option::is_none")]
346    pub(crate) options: Option<LoginEmailOtpParams>,
347}
348
349// align json field's name with https://github.com/supabase/auth/blob/1f7de6c65f31ef0bbb80899369989b13ab5a517f/openapi.yaml#L559
350#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
351pub struct LoginEmailOtpParams {
352    /// Verification token received when the user completes the captcha on the site.
353    pub captcha_token: Option<String>,
354    /// A custom data object to store the user's metadata. This maps to the `auth.users.raw_user_meta_data` column.
355    pub data: Option<serde_json::Value>,
356    /// The redirect url embedded in the email link
357    pub email_redirect_to: Option<String>,
358    /// If set to false, this method will not create a new user. Defaults to true.
359    #[serde(rename = "create_user")]
360    pub should_create_user: Option<bool>,
361}
362
363// align json field's name with https://github.com/supabase/auth/blob/1f7de6c65f31ef0bbb80899369989b13ab5a517f/openapi.yaml#L559
364#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
365pub struct LoginMobileOtpParams {
366    /// Verification token received when the user completes the captcha on the site.
367    pub captcha_token: Option<String>,
368    /// A custom data object to store the user's metadata. This maps to the `auth.users.raw_user_meta_data` column.
369    pub data: Option<serde_json::Value>,
370    /// The redirect url embedded in the email link
371    pub channel: Option<Channel>,
372    /// If set to false, this method will not create a new user. Defaults to true.
373    #[serde(rename = "create_user")]
374    pub should_create_user: Option<bool>,
375}
376
377#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
378pub(crate) struct RefreshSessionPayload<'a> {
379    pub refresh_token: &'a str,
380}
381
382#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
383pub(crate) struct ExchangeCodeForSessionPayload<'a> {
384    pub auth_code: &'a str,
385    pub code_verifier: &'a str,
386}
387
388#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
389pub(crate) struct ResetPasswordForEmailPayload {
390    pub email: String,
391    #[serde(flatten)]
392    #[serde(skip_serializing_if = "Option::is_none")]
393    pub(crate) options: Option<ResetPasswordOptions>,
394}
395
396#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
397pub struct ResendParams {
398    #[serde(rename = "type")]
399    pub otp_type: OtpType,
400    pub email: String,
401    #[serde(flatten)]
402    #[serde(skip_serializing_if = "Option::is_none")]
403    pub options: Option<DesktopResendOptions>,
404}
405
406#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
407pub struct InviteParams {
408    pub email: String,
409    pub data: Option<Value>,
410}
411
412#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
413pub struct DesktopResendOptions {
414    pub email_redirect_to: Option<String>,
415    pub captcha_token: Option<String>,
416}
417
418#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
419pub struct MobileResendParams {
420    #[serde(rename = "type")]
421    pub otp_type: OtpType,
422    pub phone: String,
423    #[serde(flatten)]
424    #[serde(skip_serializing_if = "Option::is_none")]
425    pub options: Option<MobileResendOptions>,
426}
427
428#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
429pub struct MobileResendOptions {
430    captcha_token: Option<String>,
431}
432
433#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
434#[serde(rename_all = "snake_case")]
435pub enum Channel {
436    #[default]
437    Sms,
438    Whatsapp,
439}
440
441impl Display for Channel {
442    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
443        match *self {
444            Channel::Sms => write!(f, "sms"),
445            Channel::Whatsapp => write!(f, "whatsapp"),
446        }
447    }
448}
449
450/// Health status of the Auth Server
451#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
452pub struct AuthServerHealth {
453    /// Version of the service
454    pub version: String,
455    /// Name of the service
456    pub name: String,
457    /// Description of the service
458    pub description: String,
459}
460
461/// Settings of the Auth Server
462#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
463pub struct AuthServerSettings {
464    pub external: External,
465    pub disable_signup: bool,
466    pub mailer_autoconfirm: bool,
467    pub phone_autoconfirm: bool,
468    pub sms_provider: String,
469    pub saml_enabled: bool,
470}
471
472#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
473pub struct External {
474    pub anonymous_users: bool,
475    pub apple: bool,
476    pub azure: bool,
477    pub bitbucket: bool,
478    pub discord: bool,
479    pub facebook: bool,
480    pub figma: bool,
481    pub fly: bool,
482    pub github: bool,
483    pub gitlab: bool,
484    pub google: bool,
485    pub keycloak: bool,
486    pub kakao: bool,
487    pub linkedin: bool,
488    pub linkedin_oidc: bool,
489    pub notion: bool,
490    pub spotify: bool,
491    pub slack: bool,
492    pub slack_oidc: bool,
493    pub workos: bool,
494    pub twitch: bool,
495    pub twitter: bool,
496    pub email: bool,
497    pub phone: bool,
498    pub zoom: bool,
499}
500
501#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
502#[serde(rename_all = "snake_case")]
503/// Currently enabled OAuth providers.
504///
505/// # Example
506/// ```
507/// let provider = Provider::Github.to_string();
508/// println!("{provider}") // "github"
509/// ```
510pub enum Provider {
511    Apple,
512    Azure,
513    Bitbucket,
514    Discord,
515    Facebook,
516    Figma,
517    Fly,
518    Github,
519    Gitlab,
520    Google,
521    Kakao,
522    Keycloak,
523    Linkedin,
524    LinkedinOidc,
525    Notion,
526    Slack,
527    SlackOidc,
528    Spotify,
529    Twitch,
530    Twitter,
531    Workos,
532    Zoom,
533}
534
535impl Display for Provider {
536    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
537        match *self {
538            Provider::Apple => write!(f, "apple"),
539            Provider::Azure => write!(f, "azure"),
540            Provider::Bitbucket => write!(f, "bitbucket"),
541            Provider::Discord => write!(f, "discord"),
542            Provider::Facebook => write!(f, "facebook"),
543            Provider::Figma => write!(f, "figma"),
544            Provider::Fly => write!(f, "fly"),
545            Provider::Github => write!(f, "github"),
546            Provider::Gitlab => write!(f, "gitlab"),
547            Provider::Google => write!(f, "google"),
548            Provider::Kakao => write!(f, "kakao"),
549            Provider::Keycloak => write!(f, "keycloak"),
550            Provider::Linkedin => write!(f, "linkedin"),
551            Provider::LinkedinOidc => write!(f, "linkedin_oidc"),
552            Provider::Notion => write!(f, "notion"),
553            Provider::Slack => write!(f, "slack"),
554            Provider::SlackOidc => write!(f, "slack_oidc"),
555            Provider::Spotify => write!(f, "spotify"),
556            Provider::Twitch => write!(f, "twitch"),
557            Provider::Twitter => write!(f, "twitter"),
558            Provider::Workos => write!(f, "workos"),
559            Provider::Zoom => write!(f, "zoom"),
560        }
561    }
562}
563
564/// Represents the scope of the logout operation
565#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
566#[serde(rename_all = "snake_case")]
567pub enum LogoutScope {
568    #[default]
569    Global,
570    Local,
571    Others,
572}
573
574#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
575pub struct LoginWithSSO {
576    #[serde(skip_serializing_if = "Option::is_none")]
577    /// UUID of the SSO provider to invoke single-sign on to
578    pub provider_id: Option<String>,
579    #[serde(skip_serializing_if = "Option::is_none")]
580    /// Domain of the SSO provider where users can initiate sign on
581    pub domain: Option<String>,
582    #[serde(skip_serializing_if = "Option::is_none")]
583    pub options: Option<SSOLoginOptions>,
584}
585
586#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
587pub struct SSOLoginOptions {
588    #[serde(skip_serializing_if = "Option::is_none")]
589    /// Verification token received when the user completes the captcha on the site.
590    captcha_token: Option<String>,
591    #[serde(skip_serializing_if = "Option::is_none")]
592    /// A URL to send the user to after they have signed-in.
593    redirect_to: Option<String>,
594}
595
596#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
597pub struct SSOSuccess {
598    /// URL to open in a browser which will complete the sign-in flow by
599    /// taking the user to the identity provider's authentication flow.
600    ///
601    /// On browsers you can set the URL to `window.location.href` to take
602    /// the user to the authentication flow.
603    pub url: String,
604    pub status: u16,
605    pub headers: Headers,
606}
607
608#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
609pub struct Headers {
610    pub date: String,
611    #[serde(rename = "content-type")]
612    pub content_type: String,
613    #[serde(rename = "transfer-encoding")]
614    pub transfer_encoding: String,
615    pub connection: String,
616    pub server: String,
617    pub vary: String,
618    #[serde(rename = "x-okta-request-id")]
619    pub x_okta_request_id: String,
620    #[serde(rename = "x-xss-protection")]
621    pub x_xss_protection: String,
622    pub p3p: String,
623    #[serde(rename = "set-cookie")]
624    pub set_cookie: Vec<String>,
625    #[serde(rename = "content-security-policy-report-only")]
626    pub content_security_policy_report_only: String,
627    #[serde(rename = "content-security-policy")]
628    pub content_security_policy: String,
629    #[serde(rename = "x-rate-limit-limit")]
630    pub x_rate_limit_limit: String,
631    #[serde(rename = "x-rate-limit-remaining")]
632    pub x_rate_limit_remaining: String,
633    #[serde(rename = "x-rate-limit-reset")]
634    pub x_rate_limit_reset: String,
635    #[serde(rename = "referrer-policy")]
636    pub referrer_policy: String,
637    #[serde(rename = "accept-ch")]
638    pub accept_ch: String,
639    #[serde(rename = "cache-control")]
640    pub cache_control: String,
641    pub pragma: String,
642    pub expires: String,
643    #[serde(rename = "x-frame-options")]
644    pub x_frame_options: String,
645    #[serde(rename = "x-content-type-options")]
646    pub x_content_type_options: String,
647    #[serde(rename = "x-ua-compatible")]
648    pub x_ua_compatible: String,
649    #[serde(rename = "content-language")]
650    pub content_language: String,
651    #[serde(rename = "strict-transport-security")]
652    pub strict_transport_security: String,
653    #[serde(rename = "x-robots-tag")]
654    pub x_robots_tag: String,
655}
656
657// Implement custom Debug to avoid exposing sensitive information
658impl fmt::Debug for AuthClient {
659    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
660        f.debug_struct("AuthClient")
661            .field("project_url", &self.project_url())
662            .field("api_key", &"[REDACTED]")
663            .field("jwt_secret", &"[REDACTED]")
664            .finish()
665    }
666}
667
668pub const AUTH_V1: &str = "/auth/v1";