Skip to content
Back to skills

Miro Install Auth

ASecurity

Install and configure Miro REST API v2 authentication with OAuth 2.0. Use when setting up a new Miro app, configuring OAuth tokens, or initializing the @mirohq/miro-api Node.js client. Trigger with phrases like "install miro", "setup miro", "miro auth", "miro OAuth", "configure miro API".

  • 4 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 12, 2026
securitytypescriptbashnodenodejsexpressgitapi

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned September 12, 2026

npx -y skills add aibot88/sec_skill_store --skill miro-install-auth --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Miro Install Auth?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Miro Install Auth
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/aibot88-miro-install-auth/badge)](https://www.skillsdirectory.com/skills/aibot88-miro-install-auth)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: miro-install-auth
description: |
  Install and configure Miro REST API v2 authentication with OAuth 2.0.
  Use when setting up a new Miro app, configuring OAuth tokens,
  or initializing the @mirohq/miro-api Node.js client.
  Trigger with phrases like "install miro", "setup miro",
  "miro auth", "miro OAuth", "configure miro API".
allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(npx:*), Grep
version: 1.0.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags: [saas, miro, oauth, authentication]
compatible-with: claude-code
---

# Miro Install & Auth

## Overview

Set up the official `@mirohq/miro-api` Node.js client and configure OAuth 2.0 authentication against the Miro REST API v2 (`https://api.miro.com/v2/`).

## Prerequisites

- Node.js 18+
- A Miro account (Free, Business, or Enterprise)
- A Miro app created at https://developers.miro.com (Your apps > Create new app)
- Client ID, Client Secret, and OAuth redirect URI from the app settings

## Instructions

### Step 1: Install the Official SDK

```bash
# Official Miro Node.js client
npm install @mirohq/miro-api

# For Express-based OAuth callback server
npm install express dotenv
```

### Step 2: Configure OAuth 2.0 Credentials

```bash
# .env (NEVER commit — add to .gitignore)
MIRO_CLIENT_ID=your_client_id
MIRO_CLIENT_SECRET=your_client_secret
MIRO_REDIRECT_URI=http://localhost:3000/auth/miro/callback
MIRO_ACCESS_TOKEN=              # Filled after OAuth flow
MIRO_REFRESH_TOKEN=             # Filled after OAuth flow
```

Miro uses standard OAuth 2.0 authorization code flow. Tokens expire in 3599 seconds (approximately 1 hour). Always store and use the refresh token.

### Step 3: OAuth 2.0 Authorization Flow

```typescript
// src/auth.ts
import { Miro } from '@mirohq/miro-api';
import express from 'express';

// High-level client handles token management
const miro = new Miro({
  clientId: process.env.MIRO_CLIENT_ID!,
  clientSecret: process.env.MIRO_CLIENT_SECRET!,
  redirectUrl: process.env.MIRO_REDIRECT_URI!,
  // Storage adapter for tokens (implement for production)
  storage: {
    async get(userId: string) {
      // Return stored token for user
      return getTokenFromDB(userId);
    },
    async set(userId: string, token) {
      // Persist token
      await saveTokenToDB(userId, token);
    },
  },
});

const app = express();

// Step 1: Redirect user to Miro authorization page
app.get('/auth/miro', (req, res) => {
  const authUrl = miro.getAuthUrl();
  res.redirect(authUrl);
});

// Step 2: Handle OAuth callback
app.get('/auth/miro/callback', async (req, res) => {
  const { code } = req.query;
  if (!code || typeof code !== 'string') {
    return res.status(400).send('Missing authorization code');
  }

  try {
    // Exchange code for access_token + refresh_token
    await miro.exchangeCodeForAccessToken('default-user', code);
    res.send('Miro connected successfully!');
  } catch (err) {
    console.error('Token exchange failed:', err);
    res.status(500).send('Authentication failed');
  }
});

app.listen(3000, () => console.log('OAuth server at http://localhost:3000'));
```

### Step 4: Direct API Access (Access Token Only)

For scripts and automation where you already have an access token:

```typescript
// src/client.ts
import { MiroApi } from '@mirohq/miro-api';

// Low-level stateless client — pass token directly
const api = new MiroApi(process.env.MIRO_ACCESS_TOKEN!);

// Verify connection by listing boards
async function verifyConnection() {
  const boards = await api.getBoards();
  console.log(`Connected! Found ${boards.body.data?.length ?? 0} boards`);
  return true;
}

verifyConnection().catch(console.error);
```

### Step 5: Configure OAuth Scopes

In your Miro app settings (https://developers.miro.com), enable the scopes your app requires:

| Scope | Purpose | Required For |
|-------|---------|-------------|
| `boards:read` | Read board data, items, members | GET endpoints |
| `boards:write` | Create/update/delete boards and items | POST/PUT/PATCH/DELETE endpoints |
| `team:read` | Read team info and members | Team management |
| `team:write` | Manage team membership | Team provisioning |
| `organizations:read` | Read org structure | Enterprise features |
| `identity:read` | Read user profile | User identification |
| `auditlogs:read` | Read audit logs | Enterprise compliance |

Token response after successful exchange:

```json
{
  "access_token": "eyJ...",
  "refresh_token": "eyJ...",
  "token_type": "bearer",
  "expires_in": 3599,
  "scope": "boards:read boards:write",
  "user_id": "1234567890",
  "team_id": "9876543210"
}
```

## Error Handling

| Error | HTTP Status | Cause | Solution |
|-------|-------------|-------|----------|
| `insufficientPermissions` | 403 | Missing OAuth scope | Add required scope in app settings and re-authorize |
| `tokenExpired` | 401 | Access token expired | Use refresh token to get new access token |
| `invalidGrant` | 400 | Auth code already used or expired | Restart OAuth flow from the beginning |
| `invalidClient` | 401 | Wrong client_id or client_secret | Verify credentials in Miro app settings |
| `ENOTFOUND api.miro.com` | N/A | DNS/network failure | Check internet and firewall rules |

## Token Refresh Pattern

```typescript
async function refreshAccessToken(): 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: process.env.MIRO_CLIENT_ID!,
      client_secret: process.env.MIRO_CLIENT_SECRET!,
      refresh_token: process.env.MIRO_REFRESH_TOKEN!,
    }),
  });

  if (!response.ok) {
    throw new Error(`Token refresh failed: ${response.status}`);
  }

  const data = await response.json();
  // Store new tokens
  process.env.MIRO_ACCESS_TOKEN = data.access_token;
  process.env.MIRO_REFRESH_TOKEN = data.refresh_token;
  return data.access_token;
}
```

## Resources

- [Miro OAuth 2.0 Guide](https://developers.miro.com/docs/getting-started-with-oauth)
- [Permission Scopes Reference](https://developers.miro.com/reference/scopes)
- [Miro Node.js Client](https://developers.miro.com/docs/miro-nodejs-readme)
- [@mirohq/miro-api on npm](https://www.npmjs.com/package/@mirohq/miro-api)
- [Troubleshoot OAuth 2.0](https://developers.miro.com/docs/troubleshooting-oauth20)

## Next Steps

After successful auth, proceed to `miro-hello-world` for your first board and item operations.

Files in this skill

  • SKILL.md6.4 KB
  • source.json832 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.