Claude-code-plugins miro-enterprise-rbac
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/miro-pack/skills/miro-enterprise-rbac" ~/.claude/skills/jeremylongshore-claude-code-plugins-miro-enterprise-rbac && rm -rf "$T"
manifest:
plugins/saas-packs/miro-pack/skills/miro-enterprise-rbac/SKILL.mdsource content
Miro Enterprise RBAC
Overview
Enterprise-grade access control for Miro REST API v2: organization and team management, SCIM user provisioning, board sharing with role-based permissions, and audit log access. Requires Miro Enterprise plan.
Miro Access Hierarchy
Organization (Enterprise) ├── Team 1 │ ├── Board A (sharing: team only) │ │ ├── Owner (full control) │ │ ├── Co-owner (full control, can't delete board) │ │ ├── Editor (can add/edit items) │ │ ├── Commenter (can add comments only) │ │ └── Viewer (read-only) │ └── Board B ├── Team 2 │ └── Board C └── Projects └── Project 1 (groups boards)
Board Roles & Permissions
| Role | View | Comment | Edit Items | Share | Delete Board |
|---|---|---|---|---|---|
| Viewer | Yes | No | No | No | No |
| Commenter | Yes | Yes | No | No | No |
| Editor | Yes | Yes | Yes | No | No |
| Co-owner | Yes | Yes | Yes | Yes | No |
| Owner | Yes | Yes | Yes | Yes | Yes |
Board Member Management
// List board members // GET https://api.miro.com/v2/boards/{board_id}/members const members = await miroFetch(`/v2/boards/${boardId}/members?limit=50`); for (const member of members.data) { console.log(`${member.name} (${member.id}): role=${member.role}`); } // Share board with users // POST https://api.miro.com/v2/boards/{board_id}/members await miroFetch(`/v2/boards/${boardId}/members`, 'POST', { emails: ['dev@company.com', 'pm@company.com'], role: 'editor', // 'viewer' | 'commenter' | 'editor' | 'coowner' message: 'You have been added to the sprint board', }); // Update member role // PATCH https://api.miro.com/v2/boards/{board_id}/members/{member_id} await miroFetch(`/v2/boards/${boardId}/members/${memberId}`, 'PATCH', { role: 'commenter', }); // Remove member from board // DELETE https://api.miro.com/v2/boards/{board_id}/members/{member_id} await miroFetch(`/v2/boards/${boardId}/members/${memberId}`, 'DELETE');
Team Management (Enterprise)
// List teams in organization // GET https://api.miro.com/v2/orgs/{org_id}/teams (Enterprise) const teams = await miroFetch(`/v2/orgs/${orgId}/teams?limit=50`); // Get team details // GET https://api.miro.com/v2/teams/{team_id} const team = await miroFetch(`/v2/teams/${teamId}`); // List team members // GET https://api.miro.com/v2/teams/{team_id}/members const teamMembers = await miroFetch(`/v2/teams/${teamId}/members?limit=100`); // Invite user to team // POST https://api.miro.com/v2/teams/{team_id}/members await miroFetch(`/v2/teams/${teamId}/members`, 'POST', { emails: ['newdev@company.com'], role: 'member', // 'member' | 'admin' | 'non_team' });
Organization Management (Enterprise)
// Get organization info // GET https://api.miro.com/v2/orgs/{org_id} const org = await miroFetch(`/v2/orgs/${orgId}`); // List organization members // GET https://api.miro.com/v2/orgs/{org_id}/members const orgMembers = await miroFetch(`/v2/orgs/${orgId}/members?limit=100`);
SCIM User Provisioning (Enterprise)
Miro supports SCIM 2.0 for automated user lifecycle management from identity providers (Okta, Azure AD, OneLogin).
// SCIM Base URL: https://miro.com/api/v1/scim/v2 // Create user via SCIM // POST https://miro.com/api/v1/scim/v2/Users const scimUser = await fetch('https://miro.com/api/v1/scim/v2/Users', { method: 'POST', headers: { 'Authorization': `Bearer ${scimToken}`, 'Content-Type': 'application/scim+json', }, body: JSON.stringify({ schemas: ['urn:ietf:params:scim:schemas:core:2.0:User'], userName: 'newuser@company.com', name: { givenName: 'New', familyName: 'User' }, emails: [{ value: 'newuser@company.com', type: 'work', primary: true }], active: true, }), }); // List users via SCIM // GET https://miro.com/api/v1/scim/v2/Users const users = await fetch('https://miro.com/api/v1/scim/v2/Users?filter=active eq true', { headers: { 'Authorization': `Bearer ${scimToken}` }, }); // Deactivate user (deprovision) // PATCH https://miro.com/api/v1/scim/v2/Users/{user_id} await fetch(`https://miro.com/api/v1/scim/v2/Users/${scimUserId}`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${scimToken}`, 'Content-Type': 'application/scim+json', }, body: JSON.stringify({ schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'], Operations: [{ op: 'replace', path: 'active', value: false }], }), }); // Manage team membership via SCIM Groups // GET https://miro.com/api/v1/scim/v2/Groups // POST/PATCH Groups to add/remove team members
Board Sharing Policies
Control how boards can be shared at creation time:
// Create board with restrictive sharing await miroFetch('/v2/boards', 'POST', { name: 'Confidential Strategy Board', policy: { sharingPolicy: { access: 'private', // Only invited members inviteToAccountAndBoardLinkAccess: 'no_access', organizationAccess: 'private', // Not visible to org teamAccess: 'private', // Not visible to team }, permissionsPolicy: { collaborationToolsStartAccess: 'all_editors', copyAccess: 'team_members', // Only team can copy sharingAccess: 'owners_and_coowners', // Only owners can share }, }, }); // Create board with open team access await miroFetch('/v2/boards', 'POST', { name: 'Team Brainstorming', teamId: teamId, policy: { sharingPolicy: { access: 'edit', // Team can edit by default teamAccess: 'edit', }, permissionsPolicy: { sharingAccess: 'team_members_and_collaborators', }, }, });
Audit Logs (Enterprise)
// Get audit logs — requires 'auditlogs:read' scope // GET https://api.miro.com/v2/orgs/{org_id}/audit-logs const logs = await miroFetch( `/v2/orgs/${orgId}/audit-logs?limit=100&createdAfter=${startDate}` ); // Log entries include: // - User actions (board created, item modified, member added) // - Admin actions (team created, user deactivated, settings changed) // - API actions (OAuth token issued, SCIM provisioning) for (const entry of logs.data) { console.log({ action: entry.action, actor: entry.actor?.email, target: entry.context?.boardId ?? entry.context?.teamId, timestamp: entry.createdAt, }); }
Access Control Middleware
Enforce board-level permissions in your application:
type BoardRole = 'viewer' | 'commenter' | 'editor' | 'coowner' | 'owner'; const ROLE_HIERARCHY: Record<BoardRole, number> = { viewer: 0, commenter: 1, editor: 2, coowner: 3, owner: 4, }; function hasMinimumRole(userRole: BoardRole, requiredRole: BoardRole): boolean { return ROLE_HIERARCHY[userRole] >= ROLE_HIERARCHY[requiredRole]; } async function requireBoardRole(boardId: string, userId: string, minRole: BoardRole) { const members = await miroFetch(`/v2/boards/${boardId}/members?limit=100`); const user = members.data.find((m: any) => m.id === userId); if (!user) { throw new Error('User is not a board member'); } if (!hasMinimumRole(user.role, minRole)) { throw new Error(`Requires ${minRole} role, user has ${user.role}`); } } // Usage await requireBoardRole(boardId, userId, 'editor'); // Throws if user doesn't have editor or higher role
Required OAuth Scopes
| Feature | Required Scope |
|---|---|
| Board members | (list) / (manage) |
| Team management | / |
| Organization | |
| Audit logs | |
| SCIM provisioning | SCIM token (separate from OAuth) |
Error Handling
| Error | Status | Cause | Solution |
|---|---|---|---|
| 403 | Missing scope | Add required scope in app settings |
| 404 | User not on board | Invite user first |
| 404 | Wrong team ID or no access | Verify org/team hierarchy |
| 404 | Not Enterprise plan | Upgrade to Enterprise |
| 401 | Wrong SCIM token | Generate new token in admin console |
Resources
Next Steps
For major migrations, see
miro-migration-deep-dive.