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}