Skip to main content
miro-security-basics Apply Miro REST API v2 security best practices — OAuth scope minimization,
token storage, webhook signature validation, and secret rotation.
Trigger with phrases like "miro security", "miro secrets",
"secure miro", "miro token security", "miro webhook signature".
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill miro-security-basics命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... 同仓库更多 Skills Implement user sign-up and sign-in flows with Clerk.
Use when building authentication UI, customizing sign-in experience,
or implementing OAuth social login.
Trigger with phrases like "clerk sign-in", "clerk sign-up",
"clerk login flow", "clerk OAuth", "clerk social login".
Implement session management and middleware with Clerk.
Use when managing user sessions, configuring route protection,
or implementing token refresh and custom JWT templates.
Trigger with phrases like "clerk session", "clerk middleware",
"clerk route protection", "clerk token", "clerk JWT".
Configure enterprise SSO, role-based access control, and organization management.
Use when implementing SSO integration, configuring role-based permissions,
or setting up organization-level controls.
Trigger with phrases like "clerk SSO", "clerk RBAC",
"clerk enterprise", "clerk roles", "clerk permissions", "clerk organizations".
jeremylongshore
jeremylongshore/claude-code-plugins-plus-skills
打开 GitHub 仓库 name miro-security-basics description Apply Miro REST API v2 security best practices — OAuth scope minimization,
token storage, webhook signature validation, and secret rotation.
Trigger with phrases like "miro security", "miro secrets",
"secure miro", "miro token security", "miro webhook signature".
allowed-tools Read, Write, Grep version 1.6.0 license MIT author Jeremy Longshore <jeremy@intentsolutions.io> tags ["saas","miro","security","oauth"] compatibility Designed for Claude Code
Miro Security Basics
Overview Security best practices for Miro OAuth 2.0 tokens, webhook signatures, and access control across the REST API v2.
Prerequisites
OAuth Token Security
Never Store Tokens in Code
MIRO_CLIENT_ID=3458764500000001
MIRO_CLIENT_SECRET=your_client_secret_here
MIRO_ACCESS_TOKEN=eyJ...
MIRO_REFRESH_TOKEN=eyJ...
.env
.env.local
.env .*.local
*.pem
Scope Minimization Request only the scopes your app actually needs. Fewer scopes = smaller blast radius if a token is compromised.
Use Case Minimum Scopes Read-only dashboard boards:readBoard automation boards:read, boards:writeTeam management boards:read, team:read, team:writeEnterprise admin boards:read, organizations:read, auditlogs:readFull integration boards:read, boards:write, identity:read
Token Lifecycle Management
interface TokenInfo {
accessToken : string ;
refreshToken : string ;
expiresAt : number ;
scopes : string [];
}
class MiroTokenManager {
constructor (
private storage : TokenStorage ,
private clientId : string ,
private clientSecret : string ,
) {}
async getValidToken (userId : string ): Promise <string > {
const info = await this .storage .get (userId);
if (!info) throw new Error ('User not authorized' );
if (Date .now () > info.expiresAt - 300_000 ) {
return this .refreshToken (userId, info.refreshToken );
}
return info.accessToken ;
}
private async refreshToken (userId : string , refreshToken : string ): Promise <string > {
const response = await fetch ('https://api.miro.com/v1/oauth/token' , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/x-www-form-urlencoded' },
body : new URLSearchParams ({
grant_type : 'refresh_token' ,
client_id : this .clientId ,
client_secret : this .clientSecret ,
refresh_token : refreshToken,
}),
});
if (!response.ok ) {
await this .storage .delete (userId);
throw new Error ('Miro refresh token invalid. User must re-authorize.' );
}
const data = await response.json ();
await this .storage .set (userId, {
accessToken : data.access_token ,
refreshToken : data.refresh_token ,
expiresAt : Date .now () + data.expires_in * 1000 ,
scopes : data.scope .split (' ' ),
});
return data.access_token ;
}
}
Webhook Signature Validation Miro signs webhook payloads so you can verify they originate from Miro's servers.
import crypto from 'crypto' ;
function verifyMiroWebhookSignature (
rawBody : Buffer | string ,
signature : string ,
secret : string ,
): boolean {
const expected = crypto
.createHmac ('sha256' , secret)
.update (rawBody)
.digest ('hex' );
try {
return crypto.timingSafeEqual (
Buffer .from (signature, 'hex' ),
Buffer .from (expected, 'hex' ),
);
} catch {
return false ;
}
}
app.post ('/webhooks/miro' ,
express.raw ({ type : 'application/json' }),
(req, res ) => {
const signature = req.headers ['x-miro-signature' ] as string ;
if (!signature || !verifyMiroWebhookSignature (req.body , signature, process.env .MIRO_WEBHOOK_SECRET !)) {
return res.status (401 ).json ({ error : 'Invalid signature' });
}
const event = JSON .parse (req.body .toString ());
res.status (200 ).json ({ received : true });
}
);
Client Secret Rotation
gcloud secrets versions add miro-client-secret \
--data-file=<(echo -n "new_secret_value" )
curl -X POST https://api.miro.com/v1/oauth/token \
-d "grant_type=refresh_token" \
-d "client_id=$MIRO_CLIENT_ID " \
-d "client_secret=NEW_SECRET" \
-d "refresh_token=$MIRO_REFRESH_TOKEN "
Request Signing for Audit Trails interface MiroAuditEntry {
timestamp : string ;
userId : string ;
endpoint : string ;
method : string ;
boardId ?: string ;
requestId ?: string ;
status : number ;
}
async function auditedMiroFetch (
userId : string ,
path : string ,
options : RequestInit = {},
): Promise <Response > {
const response = await fetch (`https://api.miro.com${path} ` , {
...options,
headers : {
'Authorization' : `Bearer ${await tokenManager.getValidToken(userId)} ` ,
'Content-Type' : 'application/json' ,
...options.headers ,
},
});
const audit : MiroAuditEntry = {
timestamp : new Date ().toISOString (),
userId,
endpoint : path,
method : options.method ?? 'GET' ,
boardId : path.match (/boards\/([^/]+)/ )?.[1 ],
requestId : response.headers .get ('X-Request-Id' ) ?? undefined ,
status : response.status ,
};
console .log ('[MIRO_AUDIT]' , JSON .stringify (audit));
return response;
}
Security Checklist
Error Handling Security Issue Detection Mitigation Token in logs Log audit Redact Authorization headers in logging middleware Token in git Pre-commit hook / secret scanning Rotate immediately, revoke old token Webhook forgery Signature validation fails Return 401, alert security team Excessive scopes Scope audit Reduce to minimum needed per endpoint
Resources
Next Steps For production deployment, see miro-prod-checklist.