Organizations Plugin
Manage multi-tenant organizations, members and teams. Enables team collaboration, and email-based invitations.
Overview
The Organizations plugin provides multi-tenancy and team management capabilities. It enables users to create organizations, manage members with roles, organize members into teams, and send email-based invitations.
Core Entities
- Organizations: Owner-based organizational units
- Invitations: Email-based invitations for joining organizations
- Members: Users assigned to organizations with roles
- Teams: Group members into teams within organizations
- Team Members: Users assigned to teams within organizations
Features
- Multi-tenant organization management
- Role-based member management within organizations
- Team organization and management within organizations
- Email-based member invitations with configurable expiration
- Team member management
- Full lifecycle management (create, read, update, delete operations)
NOTE
This plugin has a dependency on the Access Control plugin for role-based access control. Make sure to configure the Access Control plugin to utilize all features of the Organizations plugin.
Configuration
Standalone Mode:
[plugins.organizations]
enabled = true
organizations_limit = 10 # Optional limit on number of organizations (set to 0 for unlimited)
members_limit = 100 # Optional limit on number of members per organization (set to 0 for unlimited)
invitations_limit = 100 # Optional limit on number of invitations sent to a user (100 by default)
invitation_expires_in = "24h" # Expiration time for organization invitations (24h = 1 day by default)
require_email_verified_on_invitation = true # Whether to require the invited user's email to be verified before accepting or rejecting an organization invitation (false by default)Library Mode:
import (
"time"
authulamodels "github.com/Authula/authula/models"
organizationsplugin "github.com/Authula/authula/plugins/organizations"
organizationsplugintypes "github.com/Authula/authula/plugins/organizations/types"
)
organizationsplugin.New(organizationsplugintypes.OrganizationsPluginConfig{
Enabled: true,
OrganizationsLimit: new(10),
MembersLimit: new(100),
InvitationsLimit: new(100),
InvitationExpiresIn: 7 * 24 * time.Hour,
RequireEmailVerifiedOnInvitation: true,
ServiceHooks: &organizationsplugintypes.OrganizationsServiceHooksConfig{
// Optional service hooks for custom logic on organization lifecycle events
},
SendOrganizationInvitationEmail: func(
params organizationsplugintypes.SendOrganizationInvitationEmailParams,
reqCtx *authulamodels.RequestContext,
) error {
// Optionally handle email sending here...
return nil
},
})API Reference
Each endpoint in this plugin requires the caller to have a specific hardcoded permission assigned to their role(s).
Organizations
| HTTP Method | Route Path | Description | Required Permission |
|---|---|---|---|
POST | /organizations | Create organization | None |
GET | /organizations | List user's organizations | None |
GET | /organizations/{organization_id} | Get organization | organizations:read |
PATCH | /organizations/{organization_id} | Update organization | organizations:update |
DELETE | /organizations/{organization_id} | Delete organization | organizations:delete |
Invitations
| HTTP Method | Route Path | Description | Required Permission |
|---|---|---|---|
POST | /organizations/{organization_id}/invitations | Create invitation | organizations:invitations:create |
GET | /organizations/{organization_id}/invitations | List invitations | organizations:invitations:list |
GET | /organizations/{organization_id}/invitations/{invitation_id} | Get invitation | organizations:invitations:read |
PATCH | /organizations/{organization_id}/invitations/{invitation_id}/revoke | Revoke invitation | organizations:invitations:revoke |
PATCH | /organizations/{organization_id}/invitations/{invitation_id}/accept | Accept invitation | None |
PATCH | /organizations/{organization_id}/invitations/{invitation_id}/reject | Reject invitation | None |
Members
| HTTP Method | Route Path | Description | Required Permission |
|---|---|---|---|
POST | /organizations/{organization_id}/members | Add member | organizations:members:add |
GET | /organizations/{organization_id}/members | List members | organizations:members:list |
GET | /organizations/{organization_id}/members/{member_id} | Get member | organizations:members:read |
GET | /organizations/{organization_id}/members/by-user/{user_id} | Get member by user ID | organizations:members:read |
PATCH | /organizations/{organization_id}/members/{member_id} | Update member | organizations:members:update |
DELETE | /organizations/{organization_id}/members/{member_id} | Remove member | organizations:members:remove |
Teams
| HTTP Method | Route Path | Description | Required Permission |
|---|---|---|---|
POST | /organizations/{organization_id}/teams | Create team | organizations:teams:create |
GET | /organizations/{organization_id}/teams | List teams | organizations:teams:list |
GET | /organizations/{organization_id}/teams/{team_id} | Get team | organizations:teams:read |
PATCH | /organizations/{organization_id}/teams/{team_id} | Update team | organizations:teams:update |
DELETE | /organizations/{organization_id}/teams/{team_id} | Delete team | organizations:teams:delete |
Team Members
| HTTP Method | Route Path | Description | Required Permission |
|---|---|---|---|
POST | /organizations/{organization_id}/teams/{team_id}/members | Add member to team | organizations:team-members:add |
GET | /organizations/{organization_id}/teams/{team_id}/members | List team members | organizations:team-members:list |
GET | /organizations/{organization_id}/teams/{team_id}/members/{member_id} | Get team member | organizations:team-members:read |
DELETE | /organizations/{organization_id}/teams/{team_id}/members/{member_id} | Remove member from team | organizations:team-members:remove |
Database Schema
This plugin creates the following database tables:
Table: organizations
| Field | Type | Key | Description |
|---|---|---|---|
id | uuid | PK | Unique identifier for the organization |
owner_id | uuid | FK | Reference to the organization owner (user) |
name | string | - | Organization name |
slug | string | - | URL-friendly organization identifier (unique) |
logo | string? | - | Organization logo URL |
metadata | JSON | - | Additional organization metadata |
created_at | timestamp | - | Record creation time |
updated_at | timestamp | - | Record last update time |
Table: organization_invitations
| Field | Type | Key | Description |
|---|---|---|---|
id | uuid | PK | Unique identifier for the invitation |
organization_id | uuid | FK | Reference to the organization |
inviter_id | uuid | FK | Reference to the user who sent the invitation |
email | string | - | Email address being invited |
role | string | - | Role assigned to the invited member |
status | string | - | Invitation status |
expires_at | timestamp | - | Invitation expiration time |
created_at | timestamp | - | Record creation time |
Table: organization_members
| Field | Type | Key | Description |
|---|---|---|---|
id | uuid | PK | Unique identifier for the member record |
organization_id | uuid | FK | Reference to the organization |
user_id | uuid | FK | Reference to the user |
role | string | - | Member's role within the organization |
created_at | timestamp | - | Record creation time |
updated_at | timestamp | - | Record last update time |
Table: organization_teams
| Field | Type | Key | Description |
|---|---|---|---|
id | uuid | PK | Unique identifier for the team |
organization_id | uuid | FK | Reference to the organization |
name | string | - | Team name |
slug | string | - | URL-friendly identifier |
description | string? | - | Description |
metadata | JSON | - | Additional metadata |
created_at | timestamp | - | Record creation time |
updated_at | timestamp | - | Record last update time |
Table: organization_team_members
| Field | Type | Key | Description |
|---|---|---|---|
id | uuid | PK | Unique identifier for the team member record |
team_id | uuid | FK | Reference to the team |
member_id | uuid | FK | Reference to the organization member |
created_at | timestamp | - | Record creation time |
Migrations are automatically handled when the plugin is initialized.
Service Hooks
This plugin supports service-level hooks, providing lifecycle callbacks with access to the actor context. Hooks are only supported in Library Mode.
The following hook configurations are available via OrganizationsServiceHooksConfig:
Organizations — OrganizationServiceHooksConfig:
| Hook | Signature |
|---|---|
BeforeCreate | func(ctx, actor *models.Actor, organization *Organization) error |
AfterCreate | func(ctx, actor *models.Actor, organization *Organization) error |
BeforeUpdate | func(ctx, actor *models.Actor, organization *Organization) error |
AfterUpdate | func(ctx, actor *models.Actor, organization *Organization) error |
BeforeDelete | func(ctx, actor *models.Actor, organization *Organization) error |
AfterDelete | func(ctx, actor *models.Actor, organization *Organization) error |
Members — OrganizationMemberServiceHooksConfig:
| Hook | Signature |
|---|---|
BeforeCreate | func(ctx, actor *models.Actor, member *OrganizationMember) error |
AfterCreate | func(ctx, actor *models.Actor, member *OrganizationMember) error |
BeforeUpdate | func(ctx, actor *models.Actor, member *OrganizationMember) error |
AfterUpdate | func(ctx, actor *models.Actor, member *OrganizationMember) error |
BeforeDelete | func(ctx, actor *models.Actor, member *OrganizationMember) error |
AfterDelete | func(ctx, actor *models.Actor, member *OrganizationMember) error |
Invitations — OrganizationInvitationServiceHooksConfig:
| Hook | Signature |
|---|---|
BeforeCreate | func(ctx, actor *models.Actor, invitation *OrganizationInvitation) error |
AfterCreate | func(ctx, actor *models.Actor, invitation *OrganizationInvitation) error |
BeforeUpdate | func(ctx, actor *models.Actor, invitation *OrganizationInvitation) error |
AfterUpdate | func(ctx, actor *models.Actor, invitation *OrganizationInvitation) error |
Teams — OrganizationTeamServiceHooksConfig:
| Hook | Signature |
|---|---|
BeforeCreate | func(ctx, actor *models.Actor, team *OrganizationTeam) error |
AfterCreate | func(ctx, actor *models.Actor, team *OrganizationTeam) error |
BeforeUpdate | func(ctx, actor *models.Actor, team *OrganizationTeam) error |
AfterUpdate | func(ctx, actor *models.Actor, team *OrganizationTeam) error |
BeforeDelete | func(ctx, actor *models.Actor, team *OrganizationTeam) error |
AfterDelete | func(ctx, actor *models.Actor, team *OrganizationTeam) error |
Team Members — OrganizationTeamMemberServiceHooksConfig:
| Hook | Signature |
|---|---|
BeforeCreate | func(ctx, actor *models.Actor, member *OrganizationTeamMember) error |
AfterCreate | func(ctx, actor *models.Actor, member *OrganizationTeamMember) error |
BeforeDelete | func(ctx, actor *models.Actor, member *OrganizationTeamMember) error |
AfterDelete | func(ctx, actor *models.Actor, member *OrganizationTeamMember) error |
NOTE
Service hooks are only supported in Library Mode.
Plugin Capabilities
Service hooks can be used to execute custom logic at various points in the organization, member, invitation, team, and team member lifecycle. See the Service Hooks section for a full list of available hooks.
Security Recommendations
- Ensure that the Access Control plugin is properly configured to manage permissions for organization-related actions.
- Regularly review organization members and their roles to maintain proper access control.
- Make sure to always require authentication for all organization-related API routes and enforce role-based access control for certain routes.
Client Plugin
If you're using the Authula SDK, add the plugin to the client instance as follows:
import { createClient } from "authula";
import { OrganizationsPlugin } from "authula/plugins";
export const authulaClient = createClient({
url: "http://localhost:8080/auth",
plugins: [new OrganizationsPlugin()],
});