Organization Permissions v3
Overview
Organization Permissions v3 extends the stable v2 foundation (custom roles + JWT integration) with two major features: immutable audit logging for compliance and debugging, and team management for simplified bulk role assignment. This guide covers both features and how they work together to provide enhanced permission management at scale.Key Features
1. Audit Logging
Track every permission change with immutable audit logs that capture:- Who changed it (actor ID)
- What changed (role/team changes with before/after values)
- When it happened (timestamp)
- Where it came from (IP address, session context)
- Why it was approved (optional approval metadata in future versions)
- Immutable: Never modified or deleted (except by retention policy)
- Write-only: No direct updates after creation
- Queryable: Filter by actor, resource type, action, and date range
- Retentionable: Automatic cleanup based on organization policy
2. Teams
Simplify permission management at scale by:- Creating organization-specific teams
- Assigning multiple custom roles to teams
- Adding/removing members from teams
- Members get the union of their personal roles + team roles
- Bulk role assignment: One team can represent many members with the same permissions
- Scalability: Manage 100+ members more efficiently
- Flexibility: Members can belong to multiple teams
- Gradual adoption: Both personal and team-based role assignment work simultaneously
Architecture
Permission Check Flow
When checking if a user has a permission:Permission Union Model
A member’s effective permissions are the union of:- Personal Roles: Custom roles assigned directly to the member
- Team Roles: Custom roles assigned to teams the member belongs to
- Built-in Roles: owner/admin bypass checks entirely
Caching Strategy
Permission unions are cached for performance:- Primary: Redis (distributed, shared across processes)
- Fallback: In-memory Map (single process, when Redis unavailable)
- TTL: 5 minutes
- Invalidation: Eager, on any role/team change
member:{memberId}:org:{orgId}:permissions
Database Schema
New Tables
audit_logs — Immutable append-only audit trailid(UUID, PK)org_id(UUID, FK)actor_id(UUID) — Who made the changeaction(VARCHAR) — Free-form action string (e.g., “member_role_assigned”)resource_type(VARCHAR) — What was changed (e.g., “role”, “team”, “member”)resource_id(UUID) — ID of the resource changedchanges_json(JSONB) — Before/after valuesip_address(INET) — Optional IP address for audit trailsession_context(JSONB) — Optional session data for complianceapproval_status(VARCHAR) — Optional approval workflow status (future)approval_metadata(JSONB) — Optional approval details (future)org_retention_days(INT) — Retention policy at time of logcreated_at(TIMESTAMP)
id(UUID, PK)org_id(UUID, FK)name(VARCHAR)description(TEXT)created_at(TIMESTAMP)updated_at(TIMESTAMP)
id(UUID, PK)team_id(UUID, FK)role_id(UUID, FK)
Schema Extensions
members table:team_ids(UUID[]) — Fast lookup of member’s teams (JSONB array)
API Endpoints
Audit Logging
Read Audit Logs
GET/api/v1/organizations/:orgId/audit
List audit logs with optional filtering.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
actor_id | UUID | Filter by actor who made the change |
resource_type | string | Filter by resource type (role, team, member) |
resource_id | UUID | Filter by specific resource |
action | string | Filter by action name |
start_date | ISO 8601 | Filter logs after this date |
end_date | ISO 8601 | Filter logs before this date |
page | number | Pagination (default: 1) |
pageSize | number | Page size (default: 20, max: 100) |
Cleanup Audit Logs
POST/api/v1/organizations/:orgId/audit/cleanup
Delete audit logs older than the organization’s retention policy. Typically called by a scheduled job.
Request Body:
Teams Management
Create Team
POST/api/v1/organizations/:orgId/teams
Create a new team in the organization. Requires owner or admin role.
Request Body:
List Teams
GET/api/v1/organizations/:orgId/teams
List all teams in the organization.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
page | number | Pagination (default: 1) |
pageSize | number | Page size (default: 20, max: 100) |
Get Team
GET/api/v1/organizations/:orgId/teams/:teamId
Retrieve a specific team by ID.
Response (200 OK):
Update Team
PATCH/api/v1/organizations/:orgId/teams/:teamId
Update team name or description. Requires owner or admin role.
Request Body:
Delete Team
DELETE/api/v1/organizations/:orgId/teams/:teamId
Delete a team. Members remain in the organization but lose team role assignments.
Response (204 No Content)
Assign Role to Team
POST/api/v1/organizations/:orgId/teams/:teamId/roles
Assign a custom role to the team. Members of this team gain the role’s permissions.
Request Body:
Remove Role from Team
DELETE/api/v1/organizations/:orgId/teams/:teamId/roles/:roleId
Remove a custom role from the team. Members lose this role’s permissions unless they have it through another team or personal assignment.
Response (204 No Content)
Add Member to Team
POST/api/v1/organizations/:orgId/teams/:teamId/members
Add an existing organization member to the team.
Request Body:
Remove Member from Team
DELETE/api/v1/organizations/:orgId/teams/:teamId/members/:memberId
Remove a member from the team.
Response (204 No Content)
Common Actions
Track Permission Changes
Create a Team with Multiple Roles
Query Audit Trail for Compliance
Access Control
| Action | Required Role | Scope |
|---|---|---|
| List/View Audit Logs | Member | Organization |
| Create Team | Owner, Admin | Organization |
| Update/Delete Team | Owner, Admin | Organization |
| Assign/Remove Team Roles | Owner, Admin | Organization |
| Add/Remove Team Members | Owner, Admin | Organization |