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#[derive(Clone)]
12pub struct AuthClient {
13 pub(crate) client: Client,
14 pub(crate) project_url: String,
17 pub(crate) api_key: String,
19 pub(crate) jwt_secret: String,
21}
22
23#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
24pub struct Session {
25 pub provider_token: Option<String>,
27 pub provider_refresh_token: Option<String>,
31 pub access_token: String,
33 pub token_type: String,
34 pub expires_in: i64,
36 pub expires_at: u64,
38 pub refresh_token: String,
40 pub user: User,
41}
42
43#[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 pub provider: Provider,
124 #[serde(rename = "id_token")]
126 pub token: String,
127 pub access_token: Option<String>,
129 pub nonce: Option<String>,
131 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 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 #[serde(skip)]
225 pub email_redirect_to: Option<String>,
226 pub data: Option<Value>,
230 pub captcha_token: Option<String>,
232}
233
234#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
235pub struct ResetPasswordOptions {
236 #[serde(skip)]
238 pub email_redirect_to: Option<String>,
239
240 pub captcha_token: Option<String>,
242}
243
244#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
245pub struct LoginAnonymouslyOptions {
246 pub data: Option<Value>,
248 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 pub phone: String,
287 pub token: String,
289 #[serde(rename = "type")]
291 pub otp_type: OtpType,
292 #[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 pub email: String,
301 pub token: String,
303 #[serde(rename = "type")]
305 pub otp_type: OtpType,
306 #[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 pub token_hash: String,
315 #[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 #[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#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
351pub struct LoginEmailOtpParams {
352 pub captcha_token: Option<String>,
354 pub data: Option<serde_json::Value>,
356 pub email_redirect_to: Option<String>,
358 #[serde(rename = "create_user")]
360 pub should_create_user: Option<bool>,
361}
362
363#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
365pub struct LoginMobileOtpParams {
366 pub captcha_token: Option<String>,
368 pub data: Option<serde_json::Value>,
370 pub channel: Option<Channel>,
372 #[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#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
452pub struct AuthServerHealth {
453 pub version: String,
455 pub name: String,
457 pub description: String,
459}
460
461#[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")]
503pub 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#[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 pub provider_id: Option<String>,
579 #[serde(skip_serializing_if = "Option::is_none")]
580 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 captcha_token: Option<String>,
591 #[serde(skip_serializing_if = "Option::is_none")]
592 redirect_to: Option<String>,
594}
595
596#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
597pub struct SSOSuccess {
598 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
657impl 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";