agentsclimarketplace

Miro enterprise rbac

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/miro-pack/skills/miro-enterprise-rbac

425 plugins, 2,810 skills, 200 agents for Claude Code. Open-source marketplace at tonsofskills.com with the ccpi CLI package manager.

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill miro-enterprise-rbac

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

'Configure Miro Enterprise features: organization management, SCIM provisioning,

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

9.2 KB, as published. Nobody here has run it

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

RoleViewCommentEdit ItemsShareDelete Board
ViewerYesNoNoNoNo
CommenterYesYesNoNoNo
EditorYesYesYesNoNo
Co-ownerYesYesYesYesNo
OwnerYesYesYesYesYes

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: ['[email protected]', '[email protected]'],
  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: ['[email protected]'],
  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: '[email protected]',
    name: { givenName: 'New', familyName: 'User' },
    emails: [{ value: '[email protected]', 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

FeatureRequired Scope
Board membersboards:read (list) / boards:write (manage)
Team managementteam:read / team:write
Organizationorganizations:read
Audit logsauditlogs:read
SCIM provisioningSCIM token (separate from OAuth)

Error Handling

ErrorStatusCauseSolution
insufficientPermissions403Missing scopeAdd required scope in app settings
memberNotFound404User not on boardInvite user first
teamNotFound404Wrong team ID or no accessVerify org/team hierarchy
orgNotFound404Not Enterprise planUpgrade to Enterprise
scimTokenInvalid401Wrong SCIM tokenGenerate new token in admin console

Resources

Next Steps

For major migrations, see miro-migration-deep-dive.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.