diff --git a/src/client.rs b/src/client.rs index 7e4f164..d22ab78 100644 --- a/src/client.rs +++ b/src/client.rs @@ -32,7 +32,7 @@ use crate::{ LoginWithEmailAndPasswordPayload, LoginWithEmailOtpPayload, LoginWithOAuthOptions, LoginWithPhoneAndPasswordPayload, LoginWithSSO, LogoutScope, OAuthResponse, OTPResponse, Provider, RefreshSessionPayload, RequestMagicLinkPayload, ResendParams, - ResetPasswordForEmailPayload, SendSMSOtpPayload, Session, + ResetPasswordForEmailPayload, ResetPasswordOptions, SendSMSOtpPayload, Session, SignUpWithEmailAndPasswordPayload, SignUpWithPasswordOptions, SignUpWithPhoneAndPasswordPayload, UpdatedUser, User, VerifyOtpParams, AUTH_V1, }, @@ -1021,20 +1021,32 @@ impl AuthClient { /// Valid email addresses that are not registered as users will not return an error. /// # Example /// ``` - /// let response = auth_client.reset_password_for_email(demo_email).await.unwrap(); + /// let response = auth_client.reset_password_for_email(demo_email, None).await.unwrap(); /// ``` - pub async fn reset_password_for_email(&self, email: &str) -> Result<(), Error> { + pub async fn reset_password_for_email( + &self, + email: &str, + options: Option, + ) -> Result<(), Error> { + let redirect_to = options + .as_ref() + .and_then(|o| o.email_redirect_to.as_deref().map(str::to_owned)); + + let payload = ResetPasswordForEmailPayload { + email: String::from(email), + options, + }; + let mut headers = HeaderMap::new(); headers.insert("apikey", HeaderValue::from_str(&self.api_key)?); headers.insert(CONTENT_TYPE, HeaderValue::from_str("application/json")?); - let body = serde_json::to_string(&ResetPasswordForEmailPayload { - email: email.into(), - })?; + let body = serde_json::to_string(&payload)?; let response = self .client .post(&format!("{}{}/recover", self.project_url, AUTH_V1)) + .query(&[("redirect_to", redirect_to.as_deref())]) .headers(headers) .body(body) .send() diff --git a/src/models.rs b/src/models.rs index e90f265..5d91930 100644 --- a/src/models.rs +++ b/src/models.rs @@ -210,6 +210,16 @@ pub struct SignUpWithPasswordOptions { pub captcha_token: Option, } +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ResetPasswordOptions { + /// The redirect url embedded in the email link + #[serde(skip)] + pub email_redirect_to: Option, + + /// Verification token received when the user completes the captcha on the site. + pub captcha_token: Option, +} + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct LoginAnonymouslyOptions { /// The `data` should be a JSON object that includes user-specific info, such as their first and last name. @@ -341,6 +351,9 @@ pub(crate) struct RefreshSessionPayload<'a> { #[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] pub(crate) struct ResetPasswordForEmailPayload { pub email: String, + #[serde(flatten)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) options: Option, } #[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] diff --git a/tests/client_tests.rs b/tests/client_tests.rs index e4510b4..f4b154a 100644 --- a/tests/client_tests.rs +++ b/tests/client_tests.rs @@ -5,7 +5,7 @@ use supabase_auth::{ error::Error, models::{ AuthClient, LoginEmailOtpParams, LoginWithOAuthOptions, LoginWithSSO, LogoutScope, - ResendParams, SignUpWithPasswordOptions, UpdatedUser, + ResendParams, ResetPasswordOptions, SignUpWithPasswordOptions, UpdatedUser, }, }; @@ -357,7 +357,14 @@ async fn reset_password_for_email_test() { let demo_email = env::var("DEMO_EMAIL").unwrap(); - let response = auth_client.reset_password_for_email(&demo_email).await; + let options = ResetPasswordOptions { + email_redirect_to: Some("https://www.thisisnotarealdomain.com".to_string()), + ..Default::default() + }; + + let response = auth_client + .reset_password_for_email(&demo_email, Some(options)) + .await; // Wait to prevent running into Supabase rate limits when running cargo test let one_minute = time::Duration::from_secs(60);