supabase_auth_redux/
lib.rs

1//! # Supabase Auth Rust Client
2//!
3//! A Rust client library for interacting with the Supabase Auth API.
4//!
5//! This library provides a simple and type-safe way to integrate Supabase authentication
6//! into your Rust applications, supporting common authentication flows like signup,
7//! signin, token refresh, and user management.
8//!
9//! ## Example
10//!
11//! ```rust,no_run
12//! use supabase_auth_redux::{AuthClient, AuthError, IdType};
13//!
14//! # async fn example() -> Result<(), AuthError> {
15//! // Initialize the client
16//! let auth_client = AuthClient::new("https://your-project.supabase.co", "your-anon-key")?;
17//!
18//! // Sign up a new user
19//! let (user, access_token) = auth_client
20//!     .signup(
21//!         IdType::Email("user@example.com".to_string()),
22//!         "secure_password".to_string(),
23//!         None,
24//!     )
25//!     .await?;
26//!
27//! // Sign in an existing user
28//! let token_response = auth_client
29//!     .signin_with_password(
30//!         IdType::Email("user@example.com".to_string()),
31//!         "secure_password".to_string(),
32//!     )
33//!     .await?;
34//! # Ok(())
35//! # }
36//! ```
37
38#![warn(clippy::all)]
39#![warn(missing_docs)]
40
41use std::fmt::{Debug, Display, Formatter};
42
43use postgrest::Postgrest;
44use serde::{Deserialize, Serialize};
45use thiserror::Error;
46
47pub use error::AuthError;
48pub use models::token::TokenResponse;
49pub use models::user::UserSchema as User;
50
51// Re-export for backward compatibility
52#[allow(unused)]
53#[deprecated(
54    since = "0.1.0",
55    note = "Use specific error types from AuthError instead"
56)]
57pub use GoTrueErrorResponse as Error;
58
59mod delete_user;
60mod error;
61mod get_user;
62mod logout;
63pub mod models;
64mod refresh_token;
65mod signin_with_password;
66mod signup;
67mod util;
68
69/// The main authentication client for interacting with Supabase Auth API
70///
71/// This client handles all authentication operations including user signup,
72/// signin, token management, and user administration.
73#[derive(Clone)]
74pub struct AuthClient {
75    /// HTTP client for making API requests
76    http_client: reqwest::Client,
77    /// Base URL of the Supabase API (e.g., `https://your-project.supabase.co`)
78    supabase_api_url: String,
79    /// Anonymous key for public API access
80    supabase_anon_key: String,
81    /// Optional service role key for admin operations
82    supabase_service_role_key: Option<String>,
83    /// PostgREST client for direct database queries
84    postgrest_client: Postgrest,
85}
86
87impl Debug for AuthClient {
88    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
89        f.write_str("AuthClient")
90    }
91}
92
93impl AuthClient {
94    /// Creates a new authentication client with the given API URL and anonymous key
95    ///
96    /// # Arguments
97    ///
98    /// * `api_url` - The base URL of your Supabase instance
99    /// * `anon_key` - The anonymous key for your Supabase project
100    ///
101    /// # Example
102    ///
103    /// ```rust,no_run
104    /// use supabase_auth_redux::AuthClient;
105    ///
106    /// let client = AuthClient::new("https://your-project.supabase.co", "your-anon-key")
107    ///     .expect("Failed to create auth client");
108    /// ```
109    pub fn new(api_url: &str, anon_key: &str) -> Result<Self, AuthError> {
110        if api_url.is_empty() {
111            return Err(AuthError::InvalidParameters);
112        }
113        if anon_key.is_empty() {
114            return Err(AuthError::InvalidParameters);
115        }
116
117        Ok(Self {
118            http_client: reqwest::Client::new(),
119            supabase_api_url: api_url.to_owned(),
120            supabase_anon_key: anon_key.to_owned(),
121            supabase_service_role_key: None,
122            postgrest_client: Postgrest::new(format!("{}/rest/v1/", api_url.to_owned()))
123                .schema("auth")
124                .insert_header("apikey", anon_key),
125        })
126    }
127
128    /// Creates a new builder for constructing an AuthClient with additional options
129    ///
130    /// # Example
131    ///
132    /// ```rust,no_run
133    /// use supabase_auth_redux::AuthClient;
134    ///
135    /// let client = AuthClient::builder()
136    ///     .api_url("https://your-project.supabase.co")
137    ///     .anon_key("your-anon-key")
138    ///     .service_role_key("your-service-role-key")
139    ///     .build()
140    ///     .expect("Failed to create auth client");
141    /// ```
142    pub fn builder() -> AuthClientBuilder {
143        AuthClientBuilder::default()
144    }
145}
146
147/// Builder for constructing an AuthClient with custom configuration
148#[derive(Default)]
149pub struct AuthClientBuilder {
150    /// API URL for the Supabase instance
151    api_url: Option<String>,
152    /// Anonymous key for the Supabase project
153    anon_key: Option<String>,
154    /// Optional service role key for admin operations
155    service_role_key: Option<String>,
156}
157
158impl AuthClientBuilder {
159    /// Sets the API URL for the Supabase instance
160    pub fn api_url(mut self, url: &str) -> Self {
161        self.api_url = Some(url.to_string());
162        self
163    }
164
165    /// Sets the anonymous key for the Supabase project
166    pub fn anon_key(mut self, key: &str) -> Self {
167        self.anon_key = Some(key.to_string());
168        self
169    }
170
171    /// Sets the service role key for admin operations
172    pub fn service_role_key(mut self, key: &str) -> Self {
173        self.service_role_key = Some(key.to_string());
174        self
175    }
176
177    /// Builds the AuthClient with the configured settings
178    ///
179    /// # Errors
180    ///
181    /// Returns `AuthError::InvalidParameters` if required fields are missing
182    pub fn build(self) -> Result<AuthClient, AuthError> {
183        let api_url = self.api_url.ok_or(AuthError::InvalidParameters)?;
184        let anon_key = self.anon_key.ok_or(AuthError::InvalidParameters)?;
185
186        Ok(AuthClient {
187            http_client: reqwest::Client::new(),
188            supabase_api_url: api_url.clone(),
189            supabase_anon_key: anon_key.clone(),
190            supabase_service_role_key: self.service_role_key,
191            postgrest_client: Postgrest::new(format!("{}/rest/v1/", api_url))
192                .schema("auth")
193                .insert_header("apikey", &anon_key),
194        })
195    }
196}
197
198/// Error response from the GoTrue/Supabase Auth API
199#[derive(Debug, Error, Deserialize, Serialize)]
200pub struct GoTrueErrorResponse {
201    /// Error code number from the API
202    pub code: Option<u8>,
203    /// Primary error message
204    pub error: Option<String>,
205    /// Detailed error description
206    pub error_description: Option<String>,
207    /// Alternative error message field used by some endpoints
208    pub msg: Option<String>,
209}
210
211impl Display for GoTrueErrorResponse {
212    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
213        if let Some(ref e) = self.error {
214            f.write_str(e)?;
215            return Ok(());
216        }
217        if let Some(ref msg) = self.msg {
218            f.write_str(msg)?;
219            return Ok(());
220        }
221        Err(std::fmt::Error)
222    }
223}
224
225/// Identifier type for authentication operations
226#[derive(Debug)]
227pub enum IdType {
228    /// Email-based authentication
229    Email(String),
230    /// Phone number-based authentication
231    PhoneNumber(String),
232}