supabase_auth_redux/
get_user.rs

1use log::error;
2use reqwest::StatusCode;
3use std::ops::Add;
4use tracing::{debug, instrument, trace_span, Instrument};
5use uuid::Uuid;
6
7use crate::error::{AuthError, AuthErrorKind};
8use crate::models::user::UserSchema;
9use crate::util::handle_response_code;
10use crate::AuthClient;
11
12impl AuthClient {
13    /// Retrieves user information using an authentication token
14    ///
15    /// This method validates the provided access token and returns the associated user's information.
16    /// It's commonly used to verify that a token is valid and to get the current user's details.
17    ///
18    /// # Arguments
19    ///
20    /// * `auth_token` - A valid JWT access token
21    ///
22    /// # Errors
23    ///
24    /// Returns `AuthError::InvalidParameters` if the token is empty.
25    /// Returns `AuthError::NotAuthorized` if the token is invalid or expired.
26    /// Returns `AuthError::Http` if the API request fails.
27    ///
28    /// # Example
29    ///
30    /// ```rust,no_run
31    /// # use supabase_auth_redux::AuthClient;
32    /// # async fn example() -> Result<(), supabase_auth_redux::AuthError> {
33    /// let client = AuthClient::new("https://your-project.supabase.co", "your-anon-key")?;
34    ///
35    /// // After user signs in and you have their access token
36    /// let user = client.get_user_by_token("user-access-token").await?;
37    /// println!("User email: {:?}", user.email);
38    /// # Ok(())
39    /// # }
40    /// ```
41    #[instrument(skip(self))]
42    pub async fn get_user_by_token(&self, auth_token: &str) -> Result<UserSchema, AuthError> {
43        if auth_token.is_empty() {
44            error!("empty token");
45            return Err(AuthError::InvalidParameters);
46        }
47
48        let resp = match self
49            .http_client
50            .get(format!("{}/auth/v1/{}", self.supabase_api_url, "user"))
51            .bearer_auth(auth_token)
52            .header("apiKey", &self.supabase_anon_key)
53            .send()
54            .instrument(trace_span!("gotrue get user"))
55            .await
56        {
57            Ok(resp) => resp,
58            Err(e) => {
59                debug!("{}", e);
60                return Err(AuthError::Http);
61            }
62        };
63        let resp_code_result = handle_response_code(resp.status()).await;
64        let resp_text = match resp.text().await {
65            Ok(resp_text) => resp_text,
66            Err(e) => {
67                error!("{}", e);
68                return Err(AuthError::Http);
69            }
70        };
71        debug!("resp_text: {}", resp_text);
72        resp_code_result?;
73
74        let user = match serde_json::from_str::<UserSchema>(&resp_text) {
75            Ok(user) => user,
76            Err(e) => {
77                error!("{}", e);
78                return Err(AuthError::Http);
79            }
80        };
81
82        Ok(user)
83    }
84
85    /// Retrieves user information by user ID
86    ///
87    /// This method fetches a user's information directly from the database using their UUID.
88    /// Note: This requires appropriate permissions and may need a service role key depending
89    /// on your Row Level Security policies.
90    ///
91    /// # Arguments
92    ///
93    /// * `user_id` - The UUID of the user to retrieve
94    ///
95    /// # Returns
96    ///
97    /// Returns `Ok(Some(user))` if the user exists, `Ok(None)` if not found.
98    ///
99    /// # Errors
100    ///
101    /// Returns `AuthError::Http` if the database query fails.
102    ///
103    /// # Example
104    ///
105    /// ```rust,no_run
106    /// # use supabase_auth_redux::AuthClient;
107    /// # use uuid::Uuid;
108    /// # async fn example() -> Result<(), supabase_auth_redux::AuthError> {
109    /// let client = AuthClient::new("https://your-project.supabase.co", "your-anon-key")?;
110    ///
111    /// let user_id = Uuid::parse_str("123e4567-e89b-12d3-a456-426614174000").unwrap();
112    /// if let Some(user) = client.get_user_by_id(user_id).await? {
113    ///     println!("Found user: {:?}", user.email);
114    /// }
115    /// # Ok(())
116    /// # }
117    /// ```
118    #[instrument(skip(self))]
119    pub async fn get_user_by_id(&self, user_id: Uuid) -> Result<Option<UserSchema>, AuthError> {
120        let query_result = self
121            .postgrest_client
122            .from("users")
123            .auth(&self.supabase_anon_key)
124            .eq("id", user_id.to_string())
125            .select("*")
126            .execute()
127            .await;
128        let query_response = match query_result {
129            Ok(query_response) => query_response,
130            Err(e) => {
131                error!("{}", e);
132                return Err(AuthError::Http);
133            }
134        };
135        if query_response.status().as_u16() == StatusCode::NOT_FOUND.as_u16() {
136            return Ok(None);
137        }
138
139        let reqwuest_http_status_result = StatusCode::from_u16(query_response.status().as_u16());
140        let Ok(eqwuest_http_status) = reqwuest_http_status_result else {
141            log::error!(
142                "could not covert http status: {:?}",
143                reqwuest_http_status_result.unwrap_err()
144            );
145            return Err(AuthError::Http);
146        };
147        let handle_response_code_result = handle_response_code(eqwuest_http_status).await;
148        let body_text = match query_response.text().await {
149            Ok(resp_text) => resp_text,
150            Err(e) => {
151                error!("{}", e);
152                return Err(AuthError::Http);
153            }
154        };
155        debug!(body = body_text);
156        if let Err(e) = handle_response_code_result {
157            if e.kind() == AuthErrorKind::NotFound {
158                return Ok(None);
159            }
160            handle_response_code_result?
161        }
162
163        let users = match serde_json::from_str::<Vec<UserSchema>>(&body_text) {
164            Ok(users) => users,
165            Err(e) => {
166                error!("{}", e);
167                return Err(AuthError::Http);
168            }
169        };
170
171        if users.iter().len() > 1 {
172            let user_ids_stringify = users
173                .iter()
174                .map(|user| user.id)
175                .fold(String::new(), |mut acc, user_id| {
176                    if acc.is_empty() {
177                        let s = format!("[ {}", user_id);
178                        acc = acc.add(&s);
179                    } else {
180                        let s = format!(", {}", user_id);
181                        acc = acc.add(&s);
182                    }
183                    acc
184                })
185                .add(" ]");
186            debug!(
187                user_ids = user_ids_stringify,
188                "multiple users returned for single user_id"
189            );
190            return Err(AuthError::Internal);
191        }
192
193        Ok(users.first().cloned())
194    }
195}