alibi_plugins/sso/
hooks.rs1use super::{
3 AuthContext, AuthResult, AuthSchema, AuthUser, EndpointCall, EndpointResponse, SsoPlugin,
4};
5#[async_trait::async_trait]
6impl<S: AuthSchema> alibi_core::endpoint::EndpointHook<S> for SsoPlugin {
7 fn matches_before(&self, call: &EndpointCall, _: &AuthContext<S>) -> AuthResult<bool> {
8 Ok(call.path() == Some("/sign-out"))
9 }
10 async fn before(
11 &self,
12 call: &EndpointCall,
13 ctx: &AuthContext<S>,
14 ) -> AuthResult<Option<alibi_core::endpoint::BeforeEndpointAction>> {
15 if self.config.saml_enable_single_logout {
16 self.clear_saml_signout_tracking(call, ctx).await?;
17 }
18 Ok(None)
19 }
20 fn matches_after(
21 &self,
22 call: &EndpointCall,
23 _: &AuthContext<S>,
24 _: &EndpointResponse,
25 ) -> AuthResult<bool> {
26 Ok(call
27 .path()
28 .is_some_and(|path| path.starts_with("/callback/")))
29 }
30 async fn after(
31 &self,
32 call: &EndpointCall,
33 ctx: &AuthContext<S>,
34 response: EndpointResponse,
35 ) -> AuthResult<EndpointResponse> {
36 if let Some(completed) = call
37 .extensions()
38 .get::<crate::helpers::CompletedSession<S>>()
39 {
40 self.assign_social_domain_organization(completed.user.id().as_ref(), ctx)
41 .await?;
42 }
43 Ok(response)
44 }
45}
46impl SsoPlugin {
47 pub(super) async fn assign_social_domain_organization<S: AuthSchema>(
48 &self,
49 user_id: &str,
50 ctx: &AuthContext<S>,
51 ) -> AuthResult<()> {
52 if !self.config.domain_verification
53 || self.config.organization_provisioning.disabled
54 || ctx
55 .get_metadata(crate::organization::METADATA_ENABLED)
56 .is_none()
57 {
58 return Ok(());
59 }
60 let Some(user) = ctx.database.get_user_by_id_record(user_id).await? else {
61 return Ok(());
62 };
63 if !user.email_verified() {
64 return Ok(());
65 }
66 let Some(domain) = user.email().and_then(super::domains::email_domain) else {
67 return Ok(());
68 };
69 let mut providers: Vec<_> = ctx
70 .database
71 .list_sso_providers()
72 .await?
73 .into_iter()
74 .filter(|provider| {
75 provider.domain_verified == Some(true)
76 && provider
77 .organization_id
78 .as_deref()
79 .is_some_and(|id| !id.is_empty())
80 && super::domains::matches(&domain, &provider.domain)
81 })
82 .collect();
83 let organizations: std::collections::HashSet<_> = providers
84 .iter()
85 .filter_map(|provider| provider.organization_id.as_deref())
86 .collect();
87 if organizations.len() != 1 {
88 return Ok(());
89 }
90 providers.sort_by(|left, right| left.provider_id.cmp(&right.provider_id));
91 if let Some(provider) = providers.first() {
92 self.provision_organization(
93 &ctx.trusted_user_view(&user),
94 provider,
95 ctx,
96 &serde_json::json!({}),
97 None,
98 )
99 .await?;
100 }
101 Ok(())
102 }
103}