Skip to main content

better_auth_api/plugins/organization/
mod.rs

1pub mod handlers;
2pub mod rbac;
3pub mod types;
4
5use std::collections::HashMap;
6
7use async_trait::async_trait;
8use better_auth_core::error::AuthResult;
9use better_auth_core::plugin::{AuthContext, AuthPlugin, AuthRoute};
10use better_auth_core::types::{AuthRequest, AuthResponse, HttpMethod};
11
12/// Permission definitions for a role
13#[derive(Debug, Clone, Default)]
14pub struct RolePermissions {
15    pub organization: Vec<String>,
16    pub member: Vec<String>,
17    pub invitation: Vec<String>,
18}
19
20/// Configuration for the Organization plugin
21#[derive(Debug, Clone, better_auth_core::PluginConfig)]
22#[plugin(name = "OrganizationPlugin")]
23pub struct OrganizationConfig {
24    /// Allow users to create organizations (default: true)
25    #[config(default = true)]
26    pub allow_user_to_create_organization: bool,
27    /// Maximum organizations per user (None = unlimited)
28    #[config(default = None)]
29    pub organization_limit: Option<usize>,
30    /// Maximum members per organization (None = unlimited)
31    #[config(default = Some(100))]
32    pub membership_limit: Option<usize>,
33    /// Role assigned to organization creator (default: "owner")
34    #[config(default = "owner".to_string())]
35    pub creator_role: String,
36    /// Invitation expiration in seconds (default: 48 hours)
37    #[config(default = 60 * 60 * 48)]
38    pub invitation_expires_in: u64,
39    /// Maximum pending invitations per organization (None = unlimited)
40    #[config(default = Some(100))]
41    pub invitation_limit: Option<usize>,
42    /// Disable organization deletion (default: false)
43    #[config(default = false)]
44    pub disable_organization_deletion: bool,
45    /// Custom role definitions (extending default roles)
46    #[config(default = HashMap::new(), skip)]
47    pub roles: HashMap<String, RolePermissions>,
48}
49
50/// Organization plugin for multi-tenancy support
51pub struct OrganizationPlugin {
52    config: OrganizationConfig,
53}
54
55#[async_trait]
56impl<S: better_auth_core::AuthSchema> AuthPlugin<S> for OrganizationPlugin {
57    fn name(&self) -> &'static str {
58        "organization"
59    }
60
61    fn routes(&self) -> Vec<AuthRoute> {
62        vec![
63            // Organization CRUD
64            AuthRoute::post("/organization/create", "create_organization"),
65            AuthRoute::post("/organization/update", "update_organization"),
66            AuthRoute::post("/organization/delete", "delete_organization"),
67            AuthRoute::get("/organization/list", "list_organizations"),
68            AuthRoute::get(
69                "/organization/get-full-organization",
70                "get_full_organization",
71            ),
72            AuthRoute::post("/organization/check-slug", "check_slug"),
73            AuthRoute::post("/organization/set-active", "set_active_organization"),
74            AuthRoute::post("/organization/leave", "leave_organization"),
75            // Member management
76            AuthRoute::get("/organization/get-active-member", "get_active_member"),
77            AuthRoute::get(
78                "/organization/get-active-member-role",
79                "get_active_member_role",
80            ),
81            AuthRoute::get("/organization/list-members", "list_members"),
82            AuthRoute::post("/organization/remove-member", "remove_member"),
83            AuthRoute::post("/organization/update-member-role", "update_member_role"),
84            // Invitations
85            AuthRoute::post("/organization/invite-member", "invite_member"),
86            AuthRoute::get("/organization/get-invitation", "get_invitation"),
87            AuthRoute::get("/organization/list-invitations", "list_invitations"),
88            AuthRoute::get(
89                "/organization/list-user-invitations",
90                "list_user_invitations",
91            ),
92            AuthRoute::post("/organization/accept-invitation", "accept_invitation"),
93            AuthRoute::post("/organization/reject-invitation", "reject_invitation"),
94            AuthRoute::post("/organization/cancel-invitation", "cancel_invitation"),
95            // Permission check
96            AuthRoute::post("/organization/has-permission", "has_permission"),
97        ]
98    }
99
100    async fn on_request(
101        &self,
102        req: &AuthRequest,
103        ctx: &AuthContext<S>,
104    ) -> AuthResult<Option<AuthResponse>> {
105        match (req.method(), req.path()) {
106            // Organization CRUD
107            (HttpMethod::Post, "/organization/create") => Ok(Some(
108                handlers::org::handle_create_organization(req, ctx, &self.config).await?,
109            )),
110            (HttpMethod::Post, "/organization/update") => Ok(Some(
111                handlers::org::handle_update_organization(req, ctx, &self.config).await?,
112            )),
113            (HttpMethod::Post, "/organization/delete") => Ok(Some(
114                handlers::org::handle_delete_organization(req, ctx, &self.config).await?,
115            )),
116            (HttpMethod::Get, "/organization/list") => Ok(Some(
117                handlers::org::handle_list_organizations(req, ctx).await?,
118            )),
119            (HttpMethod::Get, "/organization/get-full-organization") => Ok(Some(
120                handlers::org::handle_get_full_organization(req, ctx, &self.config).await?,
121            )),
122            (HttpMethod::Post, "/organization/check-slug") => {
123                Ok(Some(handlers::org::handle_check_slug(req, ctx).await?))
124            }
125            (HttpMethod::Post, "/organization/set-active") => Ok(Some(
126                handlers::org::handle_set_active_organization(req, ctx).await?,
127            )),
128            (HttpMethod::Post, "/organization/leave") => Ok(Some(
129                handlers::org::handle_leave_organization(req, ctx, &self.config).await?,
130            )),
131            // Member management
132            (HttpMethod::Get, "/organization/get-active-member") => Ok(Some(
133                handlers::member::handle_get_active_member(req, ctx).await?,
134            )),
135            (HttpMethod::Get, "/organization/get-active-member-role") => Ok(Some(
136                handlers::member::handle_get_active_member_role(req, ctx).await?,
137            )),
138            (HttpMethod::Get, "/organization/list-members") => {
139                Ok(Some(handlers::member::handle_list_members(req, ctx).await?))
140            }
141            (HttpMethod::Post, "/organization/remove-member") => Ok(Some(
142                handlers::member::handle_remove_member(req, ctx, &self.config).await?,
143            )),
144            (HttpMethod::Post, "/organization/update-member-role") => Ok(Some(
145                handlers::member::handle_update_member_role(req, ctx, &self.config).await?,
146            )),
147            // Invitations
148            (HttpMethod::Post, "/organization/invite-member") => Ok(Some(
149                handlers::invitation::handle_invite_member(req, ctx, &self.config).await?,
150            )),
151            (HttpMethod::Get, "/organization/get-invitation") => Ok(Some(
152                handlers::invitation::handle_get_invitation(req, ctx).await?,
153            )),
154            (HttpMethod::Get, "/organization/list-invitations") => Ok(Some(
155                handlers::invitation::handle_list_invitations(req, ctx).await?,
156            )),
157            (HttpMethod::Get, "/organization/list-user-invitations") => Ok(Some(
158                handlers::invitation::handle_list_user_invitations(req, ctx).await?,
159            )),
160            (HttpMethod::Post, "/organization/accept-invitation") => Ok(Some(
161                handlers::invitation::handle_accept_invitation(req, ctx, &self.config).await?,
162            )),
163            (HttpMethod::Post, "/organization/reject-invitation") => Ok(Some(
164                handlers::invitation::handle_reject_invitation(req, ctx).await?,
165            )),
166            (HttpMethod::Post, "/organization/cancel-invitation") => Ok(Some(
167                handlers::invitation::handle_cancel_invitation(req, ctx, &self.config).await?,
168            )),
169            // Permission check
170            (HttpMethod::Post, "/organization/has-permission") => Ok(Some(
171                handlers::handle_has_permission(req, ctx, &self.config).await?,
172            )),
173            _ => Ok(None),
174        }
175    }
176}