Bodega

Skills › Productividad

zoom-oauth

Reference skill for Zoom authentication. Use after routing to an auth workflow when choosing app credentials, grant types, scopes, token refresh behavior, or debugging Zoom OAuth failures.

Descargar .zipVer en GitHub
Departamento
Productividad
Fuente
Oficial · anthropics/knowledge-work-plugins › zoom-plugin
Autor
Anthropic
Licencia
Apache-2.0
Idioma
Inglés
Riesgo
Medio

Señales de riesgo

Cómo instalarla en Claude

  1. Descargue el .zip sin descomprimirlo.
  2. En Claude active Configuración › Capacidades › Crear y editar archivos.
  3. Vaya a Personalizar › Skills › Crear skill › Subir una skill y elija el archivo.

Instrucciones completas (SKILL.md)

Zoom OAuth

Background reference for Zoom auth and token lifecycle behavior. Prefer setup-zoom-oauth first, then use this skill for the exact flow, scope, and error details.

Zoom OAuth

Authentication and authorization for Zoom APIs.

📖 Complete Documentation

For comprehensive guides, production patterns, and troubleshooting, see Integrated Index section below.

Quick navigation:

Prerequisites

Four Authorization Use Cases

| Use Case | App Type | Grant Type | Industry Name | |----------|----------|------------|---------------| | Account Authorization | Server-to-Server | account_credentials | Client Credentials Grant, M2M, Two-legged OAuth | | User Authorization | General | authorization_code | Authorization Code Grant, Three-legged OAuth | | Device Authorization | General | urn:ietf:params:oauth:grant-type:device_code | Device Authorization Grant (RFC 8628) | | Client Authorization | General | client_credentials | Client Credentials Grant (chatbot-scoped) |

Industry Terminology

| Term | Meaning | |------|---------| | Two-legged OAuth | No user involved (client ↔ server) | | Three-legged OAuth | User involved (user ↔ client ↔ server) | | M2M | Machine-to-Machine (backend services) | | Public client | Can't keep secrets (mobile, SPA) → use PKCE | | Confidential client | Can keep secrets (backend servers) | | PKCE | Proof Key for Code Exchange (RFC 7636), pronounced "pixy" |

Which Flow Should I Use?

                              ┌─────────────────────┐
                              │  What are you       │
                              │  building?          │
                              └──────────┬──────────┘
                                         │
                    ┌────────────────────┼────────────────────┐
                    │                    │                    │
                    ▼                    ▼                    ▼
          ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
          │  Backend        │  │  App for other  │  │  Chatbot only   │
          │  automation     │  │  users/accounts │  │  (Team Chat)    │
          │  (your account) │  │                 │  │                 │
          └────────┬────────┘  └────────┬────────┘  └────────┬────────┘
                   │                    │                    │
                   ▼                    │                    ▼
          ┌─────────────────┐           │           ┌─────────────────┐
          │    ACCOUNT      │           │           │     CLIENT      │
          │   (S2S OAuth)   │           │           │   (Chatbot)     │
          └─────────────────┘           │           └─────────────────┘
                                        │
                                        ▼
                              ┌─────────────────────┐
                              │  Does device have   │
                              │  a browser?         │
                              └──────────┬──────────┘
                                         │
                         ┌───────────────┴───────────────┐
                         │ NO                         YES│
                         ▼                               ▼
          ┌─────────────────────────┐         ┌─────────────────┐
          │        DEVICE           │         │      USER       │
          │     (Device Flow)       │         │  (Auth Code)    │
          │                         │         │                 │
          │ Examples:               │         │ + PKCE if       │
          │ • Smart TV              │         │   public client │
          │ • Meeting SDK device    │         │                 │
          └─────────────────────────┘         └─────────────────┘

---

Account Authorization (Server-to-Server OAuth)

For backend automation without user interaction.

Request Access Token

POST https://zoom.us/oauth/token?grant_type=account_credentials&account_id={ACCOUNT_ID}

Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}

Response

{
  "access_token": "eyJ...",
  "token_type": "bearer",
  "expires_in": 3600,
  "scope": "user:read:user:admin",
  "api_url": "https://api.zoom.us"
}

Refresh

Access tokens expire after 1 hour. No separate refresh flow - just request a new token.

---

User Authorization (Authorization Code Flow)

For apps that act on behalf of users.

Step 1: Redirect User to Authorize

https://zoom.us/oauth/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}

Use https://zoom.us/oauth/authorize for consent, but https://zoom.us/oauth/token for token exchange.

Optional Parameters:

| Parameter | Description | |-----------|-------------| | state | CSRF protection, maintains state through flow | | code_challenge | For PKCE (see below) | | code_challenge_method | S256 or plain (default: plain) |

Step 2: User Authorizes

  https://example.com/?code={AUTHORIZATION_CODE}

Step 3: Exchange Code for Token

POST https://zoom.us/oauth/token?grant_type=authorization_code&code={CODE}&redirect_uri={REDIRECT_URI}

Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}

With PKCE: Add code_verifier parameter.

Response

{
  "access_token": "eyJ...",
  "token_type": "bearer",
  "refresh_token": "eyJ...",
  "expires_in": 3600,
  "scope": "user:read:user",
  "api_url": "https://api.zoom.us"
}

Refresh Token

POST https://zoom.us/oauth/token?grant_type=refresh_token&refresh_token={REFRESH_TOKEN}

Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}

User-Level vs Account-Level Apps

| Type | Who Can Authorize | Scope Access | |------|-------------------|--------------| | User-level | Any individual user | Scoped to themselves | | Account-level | User with admin permissions | Account-wide access (admin scopes) |

---

Device Authorization (Device Flow)

For devices without browsers (e.g., Meeting SDK apps).

Prerequisites

Enable "Use App on Device" in: Features > Embed > Enable Meeting SDK

Step 1: Request Device Code

POST https://zoom.us/oauth/devicecode?client_id={CLIENT_ID}

Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}

Response

{
  "device_code": "DEVICE_CODE",
  "user_code": "abcd1234",
  "verification_uri": "https://zoom.us/oauth_device",
  "verification_uri_complete": "https://zoom.us/oauth/device/complete/{CODE}",
  "expires_in": 900,
  "interval": 5
}

Step 2: User Authorization

Direct user to:

User signs in and allows the app.

Step 3: Poll for Token

Poll at the interval (5 seconds) until user authorizes:

POST https://zoom.us/oauth/token?grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code={DEVICE_CODE}

Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}

Response

{
  "access_token": "eyJ...",
  "token_type": "bearer",
  "refresh_token": "eyJ...",
  "expires_in": 3599,
  "scope": "user:read:user user:read:token",
  "api_url": "https://api.zoom.us"
}

Polling Responses

| Response | Meaning | Action | |----------|---------|--------| | Token returned | User authorized | Store tokens, done | | error: authorization_pending | User hasn't authorized yet | Keep polling at interval | | error: slow_down | Polling too fast | Increase interval by 5 seconds | | error: expired_token | Device code expired (15 min) | Restart flow from Step 1 | | error: access_denied | User denied authorization | Handle denial, don't retry |

Polling Implementation

async function pollForToken(deviceCode, interval) {
  while (true) {
    await sleep(interval * 1000);
    
    try {
      const response = await axios.post(
        `https://zoom.us/oauth/token?grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=${deviceCode}`,
        null,
        { headers: { 'Authorization': `Basic ${credentials}` } }
      );
      return response.data; // Success - got tokens
    } catch (error) {
      const err = error.response?.data?.error;
      if (err === 'authorization_pending') continue;
      if (err === 'slow_down') { interval += 5; continue; }
      throw error; // expired_token or access_denied
    }
  }
}

Refresh

Same as User Authorization. If refresh token expires, restart device flow from Step 1.

---

Client Authorization (Chatbot)

For chatbot message operations only.

Request Token

POST https://zoom.us/oauth/token?grant_type=client_credentials

Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}

Response

{
  "access_token": "eyJ...",
  "token_type": "bearer",
  "expires_in": 3600,
  "scope": "imchat:bot",
  "api_url": "https://api.zoom.us"
}

Refresh

Tokens expire after 1 hour. No refresh flow - just request a new token.

---

Using Access Tokens

Call API

GET https://api.zoom.us/v2/users/me

Headers:
Authorization: Bearer {ACCESS_TOKEN}

Me Context

Replace userID with me to target the token's associated user:

| Endpoint | Methods | |----------|---------| | /v2/users/me | GET, PATCH | | /v2/users/me/token | GET | | /v2/users/me/meetings | GET, POST |

---

Revoke Access Token

Works for all authorization types.

POST https://zoom.us/oauth/revoke?token={ACCESS_TOKEN}

Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}

Response

{
  "status": "success"
}

---

PKCE (Proof Key for Code Exchange)

For public clients that can't securely store secrets (mobile apps, SPAs, desktop apps).

When to Use PKCE

| Client Type | Use PKCE? | Why | |-------------|-----------|-----| | Mobile app | Yes | Can't securely store client secret | | Single Page App (SPA) | Yes | JavaScript is visible to users | | Desktop app | Yes | Binary can be decompiled | | Meeting SDK (client-side) | Yes | Runs on user's device | | Backend server | Optional | Can keep secrets, but PKCE adds security |

How PKCE Works

┌──────────┐                              ┌──────────┐                    ┌──────────┐
│  Client  │                              │   Zoom   │                    │   Zoom   │
│   App    │                              │  Auth    │                    │  Token   │
└────┬─────┘                              └────┬─────┘                    └────┬─────┘
     │                                         │                              │
     │ 1. Generate code_verifier (random)      │                              │
     │ 2. Create code_challenge = SHA256(verifier)                            │
     │                                         │                              │
     │ ─────── /authorize + code_challenge ──► │                              │
     │                                         │                              │
     │ ◄────── authorization_code ──────────── │                              │
     │                                         │                              │
     │ ─────────────── /token + code_verifier ─┼────────────────────────────► │
     │                                         │                              │
     │                                         │     Verify: SHA256(verifier) │
     │                                         │            == challenge      │
     │                                         │                              │
     │ ◄───────────────────────────────────────┼─────── access_token ──────── │
     │                                         │                              │

Implementation (Node.js)

const crypto = require('crypto');

function generatePKCE() {
  const verifier = crypto.randomBytes(32).toString('base64url');
  const challenge = crypto.createHash('sha256').update(verifier).digest('base64url');
  return { verifier, challenge };
}

const pkce = generatePKCE();

const authUrl = `https://zoom.us/oauth/authorize?` +
  `response_type=code&` +
  `client_id=${CLIENT_ID}&` +
  `redirect_uri=${REDIRECT_URI}&` +
  `code_challenge=${pkce.challenge}&` +
  `code_challenge_method=S256`;

// Store pkce.verifier in session for callback

Token Exchange with PKCE

POST https://zoom.us/oauth/token?grant_type=authorization_code&code={CODE}&redirect_uri={REDIRECT_URI}&code_verifier={VERIFIER}

Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}

---

Deauthorization

When a user removes your app, Zoom sends a webhook to your Deauthorization Notification Endpoint URL.

Webhook Event

{
  "event": "app_deauthorized",
  "event_ts": 1740439732278,
  "payload": {
    "account_id": "ACCOUNT_ID",
    "user_id": "USER_ID",
    "signature": "SIGNATURE",
    "deauthorization_time": "2019-06-17T13:52:28.632Z",
    "client_id": "CLIENT_ID"
  }
}

Requirements

---

Pre-Approval Flow

Some Zoom accounts require Marketplace admin pre-approval before users can authorize apps.

---

Active Apps Notifier (AAN)

In-meeting feature showing apps with real-time access to content.

---

OAuth Scopes

Scope Types

| Type | Description | For | |------|-------------|-----| | Classic scopes | Legacy scopes (user, admin, master levels) | Existing apps | | Granular scopes | New fine-grained scopes with optional support | New apps |

Classic Scopes

For previously-created apps. Three levels:

Full list: https://developers.zoom.us/docs/integrations/oauth-scopes/

Granular Scopes

For new apps. Format: <service>:<action>:<data_claim>:<access>

| Component | Values | |-----------|--------| | service | meeting, webinar, user, recording, etc. | | action | read, write, update, delete | | data_claim | Data category (e.g., participants, settings) | | access | empty (user), admin, master |

Example: meeting:read:list_meetings:admin

Full list: https://developers.zoom.us/docs/integrations/oauth-scopes-granular/

Optional Scopes

Granular scopes can be marked as optional - users choose whether to grant them.

Basic authorization (uses build flow defaults):

https://zoom.us/oauth/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}

Advanced authorization (custom scopes per request):

https://zoom.us/oauth/authorize?client_id={CLIENT_ID}&response_type=code&redirect_uri={REDIRECT_URI}&scope={required_scopes}&optional_scope={optional_scopes}

Include previously granted scopes:

https://zoom.us/oauth/authorize?...&include_granted_scopes&scope={additional_scopes}

Migrating Classic to Granular

Notes:

---

Common Error Codes

| Code | Message | Solution | |------|---------|----------| | 4700 | Token cannot be empty | Check Authorization header has valid token | | 4702/4704 | Invalid client | Verify Client ID and Client Secret | | 4705 | Grant type not supported | Use: account_credentials, authorization_code, urn:ietf:params:oauth:grant-type:device_code, or client_credentials | | 4706 | Client ID or secret missing | Add credentials to header or request params | | 4709 | Redirect URI mismatch | Ensure redirect_uri matches app configuration exactly (including trailing slash) | | 4711 | Refresh token invalid | Token scopes don't match client scopes | | 4717 | App has been disabled | Contact Zoom support | | 4733 | Code is expired | Authorization codes expire in 5 minutes - restart flow | | 4734 | Invalid authorization code | Regenerate authorization code | | 4735 | Owner of token does not exist | User was removed from account - re-authorize | | 4741 | Token has been revoked | Use the most recent token from latest authorization |

See references/oauth-errors.md for complete error list.

---

Quick Reference

| Flow | Grant Type | Token Expiry | Refresh | |------|------------|--------------|---------| | Account (S2S) | account_credentials | 1 hour | Request new token | | User | authorization_code | 1 hour | Use refresh_token (90 day expiry) | | Device | urn:ietf:params:oauth:grant-type:device_code | 1 hour | Use refresh_token (90 day expiry) | | Client (Chatbot) | client_credentials | 1 hour | Request new token |

---

Demo Guidance

If you build an OAuth demo app, document its runtime base URL in that demo project's own README or .env.example, not in this shared skill.

Resources

---

Integrated Index

_This section was migrated from SKILL.md._

Quick Start Path

If you're new to Zoom OAuth, follow this order:

---

Documentation Structure

oauth/
├── SKILL.md                           # Main skill overview
├── SKILL.md                           # This file - navigation guide
│
├── concepts/                          # Core OAuth concepts
│   ├── oauth-flows.md                # 4 flows: S2S, User, Device, Chatbot
│   ├── token-lifecycle.md            # Expiration, refresh, revocation
│   ├── pkce.md                       # PKCE security for public clients
│   ├── scopes-architecture.md        # Classic vs Granular scopes
│   └── state-parameter.md            # CSRF protection with state
│
├── examples/                          # Complete working code
│   ├── s2s-oauth-basic.md            # S2S OAuth minimal example
│   ├── s2s-oauth-redis.md            # S2S OAuth with Redis caching (production)
│   ├── user-oauth-basic.md           # User OAuth minimal example
│   ├── user-oauth-mysql.md           # User OAuth with MySQL + encryption (production)
│   ├── device-flow.md                # Device authorization flow
│   ├── pkce-implementation.md        # PKCE for SPAs/mobile apps
│   └── token-refresh.md              # Auto-refresh middleware pattern
│
├── troubleshooting/                   # Problem solving guides
│   ├── common-errors.md              # Error codes 4700-4741
│   ├── redirect-uri-issues.md        # Most common OAuth error
│   ├── token-issues.md               # Expired, revoked, invalid tokens
│   └── scope-issues.md               # Scope mismatch errors
│
└── references/                        # Reference documentation
    ├── oauth-errors.md                # Complete error code reference
    ├── classic-scopes.md              # Classic scope reference
    └── granular-scopes.md             # Granular scope reference

---

By Use Case

I want to automate Zoom tasks on my own account

I want to build a SaaS app for other Zoom users

I want to build a mobile or SPA app

I want to build an app for devices without browsers (TV, kiosk)

I'm building a Team Chat bot

I'm getting redirect URI errors (4709)

I'm getting token errors (4700-4741)

I'm getting scope errors (4711)

I need to refresh tokens

I want to understand the difference between Classic and Granular scopes

I need to secure my OAuth implementation

I want to migrate from JWT app to S2S OAuth

> Note: JWT App Type was deprecated in June 2023. Migrate to S2S OAuth for server-to-server automation.

---

Most Critical Documents

1. OAuth Flows (DECISION DOCUMENT)

[concepts/oauth-flows.md](concepts/oauth-flows.md)

Understand which of the 4 flows to use:

2. Token Lifecycle (MOST COMMON ISSUE)

[concepts/token-lifecycle.md](concepts/token-lifecycle.md)

99% of OAuth issues stem from misunderstanding:

3. Redirect URI Issues (MOST COMMON ERROR)

[troubleshooting/redirect-uri-issues.md](troubleshooting/redirect-uri-issues.md)

Error 4709 ("Redirect URI mismatch") is the #1 OAuth error. Must match EXACTLY (including trailing slash, http vs https).

---

Key Learnings

Critical Discoveries:

---

Quick Reference

"Which OAuth flow should I use?"

→ [OAuth Flows](concepts/oauth-flows.md)

"Redirect URI mismatch error (4709)"

→ [Redirect URI Issues](troubleshooting/redirect-uri-issues.md)

"Token expired or invalid"

→ [Token Issues](troubleshooting/token-issues.md)

"Refresh token invalid (4735)"

→ [Token Refresh](examples/token-refresh.md) - Must save new refresh token

"Scope mismatch error (4711)"

→ [Scope Issues](troubleshooting/scope-issues.md)

"How do I secure my OAuth app?"

→ [PKCE](concepts/pkce.md) + [State Parameter](concepts/state-parameter.md)

"How do I implement auto-refresh?"

→ [Token Refresh](examples/token-refresh.md)

"What's the difference between Classic and Granular scopes?"

→ [Scopes Architecture](concepts/scopes-architecture.md)

"What error code means what?"

→ [Common Errors](troubleshooting/common-errors.md)

---

Document Version

Based on Zoom OAuth API v2 (2024+)

Deprecated: JWT App Type (June 2023)

---

Happy coding!

Remember: Start with [OAuth Flows](concepts/oauth-flows.md) to understand which flow fits your use case!

Environment Variables