1use crate::{
13 error::{Error, Result},
14 types::{SupabaseConfig, Timestamp},
15};
16use chrono::Utc;
17use reqwest::Client as HttpClient;
18use serde::{Deserialize, Serialize};
19use std::collections::HashMap;
20use std::sync::{Arc, RwLock, Weak};
21use tracing::{debug, info, warn};
22use uuid::Uuid;
23
24#[derive(Debug, Clone, PartialEq, Eq)]
26pub enum AuthEvent {
27 SignedIn,
29 SignedOut,
31 TokenRefreshed,
33 UserUpdated,
35 PasswordReset,
37}
38
39pub type AuthStateCallback = Box<dyn Fn(AuthEvent, Option<Session>) + Send + Sync + 'static>;
41
42#[derive(Debug, Clone)]
44pub struct AuthEventHandle {
45 id: Uuid,
46 auth: Weak<Auth>,
47}
48
49impl AuthEventHandle {
50 pub fn remove(&self) {
52 if let Some(auth) = self.auth.upgrade() {
53 auth.remove_auth_listener(self.id);
54 }
55 }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60pub enum OAuthProvider {
61 #[serde(rename = "google")]
63 Google,
64 #[serde(rename = "github")]
66 GitHub,
67 #[serde(rename = "discord")]
69 Discord,
70 #[serde(rename = "apple")]
72 Apple,
73 #[serde(rename = "twitter")]
75 Twitter,
76 #[serde(rename = "facebook")]
78 Facebook,
79 #[serde(rename = "azure")]
81 Microsoft,
82 #[serde(rename = "linkedin_oidc")]
84 LinkedIn,
85}
86
87impl OAuthProvider {
88 pub fn as_str(&self) -> &'static str {
90 match self {
91 OAuthProvider::Google => "google",
92 OAuthProvider::GitHub => "github",
93 OAuthProvider::Discord => "discord",
94 OAuthProvider::Apple => "apple",
95 OAuthProvider::Twitter => "twitter",
96 OAuthProvider::Facebook => "facebook",
97 OAuthProvider::Microsoft => "azure",
98 OAuthProvider::LinkedIn => "linkedin_oidc",
99 }
100 }
101}
102
103#[derive(Debug, Clone, Default)]
105pub struct OAuthOptions {
106 pub redirect_to: Option<String>,
108 pub scopes: Option<Vec<String>>,
110 pub query_params: Option<std::collections::HashMap<String, String>>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct OAuthResponse {
117 pub url: String,
119}
120
121#[derive(Debug, Serialize)]
123struct PhoneSignUpRequest {
124 phone: String,
125 password: String,
126 #[serde(skip_serializing_if = "Option::is_none")]
127 data: Option<serde_json::Value>,
128}
129
130#[derive(Debug, Serialize)]
132struct PhoneSignInRequest {
133 phone: String,
134 password: String,
135}
136
137#[derive(Debug, Serialize)]
139struct OTPVerificationRequest {
140 phone: String,
141 token: String,
142 #[serde(rename = "type")]
143 verification_type: String,
144}
145
146#[derive(Debug, Serialize)]
148struct MagicLinkRequest {
149 email: String,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 redirect_to: Option<String>,
152 #[serde(skip_serializing_if = "Option::is_none")]
153 data: Option<serde_json::Value>,
154}
155
156#[derive(Debug, Serialize)]
158struct AnonymousSignInRequest {
159 #[serde(skip_serializing_if = "Option::is_none")]
160 data: Option<serde_json::Value>,
161}
162
163pub struct Auth {
165 http_client: Arc<HttpClient>,
166 config: Arc<SupabaseConfig>,
167 session: Arc<RwLock<Option<Session>>>,
168 event_listeners: Arc<RwLock<HashMap<Uuid, AuthStateCallback>>>,
169}
170
171impl Clone for Auth {
172 fn clone(&self) -> Self {
173 Self {
174 http_client: self.http_client.clone(),
175 config: self.config.clone(),
176 session: self.session.clone(),
177 event_listeners: Arc::new(RwLock::new(HashMap::new())),
178 }
179 }
180}
181
182impl std::fmt::Debug for Auth {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 f.debug_struct("Auth")
185 .field("http_client", &"HttpClient")
186 .field("config", &self.config)
187 .field("session", &self.session)
188 .field(
189 "event_listeners",
190 &format!(
191 "HashMap<Uuid, Callback> with {} listeners",
192 self.event_listeners.read().map(|l| l.len()).unwrap_or(0)
193 ),
194 )
195 .finish()
196 }
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct User {
202 pub id: Uuid,
203 pub email: Option<String>,
204 pub phone: Option<String>,
205 pub email_confirmed_at: Option<Timestamp>,
206 pub phone_confirmed_at: Option<Timestamp>,
207 pub created_at: Timestamp,
208 pub updated_at: Timestamp,
209 pub last_sign_in_at: Option<Timestamp>,
210 pub app_metadata: serde_json::Value,
211 pub user_metadata: serde_json::Value,
212 pub aud: String,
213 pub role: Option<String>,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct Session {
219 pub access_token: String,
220 pub refresh_token: String,
221 pub expires_in: i64,
222 pub expires_at: Timestamp,
223 pub token_type: String,
224 pub user: User,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct AuthResponse {
230 pub user: Option<User>,
231 pub session: Option<Session>,
232}
233
234#[derive(Debug, Serialize)]
236struct SignUpRequest {
237 email: String,
238 password: String,
239 #[serde(skip_serializing_if = "Option::is_none")]
240 data: Option<serde_json::Value>,
241 #[serde(skip_serializing_if = "Option::is_none")]
242 redirect_to: Option<String>,
243}
244
245#[derive(Debug, Serialize)]
247struct SignInRequest {
248 email: String,
249 password: String,
250}
251
252#[derive(Debug, Serialize)]
254struct PasswordResetRequest {
255 email: String,
256 #[serde(skip_serializing_if = "Option::is_none")]
257 redirect_to: Option<String>,
258}
259
260#[derive(Debug, Serialize)]
262struct RefreshTokenRequest {
263 refresh_token: String,
264}
265
266#[derive(Debug, Serialize)]
268struct UpdateUserRequest {
269 #[serde(skip_serializing_if = "Option::is_none")]
270 email: Option<String>,
271 #[serde(skip_serializing_if = "Option::is_none")]
272 password: Option<String>,
273 #[serde(skip_serializing_if = "Option::is_none")]
274 data: Option<serde_json::Value>,
275}
276
277impl Auth {
278 pub fn new(config: Arc<SupabaseConfig>, http_client: Arc<HttpClient>) -> Result<Self> {
280 debug!("Initializing Auth module");
281
282 Ok(Self {
283 http_client,
284 config,
285 session: Arc::new(RwLock::new(None)),
286 event_listeners: Arc::new(RwLock::new(HashMap::new())),
287 })
288 }
289
290 pub async fn sign_up_with_email_and_password(
292 &self,
293 email: &str,
294 password: &str,
295 ) -> Result<AuthResponse> {
296 self.sign_up_with_email_password_and_data(email, password, None, None)
297 .await
298 }
299
300 pub async fn sign_up_with_email_password_and_data(
302 &self,
303 email: &str,
304 password: &str,
305 data: Option<serde_json::Value>,
306 redirect_to: Option<String>,
307 ) -> Result<AuthResponse> {
308 debug!("Signing up user with email: {}", email);
309
310 let payload = SignUpRequest {
311 email: email.to_string(),
312 password: password.to_string(),
313 data,
314 redirect_to,
315 };
316
317 let response = self
318 .http_client
319 .post(format!("{}/auth/v1/signup", self.config.url))
320 .json(&payload)
321 .send()
322 .await?;
323
324 if !response.status().is_success() {
325 let status = response.status();
326 let error_msg = match response.text().await {
327 Ok(text) => text,
328 Err(_) => format!("Sign up failed with status: {}", status),
329 };
330 return Err(Error::auth(error_msg));
331 }
332
333 let auth_response: AuthResponse = response.json().await?;
334
335 if let Some(ref session) = auth_response.session {
336 self.set_session(session.clone()).await?;
337 self.trigger_auth_event(AuthEvent::SignedIn);
338 info!("User signed up successfully");
339 }
340
341 Ok(auth_response)
342 }
343
344 pub async fn sign_in_with_email_and_password(
346 &self,
347 email: &str,
348 password: &str,
349 ) -> Result<AuthResponse> {
350 debug!("Signing in user with email: {}", email);
351
352 let payload = SignInRequest {
353 email: email.to_string(),
354 password: password.to_string(),
355 };
356
357 let response = self
358 .http_client
359 .post(format!(
360 "{}/auth/v1/token?grant_type=password",
361 self.config.url
362 ))
363 .json(&payload)
364 .send()
365 .await?;
366
367 if !response.status().is_success() {
368 let status = response.status();
369 let error_msg = match response.text().await {
370 Ok(text) => text,
371 Err(_) => format!("Sign in failed with status: {}", status),
372 };
373 return Err(Error::auth(error_msg));
374 }
375
376 let auth_response: AuthResponse = response.json().await?;
377
378 if let Some(ref session) = auth_response.session {
379 self.set_session(session.clone()).await?;
380 self.trigger_auth_event(AuthEvent::SignedIn);
381 info!("User signed in successfully");
382 }
383
384 Ok(auth_response)
385 }
386
387 pub async fn sign_out(&self) -> Result<()> {
389 debug!("Signing out user");
390
391 let session = self.get_session()?;
392
393 let response = self
394 .http_client
395 .post(format!("{}/auth/v1/logout", self.config.url))
396 .header("Authorization", format!("Bearer {}", session.access_token))
397 .send()
398 .await?;
399
400 if !response.status().is_success() {
401 warn!("Sign out request failed with status: {}", response.status());
402 }
403
404 self.clear_session().await?;
405 self.trigger_auth_event(AuthEvent::SignedOut);
406 info!("User signed out successfully");
407
408 Ok(())
409 }
410
411 pub async fn reset_password_for_email(&self, email: &str) -> Result<()> {
413 self.reset_password_for_email_with_redirect(email, None)
414 .await
415 }
416
417 pub async fn reset_password_for_email_with_redirect(
419 &self,
420 email: &str,
421 redirect_to: Option<String>,
422 ) -> Result<()> {
423 debug!("Requesting password reset for email: {}", email);
424
425 let payload = PasswordResetRequest {
426 email: email.to_string(),
427 redirect_to,
428 };
429
430 let response = self
431 .http_client
432 .post(format!("{}/auth/v1/recover", self.config.url))
433 .json(&payload)
434 .send()
435 .await?;
436
437 if !response.status().is_success() {
438 let status = response.status();
439 let error_msg = match response.text().await {
440 Ok(text) => text,
441 Err(_) => format!("Password reset failed with status: {}", status),
442 };
443 return Err(Error::auth(error_msg));
444 }
445
446 info!("Password reset email sent successfully");
447 Ok(())
448 }
449
450 pub async fn update_user(
452 &self,
453 email: Option<String>,
454 password: Option<String>,
455 data: Option<serde_json::Value>,
456 ) -> Result<AuthResponse> {
457 debug!("Updating user information");
458
459 let session = self.get_session()?;
460
461 let payload = UpdateUserRequest {
462 email,
463 password,
464 data,
465 };
466
467 let response = self
468 .http_client
469 .put(format!("{}/auth/v1/user", self.config.url))
470 .header("Authorization", format!("Bearer {}", session.access_token))
471 .json(&payload)
472 .send()
473 .await?;
474
475 if !response.status().is_success() {
476 let status = response.status();
477 let error_msg = match response.text().await {
478 Ok(text) => text,
479 Err(_) => format!("User update failed with status: {}", status),
480 };
481 return Err(Error::auth(error_msg));
482 }
483
484 let auth_response: AuthResponse = response.json().await?;
485
486 if let Some(ref session) = auth_response.session {
487 self.set_session(session.clone()).await?;
488 }
489
490 info!("User updated successfully");
491 Ok(auth_response)
492 }
493
494 pub async fn refresh_session(&self) -> Result<AuthResponse> {
496 debug!("Refreshing session token");
497
498 let current_session = self.get_session()?;
499
500 let payload = RefreshTokenRequest {
501 refresh_token: current_session.refresh_token.clone(),
502 };
503
504 let response = self
505 .http_client
506 .post(format!(
507 "{}/auth/v1/token?grant_type=refresh_token",
508 self.config.url
509 ))
510 .json(&payload)
511 .send()
512 .await?;
513
514 if !response.status().is_success() {
515 let status = response.status();
516 let error_msg = match response.text().await {
517 Ok(text) => text,
518 Err(_) => format!("Token refresh failed with status: {}", status),
519 };
520 return Err(Error::auth(error_msg));
521 }
522
523 let auth_response: AuthResponse = response.json().await?;
524
525 if let Some(ref session) = auth_response.session {
526 self.set_session(session.clone()).await?;
527 self.trigger_auth_event(AuthEvent::TokenRefreshed);
528 info!("Session refreshed successfully");
529 }
530
531 Ok(auth_response)
532 }
533
534 pub async fn current_user(&self) -> Result<Option<User>> {
536 let session_guard = self
537 .session
538 .read()
539 .map_err(|_| Error::auth("Failed to read session"))?;
540 Ok(session_guard.as_ref().map(|s| s.user.clone()))
541 }
542
543 pub fn get_session(&self) -> Result<Session> {
545 let session_guard = self
546 .session
547 .read()
548 .map_err(|_| Error::auth("Failed to read session"))?;
549 session_guard
550 .as_ref()
551 .cloned()
552 .ok_or_else(|| Error::auth("No active session"))
553 }
554
555 pub async fn set_session(&self, session: Session) -> Result<()> {
557 let mut session_guard = self
558 .session
559 .write()
560 .map_err(|_| Error::auth("Failed to write session"))?;
561 *session_guard = Some(session);
562 Ok(())
563 }
564
565 pub async fn set_session_token(&self, token: &str) -> Result<()> {
567 debug!("Setting session from token");
568
569 let user_response = self
570 .http_client
571 .get(format!("{}/auth/v1/user", self.config.url))
572 .header("Authorization", format!("Bearer {}", token))
573 .send()
574 .await?;
575
576 if !user_response.status().is_success() {
577 return Err(Error::auth("Invalid token"));
578 }
579
580 let user: User = user_response.json().await?;
581
582 let session = Session {
583 access_token: token.to_string(),
584 refresh_token: String::new(),
585 expires_in: 3600,
586 expires_at: Utc::now() + chrono::Duration::seconds(3600),
587 token_type: "bearer".to_string(),
588 user,
589 };
590
591 self.set_session(session).await?;
592 Ok(())
593 }
594
595 pub async fn clear_session(&self) -> Result<()> {
597 let mut session_guard = self
598 .session
599 .write()
600 .map_err(|_| Error::auth("Failed to write session"))?;
601 *session_guard = None;
602 Ok(())
603 }
604
605 pub fn is_authenticated(&self) -> bool {
607 let session_guard = self.session.read().unwrap_or_else(|_| {
608 warn!("Failed to read session lock");
609 self.session.read().unwrap()
610 });
611
612 match session_guard.as_ref() {
613 Some(session) => {
614 let now = Utc::now();
615 session.expires_at > now
616 }
617 None => false,
618 }
619 }
620
621 pub fn needs_refresh(&self) -> bool {
623 let session_guard = match self.session.read() {
624 Ok(guard) => guard,
625 Err(_) => {
626 warn!("Failed to read session lock");
627 return false;
628 }
629 };
630
631 match session_guard.as_ref() {
632 Some(session) => {
633 let now = Utc::now();
634 let buffer = chrono::Duration::minutes(5); session.expires_at < (now + buffer)
636 }
637 None => false,
638 }
639 }
640
641 pub async fn sign_in_with_oauth(
666 &self,
667 provider: OAuthProvider,
668 options: Option<OAuthOptions>,
669 ) -> Result<OAuthResponse> {
670 debug!("Initiating OAuth sign-in with provider: {:?}", provider);
671
672 let mut url = format!(
673 "{}/auth/v1/authorize?provider={}",
674 self.config.url,
675 provider.as_str()
676 );
677
678 if let Some(opts) = options {
679 if let Some(redirect_to) = opts.redirect_to {
680 url.push_str(&format!(
681 "&redirect_to={}",
682 urlencoding::encode(&redirect_to)
683 ));
684 }
685
686 if let Some(scopes) = opts.scopes {
687 let scope_str = scopes.join(" ");
688 url.push_str(&format!("&scope={}", urlencoding::encode(&scope_str)));
689 }
690
691 if let Some(query_params) = opts.query_params {
692 for (key, value) in query_params {
693 url.push_str(&format!(
694 "&{}={}",
695 urlencoding::encode(&key),
696 urlencoding::encode(&value)
697 ));
698 }
699 }
700 }
701
702 Ok(OAuthResponse { url })
703 }
704
705 pub async fn sign_up_with_phone(
724 &self,
725 phone: &str,
726 password: &str,
727 data: Option<serde_json::Value>,
728 ) -> Result<AuthResponse> {
729 debug!("Signing up user with phone: {}", phone);
730
731 let payload = PhoneSignUpRequest {
732 phone: phone.to_string(),
733 password: password.to_string(),
734 data,
735 };
736
737 let response = self
738 .http_client
739 .post(format!("{}/auth/v1/signup", self.config.url))
740 .json(&payload)
741 .send()
742 .await?;
743
744 if !response.status().is_success() {
745 let status = response.status();
746 let error_msg = match response.text().await {
747 Ok(text) => text,
748 Err(_) => format!("Phone sign up failed with status: {}", status),
749 };
750 return Err(Error::auth(error_msg));
751 }
752
753 let auth_response: AuthResponse = response.json().await?;
754
755 if let Some(ref session) = auth_response.session {
756 self.set_session(session.clone()).await?;
757 self.trigger_auth_event(AuthEvent::SignedIn);
758 info!("User signed up with phone successfully");
759 }
760
761 Ok(auth_response)
762 }
763
764 pub async fn sign_in_with_phone(&self, phone: &str, password: &str) -> Result<AuthResponse> {
783 debug!("Signing in user with phone: {}", phone);
784
785 let payload = PhoneSignInRequest {
786 phone: phone.to_string(),
787 password: password.to_string(),
788 };
789
790 let response = self
791 .http_client
792 .post(format!(
793 "{}/auth/v1/token?grant_type=password",
794 self.config.url
795 ))
796 .json(&payload)
797 .send()
798 .await?;
799
800 if !response.status().is_success() {
801 let status = response.status();
802 let error_msg = match response.text().await {
803 Ok(text) => text,
804 Err(_) => format!("Phone sign in failed with status: {}", status),
805 };
806 return Err(Error::auth(error_msg));
807 }
808
809 let auth_response: AuthResponse = response.json().await?;
810
811 if let Some(ref session) = auth_response.session {
812 self.set_session(session.clone()).await?;
813 self.trigger_auth_event(AuthEvent::SignedIn);
814 info!("User signed in with phone successfully");
815 }
816
817 Ok(auth_response)
818 }
819
820 pub async fn verify_otp(
839 &self,
840 phone: &str,
841 token: &str,
842 verification_type: &str,
843 ) -> Result<AuthResponse> {
844 debug!("Verifying OTP for phone: {}", phone);
845
846 let payload = OTPVerificationRequest {
847 phone: phone.to_string(),
848 token: token.to_string(),
849 verification_type: verification_type.to_string(),
850 };
851
852 let response = self
853 .http_client
854 .post(format!("{}/auth/v1/verify", self.config.url))
855 .json(&payload)
856 .send()
857 .await?;
858
859 if !response.status().is_success() {
860 let status = response.status();
861 let error_msg = match response.text().await {
862 Ok(text) => text,
863 Err(_) => format!("OTP verification failed with status: {}", status),
864 };
865 return Err(Error::auth(error_msg));
866 }
867
868 let auth_response: AuthResponse = response.json().await?;
869
870 if let Some(ref session) = auth_response.session {
871 self.set_session(session.clone()).await?;
872 self.trigger_auth_event(AuthEvent::SignedIn);
873 info!("OTP verified successfully");
874 }
875
876 Ok(auth_response)
877 }
878
879 pub async fn sign_in_with_magic_link(
896 &self,
897 email: &str,
898 redirect_to: Option<String>,
899 data: Option<serde_json::Value>,
900 ) -> Result<()> {
901 debug!("Sending magic link to email: {}", email);
902
903 let payload = MagicLinkRequest {
904 email: email.to_string(),
905 redirect_to,
906 data,
907 };
908
909 let response = self
910 .http_client
911 .post(format!("{}/auth/v1/magiclink", self.config.url))
912 .json(&payload)
913 .send()
914 .await?;
915
916 if !response.status().is_success() {
917 let status = response.status();
918 let error_msg = match response.text().await {
919 Ok(text) => text,
920 Err(_) => format!("Magic link request failed with status: {}", status),
921 };
922 return Err(Error::auth(error_msg));
923 }
924
925 info!("Magic link sent successfully");
926 Ok(())
927 }
928
929 pub async fn sign_in_anonymously(
950 &self,
951 data: Option<serde_json::Value>,
952 ) -> Result<AuthResponse> {
953 debug!("Creating anonymous user session");
954
955 let payload = AnonymousSignInRequest { data };
956
957 let response = self
958 .http_client
959 .post(format!("{}/auth/v1/signup", self.config.url))
960 .header("Authorization", format!("Bearer {}", self.config.key))
961 .json(&payload)
962 .send()
963 .await?;
964
965 if !response.status().is_success() {
966 let status = response.status();
967 let error_msg = match response.text().await {
968 Ok(text) => text,
969 Err(_) => format!("Anonymous sign in failed with status: {}", status),
970 };
971 return Err(Error::auth(error_msg));
972 }
973
974 let auth_response: AuthResponse = response.json().await?;
975
976 if let Some(ref session) = auth_response.session {
977 self.set_session(session.clone()).await?;
978 self.trigger_auth_event(AuthEvent::SignedIn);
979 info!("Anonymous user session created successfully");
980 }
981
982 Ok(auth_response)
983 }
984
985 pub async fn reset_password_for_email_enhanced(
1002 &self,
1003 email: &str,
1004 redirect_to: Option<String>,
1005 ) -> Result<()> {
1006 debug!("Initiating enhanced password recovery for email: {}", email);
1007
1008 let payload = PasswordResetRequest {
1009 email: email.to_string(),
1010 redirect_to,
1011 };
1012
1013 let response = self
1014 .http_client
1015 .post(format!("{}/auth/v1/recover", self.config.url))
1016 .json(&payload)
1017 .send()
1018 .await?;
1019
1020 if !response.status().is_success() {
1021 let status = response.status();
1022 let error_msg = match response.text().await {
1023 Ok(text) => text,
1024 Err(_) => format!("Enhanced password recovery failed with status: {}", status),
1025 };
1026 return Err(Error::auth(error_msg));
1027 }
1028
1029 self.trigger_auth_event(AuthEvent::PasswordReset);
1030 info!("Enhanced password recovery email sent successfully");
1031 Ok(())
1032 }
1033
1034 pub fn on_auth_state_change<F>(&self, callback: F) -> AuthEventHandle
1065 where
1066 F: Fn(AuthEvent, Option<Session>) + Send + Sync + 'static,
1067 {
1068 let id = Uuid::new_v4();
1069 let callback = Box::new(callback);
1070
1071 if let Ok(mut listeners) = self.event_listeners.write() {
1072 listeners.insert(id, callback);
1073 }
1074
1075 AuthEventHandle {
1076 id,
1077 auth: Arc::downgrade(&Arc::new(self.clone())),
1078 }
1079 }
1080
1081 pub fn remove_auth_listener(&self, id: Uuid) {
1083 if let Ok(mut listeners) = self.event_listeners.write() {
1084 listeners.remove(&id);
1085 }
1086 }
1087
1088 fn trigger_auth_event(&self, event: AuthEvent) {
1090 let session = match self.session.read() {
1091 Ok(guard) => guard.clone(),
1092 Err(_) => {
1093 warn!("Failed to read session for event trigger");
1094 return;
1095 }
1096 };
1097
1098 let listeners = match self.event_listeners.read() {
1099 Ok(guard) => guard,
1100 Err(_) => {
1101 warn!("Failed to read event listeners");
1102 return;
1103 }
1104 };
1105
1106 for callback in listeners.values() {
1107 callback(event.clone(), session.clone());
1108 }
1109 }
1110}