Configure Figma Enterprise features: OAuth 2.0, team management, and
BY jeremylongshoreUPDATED Oct 27, 2026
INTENDED FOR✳Claude CodeInstall in a listed agent.
instructions.md
---
name: figma-enterprise-rbac
description: 'Configure Figma Enterprise features: OAuth 2.0, team management, and
access control.
Use when implementing OAuth flows, managing team/project access via API,
or building Enterprise-level Figma integrations.
Trigger with phrases like "figma enterprise", "figma OAuth",
"figma team management", "figma access control", "figma SCIM".
'
allowed-tools: Read, Write, Edit
version: 1.6.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- figma
compatibility: Designed for Claude Code
---
# Figma Enterprise RBAC
## Overview
Figma Enterprise features accessible via the REST API: OAuth 2.0 for user-facing apps, team/project management, and the Variables API (Enterprise-only). This skill covers building OAuth integrations and managing organizational access.
## Prerequisites
- Figma Enterprise or Organization plan
- OAuth app registered in Figma developer dashboard
- Understanding of OAuth 2.0 authorization code flow
## Instructions
### Step 1: OAuth 2.0 App Setup
```typescript
// Figma OAuth 2.0 Authorization Code Flow
// 1. Build authorization URL
function getAuthUrl(state: string): string {
const params = new URLSearchParams({
client_id: process.env.FIGMA_CLIENT_ID!,
redirect_uri: process.env.FIGMA_REDIRECT_URI!,
scope: 'file_content:read,file_comments:write,file_variables:read',
state,
response_type: 'code',
});
return `https://www.figma.com/oauth?${params}`;
}
// 2. Exchange authorization code for tokens (within 30 seconds!)
async function exchangeCode(code: string): Promise<{
access_token: string;
refresh_token: string;
expires_in: number;
user_id: string;
}> {
const res = await fetch('https://api.figma.com/v1/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.FIGMA_CLIENT_ID!,
client_secret: process.env.FIGMA_CLIENT_SECRET!,
redirect_uri: process.env.FIGMA_REDIRECT_URI!,
code,
grant_type: 'authorization_code',
}),
});
if (!res.ok) {
const error = await res.text();
throw new Error(`Token exchange failed: ${res.status} ${error}`);
}
return res.json();
}
// 3. Refresh expired tokens
async function refreshAccessToken(refreshToken: string): Promise<{
access_token: string;
expires_in: number;
}> {
const res = await fetch('https://api.figma.com/v1/oauth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.FIGMA_CLIENT_ID!,
client_secret: process.env.FIGMA_CLIENT_SECRET!,
refresh_token: refreshToken,
}),
});
if (!res.ok) throw new Error(`Token refresh failed: ${res.status}`);
return res.json();
}
```
### Step 2: OAuth Callback Handler
```typescript
// Express callback handler
app.get('/auth/figma/callback', async (req, res) => {
const { code, state } = req.query;
// Verify state matches what we sent (CSRF protection)
if (state !== req.session.oauthState) {
return res.status(403).json({ error: 'Invalid state parameter' });
}
try {
// Exchange code within 30 seconds
const tokens = await exchangeCode(code as string);
// Get user info with the new token
const userRes = await fetch('https://api.figma.com/v1/me', {
headers: { Authorization: `Bearer ${tokens.access_token}` },
});
const user = await userRes.json();
// Store tokens securely (encrypted at rest)
await saveUserTokens(user.id, {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
expiresAt: new Date(Date.now() + tokens.expires_in * 1000),
});
res.redirect('/dashboard?connected=figma');
} catch (error) {
console.error('Figma OAuth error:', error);
res.redirect('/settings?error=figma_auth_failed');
}
});
```
### Step 3: Team and Project Management
```typescript
// GET /v1/teams/:team_id/projects -- list team projects
async function getTeamProjects(teamId: string, token: string) {
const res = await fetch(
`https://api.figma.com/v1/teams/${teamId}/projects`,
{ headers: { Authorization: `Bearer ${token}` } }
);
return res.json(); // { projects: [{ id, name }] }
}
// GET /v1/projects/:project_id/files -- list project files
async function getProjectFiles(projectId: string, token: string) {
const res = await fetch(
`https://api.figma.com/v1/projects/${projectId}/files`,
{ headers: { Authorization: `Bearer ${token}` } }
);
return res.json(); // { files: [{ key, name, thumbnail_url, last_modified }] }
}
// GET /v1/teams/:team_id/components -- published components (Tier 3)
async function getTeamComponents(teamId: string, token: string) {
const res = await fetch(
`https://api.figma.com/v1/teams/${teamId}/components`,
{ headers: { Authorization: `Bearer ${token}` } }
);
return res.json();
// { meta: { components: [{ key, file_key, node_id, name, description }] } }
}
// GET /v1/teams/:team_id/styles -- published styles (Tier 3)
async function getTeamStyles(teamId: string, token: string) {
const res = await fetch(
`https://api.figma.com/v1/teams/${teamId}/styles`,
{ headers: { Authorization: `Bearer ${token}` } }
);
return res.json();
// { meta: { styles: [{ key, file_key, node_id, name, style_type }] } }
}
```
### Step 4: Variables API (Enterprise Only)
```typescript
// GET /v1/files/:key/variables/local -- Tier 2, requires file_variables:read
async function getLocalVariables(fileKey: string, token: string) {
const res = await fetch(
`https://api.figma.com/v1/files/${fileKey}/variables/local`,
{ headers: { Authorization: `Bearer ${token}` } }
);
if (res.status === 403) {
throw new Error('Variables API requires Figma Enterprise plan');
}
return res.json();
// { meta: { variables: Record<id, Variable>, variableCollections: Record<id, Collection> } }
}
// GET /v1/files/:key/variables/published -- published variables
async function getPublishedVariables(fileKey: string, token: string) {
const res = await fetch(
`https://api.figma.com/v1/files/${fileKey}/variables/published`,
{ headers: { Authorization: `Bearer ${token}` } }
);
return res.json();
// Published variables have a subscribed_id that changes each publish
}
// POST /v1/files/:key/variables -- bulk create/update/delete
async function updateVariables(
fileKey: string,
changes: VariableChanges,
token: string
) {
const res = await fetch(
`https://api.figma.com/v1/files/${fileKey}/variables`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(changes),
}
);
return res.json();
}
```
### Step 5: Access Control Middleware
```typescript
// Middleware that checks if user has Figma access to a resource
async function requireFigmaAccess(fileKey: string) {
return async (req: Request, res: Response, next: NextFunction) => {
const userToken = await getUserFigmaToken(req.user.id);
if (!userToken) {
return res.status(403).json({ error: 'Figma account not connected' });
}
// Check if user's token can access this file
const check = await fetch(
`https://api.figma.com/v1/files/${fileKey}?depth=1`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
if (check.status === 403) {
return res.status(403).json({ error: 'No access to this Figma file' });
}
next();
};
}
```
## Output
- OAuth 2.0 flow with authorization, token exchange, and refresh
- Team/project/file listing via API
- Variables API access (Enterprise)
- Access control middleware for file-level permissions
## Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| OAuth code expired | Exchange took >30s | Exchange immediately on callback |
| Token refresh failed | Refresh token revoked | Re-authenticate user through OAuth flow |
| 403 on Variables API | Not Enterprise plan | Use styles API instead (available on all plans) |
| Team components empty | No published components | Publish components in Figma first |
## Examples
Complete the OAuth flow locally and inspect the granted user (Steps 1-2):
```bash
# After the callback exchanges the code (POST /v1/oauth/token):
curl -s -H "Authorization: Bearer ${FIGMA_OAUTH_TOKEN}" https://api.figma.com/v1/me \
| jq '{handle, email}'
# {"handle": "ops-bot", "email": "design-infra@example.com"}
```
List a project's files as that user — RBAC means you only see what the user can (Step 3):
```bash
curl -s -H "Authorization: Bearer ${FIGMA_OAUTH_TOKEN}" \
"https://api.figma.com/v1/projects/${PROJECT_ID}/files" | jq '.files[].name'
```
A 403 here is working access control, not a bug — route it through the Step 5 middleware. Variables API (Enterprise-only) usage: `references/variables-api-enterprise-only.md`.
## Resources
- [Figma OAuth 2.0](https://developers.figma.com/docs/rest-api/authentication/)
- [Figma Variables API](https://developers.figma.com/docs/rest-api/variables-endpoints/)
- [Component Endpoints](https://developers.figma.com/docs/rest-api/component-endpoints/)
## Next Steps
For major migrations, see `figma-migration-deep-dive`.
---
name: figma-enterprise-rbac
description: 'Configure Figma Enterprise features: OAuth 2.0, team management, and
access control.
Use when implementing OAuth flows, managing team/project access via API,
or building Enterprise-level Figma integrations.
Trigger with phrases like "figma enterprise", "figma OAuth",
"figma team management", "figma access control", "figma SCIM".
'
allowed-tools: Read, Write, Edit
version: 1.6.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- figma
compatibility: Designed for Claude Code
---
# Figma Enterprise RBAC
## Overview
Figma Enterprise features accessible via the REST API: OAuth 2.0 for user-facing apps, team/project management, and the Variables API (Enterprise-only). This skill covers building OAuth integrations and managing organizational access.
## Prerequisites
- Figma Enterprise or Organization plan
- OAuth app registered in Figma developer dashboard
- Understanding of OAuth 2.0 authorization code flow
## Instructions
### Step 1: OAuth 2.0 App Setup
```typescript
// Figma OAuth 2.0 Authorization Code Flow
// 1. Build authorization URL
function getAuthUrl(state: string): string {
const params = new URLSearchParams({
client_id: process.env.FIGMA_CLIENT_ID!,
redirect_uri: process.env.FIGMA_REDIRECT_URI!,
scope: 'file_content:read,file_comments:write,file_variables:read',
state,
response_type: 'code',
});
return `https://www.figma.com/oauth?${params}`;
}
// 2. Exchange authorization code for tokens (within 30 seconds!)
async function exchangeCode(code: string): Promise<{
access_token: string;
refresh_token: string;
expires_in: number;
user_id: string;
}> {
const res = await fetch('https://api.figma.com/v1/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.FIGMA_CLIENT_ID!,
client_secret: process.env.FIGMA_CLIENT_SECRET!,
redirect_uri: process.env.FIGMA_REDIRECT_URI!,
code,
grant_type: 'authorization_code',
}),
});
if (!res.ok) {
const error = await res.text();
throw new Error(`Token exchange failed: ${res.status} ${error}`);
}
return res.json();
}
// 3. Refresh expired tokens
async function refreshAccessToken(refreshToken: string): Promise<{
access_token: string;
expires_in: number;
}> {
const res = await fetch('https://api.figma.com/v1/oauth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.FIGMA_CLIENT_ID!,
client_secret: process.env.FIGMA_CLIENT_SECRET!,
refresh_token: refreshToken,
}),
});
if (!res.ok) throw new Error(`Token refresh failed: ${res.status}`);
return res.json();
}
```
### Step 2: OAuth Callback Handler
```typescript
// Express callback handler
app.get('/auth/figma/callback', async (req, res) => {
const { code, state } = req.query;
// Verify state matches what we sent (CSRF protection)
if (state !== req.session.oauthState) {
return res.status(403).json({ error: 'Invalid state parameter' });
}
try {
// Exchange code within 30 seconds
const tokens = await exchangeCode(code as string);
// Get user info with the new token
const userRes = await fetch('https://api.figma.com/v1/me', {
headers: { Authorization: `Bearer ${tokens.access_token}` },
});
const user = await userRes.json();
// Store tokens securely (encrypted at rest)
await saveUserTokens(user.id, {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
expiresAt: new Date(Date.now() + tokens.expires_in * 1000),
});
res.redirect('/dashboard?connected=figma');
} catch (error) {
console.error('Figma OAuth error:', error);
res.redirect('/settings?error=figma_auth_failed');
}
});
```
### Step 3: Team and Project Management
```typescript
// GET /v1/teams/:team_id/projects -- list team projects
async function getTeamProjects(teamId: string, token: string) {
const res = await fetch(
`https://api.figma.com/v1/teams/${teamId}/projects`,
{ headers: { Authorization: `Bearer ${token}` } }
);
return res.json(); // { projects: [{ id, name }] }
}
// GET /v1/projects/:project_id/files -- list project files
async function getProjectFiles(projectId: string, token: string) {
const res = await fetch(
`https://api.figma.com/v1/projects/${projectId}/files`,
{ headers: { Authorization: `Bearer ${token}` } }
);
return res.json(); // { files: [{ key, name, thumbnail_url, last_modified }] }
}
// GET /v1/teams/:team_id/components -- published components (Tier 3)
async function getTeamComponents(teamId: string, token: string) {
const res = await fetch(
`https://api.figma.com/v1/teams/${teamId}/components`,
{ headers: { Authorization: `Bearer ${token}` } }
);
return res.json();
// { meta: { components: [{ key, file_key, node_id, name, description }] } }
}
// GET /v1/teams/:team_id/styles -- published styles (Tier 3)
async function getTeamStyles(teamId: string, token: string) {
const res = await fetch(
`https://api.figma.com/v1/teams/${teamId}/styles`,
{ headers: { Authorization: `Bearer ${token}` } }
);
return res.json();
// { meta: { styles: [{ key, file_key, node_id, name, style_type }] } }
}
```
### Step 4: Variables API (Enterprise Only)
```typescript
// GET /v1/files/:key/variables/local -- Tier 2, requires file_variables:read
async function getLocalVariables(fileKey: string, token: string) {
const res = await fetch(
`https://api.figma.com/v1/files/${fileKey}/variables/local`,
{ headers: { Authorization: `Bearer ${token}` } }
);
if (res.status === 403) {
throw new Error('Variables API requires Figma Enterprise plan');
}
return res.json();
// { meta: { variables: Record<id, Variable>, variableCollections: Record<id, Collection> } }
}
// GET /v1/files/:key/variables/published -- published variables
async function getPublishedVariables(fileKey: string, token: string) {
const res = await fetch(
`https://api.figma.com/v1/files/${fileKey}/variables/published`,
{ headers: { Authorization: `Bearer ${token}` } }
);
return res.json();
// Published variables have a subscribed_id that changes each publish
}
// POST /v1/files/:key/variables -- bulk create/update/delete
async function updateVariables(
fileKey: string,
changes: VariableChanges,
token: string
) {
const res = await fetch(
`https://api.figma.com/v1/files/${fileKey}/variables`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(changes),
}
);
return res.json();
}
```
### Step 5: Access Control Middleware
```typescript
// Middleware that checks if user has Figma access to a resource
async function requireFigmaAccess(fileKey: string) {
return async (req: Request, res: Response, next: NextFunction) => {
const userToken = await getUserFigmaToken(req.user.id);
if (!userToken) {
return res.status(403).json({ error: 'Figma account not connected' });
}
// Check if user's token can access this file
const check = await fetch(
`https://api.figma.com/v1/files/${fileKey}?depth=1`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
if (check.status === 403) {
return res.status(403).json({ error: 'No access to this Figma file' });
}
next();
};
}
```
## Output
- OAuth 2.0 flow with authorization, token exchange, and refresh
- Team/project/file listing via API
- Variables API access (Enterprise)
- Access control middleware for file-level permissions
## Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| OAuth code expired | Exchange took >30s | Exchange immediately on callback |
| Token refresh failed | Refresh token revoked | Re-authenticate user through OAuth flow |
| 403 on Variables API | Not Enterprise plan | Use styles API instead (available on all plans) |
| Team components empty | No published components | Publish components in Figma first |
## Examples
Complete the OAuth flow locally and inspect the granted user (Steps 1-2):
```bash
# After the callback exchanges the code (POST /v1/oauth/token):
curl -s -H "Authorization: Bearer ${FIGMA_OAUTH_TOKEN}" https://api.figma.com/v1/me \
| jq '{handle, email}'
# {"handle": "ops-bot", "email": "design-infra@example.com"}
```
List a project's files as that user — RBAC means you only see what the user can (Step 3):
```bash
curl -s -H "Authorization: Bearer ${FIGMA_OAUTH_TOKEN}" \
"https://api.figma.com/v1/projects/${PROJECT_ID}/files" | jq '.files[].name'
```
A 403 here is working access control, not a bug — route it through the Step 5 middleware. Variables API (Enterprise-only) usage: `references/variables-api-enterprise-only.md`.
## Resources
- [Figma OAuth 2.0](https://developers.figma.com/docs/rest-api/authentication/)
- [Figma Variables API](https://developers.figma.com/docs/rest-api/variables-endpoints/)
- [Component Endpoints](https://developers.figma.com/docs/rest-api/component-endpoints/)
## Next Steps
For major migrations, see `figma-migration-deep-dive`.
COMPATIBILITY & REFERENCES
Platform guidance
Imported from the listed AgentSkill.sh category. Complete supplied package files and notices are retained. Required external tools and credentials are not installed. No bundled code was executed during import. Individual agent task behavior has not been tested.
Creates comprehensive design systems with typography, colors, components, and documentation for consistent UI development. Use when establishing design standards, building component libraries, or ensuring cross-team consistency. Keywords: design-tokens, typography, spacing, color-palette, compon...
UI design system toolkit for Senior UI Designer including design token generation, component documentation, responsive design calculations, and developer handoff tools. Use for creating design systems, maintaining visual consistency, and facilitating design-dev collaboration.
Comprehensive guide for creating professional UI/UX designs in Penpot using MCP tools. Use this skill when: (1) Creating new UI/UX designs for web, mobile, or desktop applications, (2) Building design systems with components and tokens, (3) Designing dashboards, forms, navigation, or landing page...