1use std::env;
17
18use reqwest::{
19 header::{self, HeaderMap, HeaderValue, AUTHORIZATION, CONTENT_TYPE},
20 Client, Url,
21};
22use serde_json::{from_str, Value};
23
24use crate::{
25 error::{
26 Error::{self, AuthError},
27 SupabaseHTTPError,
28 },
29 models::{
30 AuthClient, AuthServerHealth, AuthServerSettings, EmailSignUpConfirmation,
31 EmailSignUpResult, IdTokenCredentials, InviteParams, LoginAnonymouslyOptions,
32 LoginAnonymouslyPayload, LoginEmailOtpParams, LoginWithEmailAndPasswordPayload,
33 LoginWithEmailOtpPayload, LoginWithOAuthOptions, LoginWithPhoneAndPasswordPayload,
34 LoginWithSSO, LogoutScope, OAuthResponse, OTPResponse, Provider, RefreshSessionPayload,
35 RequestMagicLinkPayload, ResendParams, ResetPasswordForEmailPayload, ResetPasswordOptions,
36 SendSMSOtpPayload, Session, SignUpWithEmailAndPasswordPayload, SignUpWithPasswordOptions,
37 SignUpWithPhoneAndPasswordPayload, UpdatedUser, User, VerifyOtpParams, AUTH_V1,
38 },
39};
40
41impl AuthClient {
42 pub fn new(
49 project_url: impl Into<String>,
50 api_key: impl Into<String>,
51 jwt_secret: impl Into<String>,
52 ) -> Self {
53 AuthClient {
54 client: Client::new(),
55 project_url: project_url.into(),
56 api_key: api_key.into(),
57 jwt_secret: jwt_secret.into(),
58 }
59 }
60
61 pub fn new_from_env() -> Result<AuthClient, Error> {
70 let project_url = env::var("SUPABASE_URL")?;
71 let api_key = env::var("SUPABASE_API_KEY")?;
72 let jwt_secret = env::var("SUPABASE_JWT_SECRET")?;
73
74 Ok(AuthClient {
75 client: Client::new(),
76 project_url,
77 api_key,
78 jwt_secret,
79 })
80 }
81
82 pub async fn login_with_email(&self, email: &str, password: &str) -> Result<Session, Error> {
93 let payload = LoginWithEmailAndPasswordPayload { email, password };
94
95 let mut headers = header::HeaderMap::new();
96 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
97 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
98 let body = serde_json::to_string(&payload)?;
99
100 let response = self
101 .client
102 .post(format!(
103 "{}{}/token?grant_type=password",
104 self.project_url, AUTH_V1
105 ))
106 .headers(headers)
107 .body(body)
108 .send()
109 .await?;
110
111 let res_status = response.status();
112 let res_body = response.text().await?;
113
114 if let Ok(session) = from_str(&res_body) {
115 return Ok(session);
116 }
117
118 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
119 return Err(Error::AuthError {
120 status: res_status,
121 message: error.message,
122 });
123 }
124
125 Err(Error::AuthError {
127 status: res_status,
128 message: res_body,
129 })
130 }
131
132 pub async fn login_with_phone(&self, phone: &str, password: &str) -> Result<Session, Error> {
143 let payload = LoginWithPhoneAndPasswordPayload { phone, password };
144
145 let mut headers = header::HeaderMap::new();
146 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
147 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
148
149 let body = serde_json::to_string(&payload)?;
150
151 let response = self
152 .client
153 .post(format!(
154 "{}{}/token?grant_type=password",
155 self.project_url, AUTH_V1
156 ))
157 .headers(headers)
158 .body(body)
159 .send()
160 .await?;
161
162 let res_status = response.status();
163 let res_body = response.text().await?;
164
165 if let Ok(session) = from_str(&res_body) {
166 return Ok(session);
167 }
168
169 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
170 return Err(Error::AuthError {
171 status: res_status,
172 message: error.message,
173 });
174 }
175
176 Err(Error::AuthError {
178 status: res_status,
179 message: res_body,
180 })
181 }
182
183 pub async fn sign_up_with_email_and_password(
194 &self,
195 email: &str,
196 password: &str,
197 options: Option<SignUpWithPasswordOptions>,
198 ) -> Result<EmailSignUpResult, Error> {
199 let redirect_to = options
200 .as_ref()
201 .and_then(|o| o.email_redirect_to.as_deref().map(str::to_owned));
202
203 let payload = SignUpWithEmailAndPasswordPayload {
204 email,
205 password,
206 options,
207 };
208
209 let mut headers = header::HeaderMap::new();
210 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
211 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
212
213 let body = serde_json::to_string(&payload)?;
214
215 let response = self
216 .client
217 .post(format!("{}{}/signup", self.project_url, AUTH_V1))
218 .query(&[("redirect_to", redirect_to.as_deref())])
219 .headers(headers)
220 .body(body)
221 .send()
222 .await?;
223
224 let res_status = response.status();
225 let res_body = response.text().await?;
226
227 if let Ok(session) = from_str::<Session>(&res_body) {
228 return Ok(EmailSignUpResult::SessionResult(session));
229 }
230
231 if let Ok(result) = from_str::<EmailSignUpConfirmation>(&res_body) {
232 return Ok(EmailSignUpResult::ConfirmationResult(result));
233 }
234
235 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
236 return Err(Error::AuthError {
237 status: res_status,
238 message: error.message,
239 });
240 }
241
242 Err(Error::AuthError {
244 status: res_status,
245 message: res_body,
246 })
247 }
248
249 pub async fn sign_up_with_phone_and_password(
260 &self,
261 phone: &str,
262 password: &str,
263 options: Option<SignUpWithPasswordOptions>,
264 ) -> Result<Session, Error> {
265 let redirect_to = options
266 .as_ref()
267 .and_then(|o| o.email_redirect_to.as_deref().map(str::to_owned));
268
269 let payload = SignUpWithPhoneAndPasswordPayload {
270 phone,
271 password,
272 options,
273 };
274
275 let mut headers = header::HeaderMap::new();
276 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
277 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
278
279 let body = serde_json::to_string(&payload)?;
280
281 let response = self
282 .client
283 .post(format!("{}{}/signup", self.project_url, AUTH_V1))
284 .query(&[("email_redirect_to", redirect_to.as_deref())])
285 .headers(headers)
286 .body(body)
287 .send()
288 .await?;
289
290 let res_status = response.status();
291 let res_body = response.text().await?;
292
293 if let Ok(session) = from_str(&res_body) {
294 return Ok(session);
295 }
296
297 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
298 return Err(Error::AuthError {
299 status: res_status,
300 message: error.message,
301 });
302 }
303
304 Err(Error::AuthError {
306 status: res_status,
307 message: res_body,
308 })
309 }
310
311 pub async fn login_anonymously(
328 &self,
329 options: Option<LoginAnonymouslyOptions>,
330 ) -> Result<Session, Error> {
331 let payload = LoginAnonymouslyPayload { options };
332
333 let mut headers = header::HeaderMap::new();
334 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
335 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
336
337 let body = serde_json::to_string(&payload)?;
338
339 let response = self
340 .client
341 .post(format!("{}{}/signup", self.project_url, AUTH_V1))
342 .headers(headers)
343 .body(body)
344 .send()
345 .await?;
346
347 let res_status = response.status();
348 let res_body = response.text().await?;
349
350 if let Ok(session) = from_str(&res_body) {
351 return Ok(session);
352 }
353
354 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
355 return Err(Error::AuthError {
356 status: res_status,
357 message: error.message,
358 });
359 }
360
361 Err(Error::AuthError {
363 status: res_status,
364 message: res_body,
365 })
366 }
367
368 pub async fn send_login_email_with_magic_link(&self, email: &str) -> Result<(), Error> {
377 let payload = RequestMagicLinkPayload { email };
378
379 let mut headers = header::HeaderMap::new();
380 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
381 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
382
383 let body = serde_json::to_string(&payload)?;
384
385 let response = self
386 .client
387 .post(format!("{}{}/magiclink", self.project_url, AUTH_V1))
388 .headers(headers)
389 .body(body)
390 .send()
391 .await?;
392
393 let res_status = response.status();
394 let res_body = response.text().await?;
395
396 if res_status.is_success() {
397 Ok(())
398 } else {
399 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
400 return Err(AuthError {
401 status: res_status,
402 message: error.message,
403 });
404 }
405
406 return Err(AuthError {
408 status: res_status,
409 message: res_body,
410 });
411 }
412 }
413
414 pub async fn send_sms_with_otp(&self, phone: &str) -> Result<OTPResponse, Error> {
421 let payload = SendSMSOtpPayload { phone };
422
423 let mut headers = header::HeaderMap::new();
424 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
425 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
426
427 let body = serde_json::to_string(&payload)?;
428
429 let response = self
430 .client
431 .post(format!("{}{}/otp", self.project_url, AUTH_V1))
432 .headers(headers)
433 .body(body)
434 .send()
435 .await?;
436
437 let res_status = response.status();
438 let res_body = response.text().await?;
439
440 if res_status.is_success() {
441 let message = serde_json::from_str(&res_body)?;
442 Ok(message)
443 } else {
444 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
445 return Err(AuthError {
446 status: res_status,
447 message: error.message,
448 });
449 }
450
451 return Err(AuthError {
453 status: res_status,
454 message: res_body,
455 });
456 }
457 }
458
459 pub async fn send_email_with_otp(
467 &self,
468 email: &str,
469 options: Option<LoginEmailOtpParams>,
470 ) -> Result<OTPResponse, Error> {
471 let payload = LoginWithEmailOtpPayload { email, options };
472
473 let mut headers = header::HeaderMap::new();
474 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
475 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
476
477 let body = serde_json::to_string(&payload)?;
478
479 let response = self
480 .client
481 .post(format!("{}{}/otp", self.project_url, AUTH_V1))
482 .headers(headers)
483 .body(body)
484 .send()
485 .await?;
486
487 let res_status = response.status();
488 let res_body = response.text().await?;
489
490 if res_status.is_success() {
491 let message = serde_json::from_str(&res_body)?;
492 Ok(message)
493 } else {
494 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
495 return Err(AuthError {
496 status: res_status,
497 message: error.message,
498 });
499 }
500
501 return Err(AuthError {
503 status: res_status,
504 message: res_body,
505 });
506 }
507 }
508
509 pub fn login_with_oauth(
528 &self,
529 provider: Provider,
530 options: Option<LoginWithOAuthOptions>,
531 ) -> Result<OAuthResponse, Error> {
532 let query_params = options.as_ref().map_or_else(
533 || vec![("provider", provider.to_string())],
534 |o| {
535 let mut params = vec![("provider", provider.to_string())];
536
537 if let Some(ref redirect) = o.redirect_to {
538 params.push(("redirect_to", redirect.to_string()));
539 }
540
541 if let Some(ref extra) = o.query_params {
542 params.extend(extra.iter().map(|(k, v)| (k.as_str(), v.to_string())));
543 }
544
545 params
546 },
547 );
548
549 let url = Url::parse_with_params(
550 format!("{}{}/authorize", self.project_url, AUTH_V1).as_str(),
551 query_params,
552 )
553 .map_err(|_| Error::ParseUrlError)?;
554
555 Ok(OAuthResponse { url, provider })
556 }
557
558 pub fn sign_up_with_oauth(
577 &self,
578 provider: Provider,
579 options: Option<LoginWithOAuthOptions>,
580 ) -> Result<OAuthResponse, Error> {
581 self.login_with_oauth(provider, options)
582 }
583
584 pub async fn get_user(&self, bearer_token: &str) -> Result<User, Error> {
595 let mut headers = header::HeaderMap::new();
596 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
597 headers.insert(
598 AUTHORIZATION,
599 HeaderValue::from_str(&format!("Bearer {}", bearer_token))?,
600 );
601
602 let response = self
603 .client
604 .get(format!("{}{}/user", self.project_url, AUTH_V1))
605 .headers(headers)
606 .send()
607 .await?;
608
609 let res_status = response.status();
610 let res_body = response.text().await?;
611
612 if let Ok(user) = from_str(&res_body) {
613 return Ok(user);
614 }
615
616 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
617 return Err(Error::AuthError {
618 status: res_status,
619 message: error.message,
620 });
621 }
622
623 Err(Error::AuthError {
625 status: res_status,
626 message: res_body,
627 })
628 }
629
630 pub async fn update_user(
645 &self,
646 updated_user: UpdatedUser,
647 bearer_token: &str,
648 ) -> Result<User, Error> {
649 let mut headers = header::HeaderMap::new();
650 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
651 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
652 headers.insert(
653 AUTHORIZATION,
654 HeaderValue::from_str(&format!("Bearer {}", bearer_token))?,
655 );
656
657 let body = serde_json::to_string::<UpdatedUser>(&updated_user)?;
658
659 let response = self
660 .client
661 .put(format!("{}{}/user", self.project_url, AUTH_V1))
662 .headers(headers)
663 .body(body)
664 .send()
665 .await?;
666
667 let res_status = response.status();
668 let res_body = response.text().await?;
669
670 if let Ok(user) = from_str(&res_body) {
671 return Ok(user);
672 }
673
674 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
675 return Err(Error::AuthError {
676 status: res_status,
677 message: error.message,
678 });
679 }
680
681 Err(Error::AuthError {
683 status: res_status,
684 message: res_body,
685 })
686 }
687
688 pub async fn login_with_id_token(
702 &self,
703 credentials: IdTokenCredentials,
704 ) -> Result<Session, Error> {
705 let mut headers = HeaderMap::new();
706 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
707 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
708
709 let body = serde_json::to_string(&credentials)?;
710
711 let response = self
712 .client
713 .post(format!(
714 "{}{}/token?grant_type=id_token",
715 self.project_url, AUTH_V1
716 ))
717 .headers(headers)
718 .body(body)
719 .send()
720 .await?;
721
722 let res_status = response.status();
723 let res_body = response.text().await?;
724
725 if let Ok(session) = from_str(&res_body) {
726 return Ok(session);
727 }
728
729 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
730 return Err(Error::AuthError {
731 status: res_status,
732 message: error.message,
733 });
734 }
735
736 Err(Error::AuthError {
738 status: res_status,
739 message: res_body,
740 })
741 }
742
743 pub async fn invite_user_by_email(
757 &self,
758 email: &str,
759 data: Option<Value>,
760 bearer_token: &str,
761 ) -> Result<User, Error> {
762 let mut headers = HeaderMap::new();
763 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
764 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
765 headers.insert(
766 AUTHORIZATION,
767 HeaderValue::from_str(&format!("Bearer {}", bearer_token))?,
768 );
769
770 let invite_payload = InviteParams {
771 email: email.into(),
772 data,
773 };
774
775 let body = serde_json::to_string(&invite_payload)?;
776
777 let response = self
778 .client
779 .post(format!("{}{}/invite", self.project_url, AUTH_V1))
780 .headers(headers)
781 .body(body)
782 .send()
783 .await?;
784
785 let res_status = response.status();
786 let res_body = response.text().await?;
787
788 if let Ok(user) = from_str(&res_body) {
789 return Ok(user);
790 }
791
792 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
793 return Err(Error::AuthError {
794 status: res_status,
795 message: error.message,
796 });
797 }
798
799 Err(Error::AuthError {
801 status: res_status,
802 message: res_body,
803 })
804 }
805
806 pub async fn verify_otp(&self, params: VerifyOtpParams) -> Result<Session, Error> {
821 let mut headers = HeaderMap::new();
822 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
823 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
824
825 let body = serde_json::to_string(¶ms)?;
826
827 let response = self
828 .client
829 .post(&format!("{}{}/verify", self.project_url, AUTH_V1))
830 .headers(headers)
831 .body(body)
832 .send()
833 .await?;
834
835 let res_status = response.status();
836 let res_body = response.text().await?;
837
838 if let Ok(session) = from_str(&res_body) {
839 return Ok(session);
840 }
841
842 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
843 return Err(Error::AuthError {
844 status: res_status,
845 message: error.message,
846 });
847 }
848
849 Err(Error::AuthError {
851 status: res_status,
852 message: res_body,
853 })
854 }
855
856 pub async fn get_health(&self) -> Result<AuthServerHealth, Error> {
865 let mut headers = HeaderMap::new();
866 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
867
868 let response = self
869 .client
870 .get(&format!("{}{}/health", self.project_url, AUTH_V1))
871 .headers(headers)
872 .send()
873 .await?;
874
875 let res_status = response.status();
876 let res_body = response.text().await?;
877
878 if let Ok(health) = from_str::<AuthServerHealth>(&res_body) {
879 return Ok(health);
880 }
881
882 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
883 return Err(Error::AuthError {
884 status: res_status,
885 message: error.message,
886 });
887 }
888
889 Err(Error::AuthError {
891 status: res_status,
892 message: res_body,
893 })
894 }
895
896 pub async fn get_settings(&self) -> Result<AuthServerSettings, Error> {
905 let mut headers = HeaderMap::new();
906 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
907
908 let response = self
909 .client
910 .get(&format!("{}{}/settings", self.project_url, AUTH_V1))
911 .headers(headers)
912 .send()
913 .await?;
914
915 let res_status = response.status();
916 let res_body = response.text().await?;
917
918 if let Ok(settings) = from_str(&res_body) {
919 return Ok(settings);
920 }
921
922 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
923 return Err(Error::AuthError {
924 status: res_status,
925 message: error.message,
926 });
927 }
928
929 Err(Error::AuthError {
931 status: res_status,
932 message: res_body,
933 })
934 }
935
936 pub async fn exchange_token_for_session(&self, refresh_token: &str) -> Result<Session, Error> {
952 let mut headers = HeaderMap::new();
953 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
954 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
955
956 let body = serde_json::to_string(&RefreshSessionPayload { refresh_token })?;
957
958 let response = self
959 .client
960 .post(&format!(
961 "{}{}/token?grant_type=refresh_token",
962 self.project_url, AUTH_V1
963 ))
964 .headers(headers)
965 .body(body)
966 .send()
967 .await?;
968
969 let res_status = response.status();
970 let res_body = response.text().await?;
971
972 if let Ok(session) = from_str(&res_body) {
973 return Ok(session);
974 }
975
976 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
977 return Err(Error::AuthError {
978 status: res_status,
979 message: error.message,
980 });
981 }
982
983 Err(Error::AuthError {
985 status: res_status,
986 message: res_body,
987 })
988 }
989
990 pub async fn refresh_session(&self, refresh_token: &str) -> Result<Session, Error> {
991 self.exchange_token_for_session(refresh_token).await
992 }
993
994 pub async fn reset_password_for_email(
1001 &self,
1002 email: &str,
1003 options: Option<ResetPasswordOptions>,
1004 ) -> Result<(), Error> {
1005 let redirect_to = options
1006 .as_ref()
1007 .and_then(|o| o.email_redirect_to.as_deref().map(str::to_owned));
1008
1009 let payload = ResetPasswordForEmailPayload {
1010 email: String::from(email),
1011 options,
1012 };
1013
1014 let mut headers = HeaderMap::new();
1015 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
1016 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
1017
1018 let body = serde_json::to_string(&payload)?;
1019
1020 let response = self
1021 .client
1022 .post(&format!("{}{}/recover", self.project_url, AUTH_V1))
1023 .query(&[("redirect_to", redirect_to.as_deref())])
1024 .headers(headers)
1025 .body(body)
1026 .send()
1027 .await?;
1028
1029 let res_status = response.status();
1030 let res_body = response.text().await?;
1031
1032 if res_status.is_success() {
1033 return Ok(());
1034 }
1035
1036 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
1037 return Err(Error::AuthError {
1038 status: res_status,
1039 message: error.message,
1040 });
1041 }
1042
1043 Err(Error::AuthError {
1044 status: res_status,
1045 message: res_body,
1046 })
1047 }
1048
1049 pub async fn resend(&self, credentials: ResendParams) -> Result<(), Error> {
1062 let mut headers = HeaderMap::new();
1063 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
1064 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
1065
1066 let body = serde_json::to_string(&credentials)?;
1067
1068 let response = self
1069 .client
1070 .post(&format!("{}{}/resend", self.project_url, AUTH_V1))
1071 .headers(headers)
1072 .body(body)
1073 .send()
1074 .await?;
1075
1076 let res_status = response.status();
1077 let res_body = response.text().await?;
1078
1079 if res_status.is_success() {
1080 return Ok(());
1081 }
1082
1083 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
1084 return Err(Error::AuthError {
1085 status: res_status,
1086 message: error.message,
1087 });
1088 }
1089
1090 Err(Error::AuthError {
1091 status: res_status,
1092 message: res_body,
1093 })
1094 }
1095
1096 pub async fn logout(
1102 &self,
1103 scope: Option<LogoutScope>,
1104 bearer_token: &str,
1105 ) -> Result<(), Error> {
1106 let mut headers = HeaderMap::new();
1107 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
1108 headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?);
1109 headers.insert(
1110 AUTHORIZATION,
1111 HeaderValue::from_str(&format!("Bearer {}", bearer_token))?,
1112 );
1113
1114 let body = serde_json::to_string(&scope)?;
1115
1116 let response = self
1117 .client
1118 .post(&format!("{}{}/logout", self.project_url, AUTH_V1))
1119 .headers(headers)
1120 .body(body)
1121 .send()
1122 .await?;
1123
1124 let res_status = response.status();
1125 let res_body = response.text().await?;
1126
1127 if res_status.is_success() {
1128 return Ok(());
1129 }
1130
1131 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
1132 return Err(Error::AuthError {
1133 status: res_status,
1134 message: error.message,
1135 });
1136 }
1137
1138 Err(Error::AuthError {
1139 status: res_status,
1140 message: res_body,
1141 })
1142 }
1143
1144 pub async fn sso(&self, params: LoginWithSSO) -> Result<Url, Error> {
1156 let mut headers = HeaderMap::new();
1157 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
1158 headers.insert("apikey", HeaderValue::from_str(&self.api_key)?);
1159
1160 let body = serde_json::to_string::<crate::models::LoginWithSSO>(¶ms)?;
1161
1162 let response = self
1163 .client
1164 .post(&format!("{}{}/sso", self.project_url, AUTH_V1))
1165 .headers(headers)
1166 .body(body)
1167 .send()
1168 .await?;
1169
1170 let res_status = response.status();
1171 let url = response.url().clone();
1172 let res_body = response.text().await?;
1173
1174 if res_status.is_server_error() || res_status.is_client_error() {
1175 if let Ok(error) = from_str::<SupabaseHTTPError>(&res_body) {
1176 return Err(AuthError {
1177 status: res_status,
1178 message: error.message,
1179 });
1180 }
1181
1182 return Err(AuthError {
1184 status: res_status,
1185 message: res_body,
1186 });
1187 }
1188
1189 Ok(url)
1190 }
1191
1192 pub fn project_url(&self) -> &str {
1194 &self.project_url
1195 }
1196
1197 pub fn api_key(&self) -> &str {
1199 &self.api_key
1200 }
1201
1202 pub fn jwt_secret(&self) -> &str {
1204 &self.jwt_secret
1205 }
1206}