Skip to main content

Organization Module

The Organization module adds multi-tenant organization support to your application. It provides a complete workspace/team management system with role-based access control, invitation workflows, and organization-scoped middleware. Users can create organizations, invite members, assign roles, and switch between organizations. JWT claims are automatically enriched with organization context on login.

Capabilities​

  • Multi-Organization Membership — Users can belong to multiple organizations simultaneously. Each membership has an independent role.
  • Role Hierarchy — Three built-in roles: owner (full control, can delete org), admin (manage members and invitations), member (read access). Role assignments are enforced at the middleware level.
  • Invitation Flow — Invite users by email with a role assignment. Invitations have configurable expiry (default: 7 days), support accept/decline actions, and can be cancelled by org admins. Optional frontend callback URL for invitation acceptance.
  • Organization Switching — Users switch their active organization via POST /org/switch. The active org is embedded in JWT claims, so downstream services can scope queries without additional lookups.
  • Org-Scoped JWT Claims — The auth interceptor (priority 50) enriches JWT tokens on login with active_org_id, org_role, and org_memberships (array of all memberships with org_id and role).
  • Auto-Create on Signup — When AutoCreateOrg: true, a default organization is automatically created for every new user. The org name supports placeholders: {name}, {email}, {username}.
  • Organization Context Middleware — The org.auth middleware (priority 45) extracts {orgId} from the URL, verifies the user is a member, and injects the org context for handlers.
  • Member Management — List, view, update roles, and remove members. Owners and admins can manage the membership roster.
  • Organization CRUD — Create, read, update, and delete organizations. Includes slug generation for URL-safe identifiers and optional metadata (JSON) and logo URL fields.
  • Configurable Member Limits — Set MaxMembersPerOrg to enforce organization size limits. Use -1 for unlimited (default).

Registration​

import (
"time"

"github.com/bete7512/goauth/pkg/modules/organization"
)

a.Use(organization.New(&organization.Config{
AutoCreateOrg: true,
DefaultOrgNamePattern: "{name}'s workspace",
MaxMembersPerOrg: 50,
InvitationExpiry: 7 * 24 * time.Hour,
InvitationCallbackURL: "http://localhost:3000/invite",
}))

// Or with defaults (no auto-create, unlimited members, 7-day invitations)
a.Use(organization.New(nil))

Configuration​

// organization.Config is a type alias for the internal config.
type Config struct {
// Create a default org when a user signs up (default: false)
AutoCreateOrg bool

// Pattern for auto-created org names.
// Supports {name}, {email}, {username} placeholders.
// Default: "{name}'s workspace"
DefaultOrgNamePattern string

// Maximum members per organization. -1 = unlimited (default).
MaxMembersPerOrg int

// How long invitation tokens remain valid (default: 7 days)
InvitationExpiry time.Duration

// Frontend URL for invitation acceptance.
// Token appended as query param: {URL}?token=xxx
// If empty, the accept endpoint returns JSON response.
InvitationCallbackURL string
}

Endpoints​

User-level routes (no org context)​

These routes require authentication (core.auth middleware) but do not require an organization context.

MethodPathRoute NameDescription
POST/orgorg.createCreate a new organization
GET/org/myorg.myList organizations for current user
POST/org/switchorg.switchSwitch active organization
GET/org/my/invitationsorg.my.invitationsList pending invitations for current user

Public routes (no auth required)​

The accept and decline endpoints are public. The invitation token is the authorization. New users provide name + password to create an account during acceptance.

MethodPathRoute NameDescription
POST/org/invitations/acceptorg.invitations.acceptAccept an invitation (creates user if needed)
POST/org/invitations/declineorg.invitations.declineDecline an invitation

Organization-scoped routes​

These routes require both authentication and the org.auth middleware, which validates organization membership and injects the org context.

MethodPathRoute NameDescription
GET/org/{orgId}org.getGet organization details
PUT/org/{orgId}org.updateUpdate organization
DELETE/org/{orgId}org.deleteDelete organization
GET/org/{orgId}/membersorg.members.listList organization members
GET/org/{orgId}/members/{userId}org.members.getGet a specific member
PUT/org/{orgId}/members/{userId}org.members.updateUpdate member role
DELETE/org/{orgId}/members/{userId}org.members.removeRemove a member
POST/org/{orgId}/inviteorg.inviteSend an invitation
GET/org/{orgId}/invitationsorg.invitations.listList invitations for the org
DELETE/org/{orgId}/invitations/{invId}org.invitations.cancelCancel a pending invitation

Roles​

RoleDescription
ownerFull control. Created automatically when a user creates an org. Can delete the org and manage all members.
adminCan manage members and invitations. Cannot delete the org.
memberBasic access. Can view org details and member list.

Models​

Organization​

FieldTypeDescription
IDstringUUID primary key
NamestringOrganization name
SlugstringURL-safe unique identifier
OwnerIDstringUUID of the user who owns the org
LogoURLstringOptional logo URL
MetadatastringOptional JSON metadata
ActiveboolWhether the org is active (default: true)
CreatedAttime.TimeCreation timestamp
UpdatedAt*time.TimeLast update timestamp

OrganizationMember​

FieldTypeDescription
IDstringUUID primary key
OrgIDstringOrganization UUID
UserIDstringUser UUID
Rolestringowner, admin, or member
JoinedAttime.TimeWhen the user joined
UpdatedAt*time.TimeLast update timestamp

OrgInvitation​

Table: org_invitations

FieldTypeDescription
IDstringUUID primary key
OrgIDstringOrganization UUID
EmailstringInvitee email address
RolestringRole assigned on acceptance
InviterIDstringUUID of the user who sent the invitation
TokenstringUnique invitation token (not exposed in JSON)
Statusstringpending, accepted, declined, or expired
ExpiresAttime.TimeWhen the invitation expires
CreatedAttime.TimeCreation timestamp
AcceptedAt*time.TimeWhen the invitation was accepted

Middleware​

The module registers an org.auth middleware (priority 45, below auth at 50). This middleware is not global -- it is applied per-route via the route's Middlewares field on all organization-scoped routes.

The middleware:

  1. Extracts {orgId} from the URL path
  2. Verifies the authenticated user is a member of that organization
  3. Injects the organization context for handlers to use

Auth Interceptor​

The organization module registers an auth interceptor (priority 50) that enriches JWT claims on login with:

  • active_org_id -- the user's first organization ID
  • org_role -- the user's role in that organization
  • org_memberships -- array of all org memberships with org_id and role

Events​

EventFired When
org.createdOrganization is created
org.updatedOrganization is updated
org.deletedOrganization is deleted
org.member.addedA member is added to an org
org.member.removedA member is removed from an org
org.member.role.changedA member's role is changed
org.invitation.sentAn invitation is sent
org.invitation.acceptedAn invitation is accepted
org.invitation.declinedAn invitation is declined
org.switchedA user switches active organization

Hooks​

When AutoCreateOrg is true, the module subscribes to EventAfterSignup and automatically creates an organization for the new user using the DefaultOrgNamePattern.

Extensibility​

Custom Storage​

The Organization module accepts custom storage through the module constructor. When using the default, repositories are obtained from the shared storage layer during initialization.

Event Hooks​

Subscribe to organization events for custom logic (e.g., provisioning resources when an org is created, notifying members when roles change):

a.On(types.EventOrgCreated, func(ctx context.Context, e *types.Event) error {
// Provision workspace resources, create default channels, etc.
return nil
})

a.On(types.EventOrgMemberAdded, func(ctx context.Context, e *types.Event) error {
// Send welcome message, grant access to shared resources, etc.
return nil
})

Dependencies​

  • Core module (auto-registered)