Zoom Oauth
OpenAIOfficialCreated by ZoomDistributed by OpenAI
- Skill
- Tool integration
- Automation and integrations
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured approach to implementing Zoom OAuth, covering various app types including user-level, account-level, and server-to-server OAuth. It assists developers in selecting the correct grant flow, managing token lifecycles, and diagnosing common authentication errors. The skill emphasizes secure practices, such as handling refresh tokens as single-use values and maintaining proper scope architecture.
What it does
- Guides selection of appropriate OAuth grant flows like PKCE or authorization code.
- Provides workflows for identifying app types and required scopes.
- Offers best practices for secure token management and refresh token rotation.
- Assists in troubleshooting common OAuth errors and redirect URI issues.
- Clarifies the distinction between classic and granular scope usage.
- Defines standard environment variables required for Zoom API integration.
When to use it
- When setting up authentication for a new Zoom Marketplace application.
- When debugging failed authorization requests or token refresh issues.
- When determining the correct OAuth scopes for specific API endpoints.
- When transitioning from classic scopes to granular scopes.
- When implementing server-to-server OAuth for automated account-level tasks.
Advantages
- Reduces integration errors by providing clear flow selection criteria.
- Promotes security by enforcing single-use refresh token patterns.
- Simplifies scope management through structured lookup workflows.
- Centralizes configuration requirements and environment variable standards.
Usage examples
- “How do I implement authorization code flow with PKCE for my public Zoom app?”
- The agent will outline the steps to generate a code challenge, initiate the authorization request, and exchange the code for an access token while ensuring PKCE security.
- “I am getting an invalid scope error when calling the meeting API. How do I fix this?”
- The agent will guide you to verify your app's configured scopes against the endpoint requirements and check if you are using deprecated classic scopes instead of granular ones.
- “What is the correct way to handle Zoom refresh tokens?”
- The agent will explain the requirement to persist the new refresh token returned by every refresh response and discard the old one to maintain security.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/openai/zoom-oauth/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip zoom-oauth.zip -d .agents/skills
Documentation by Zoom
Zoom OAuth
Use this skill for concrete Zoom authentication implementation and troubleshooting. Prefer setup-zoom-oauth for the first-pass setup plan, then return here for exact flows, scope behavior, refresh handling, and error diagnosis.
Workflow
Identify the app type and actor: user-level OAuth, account-level OAuth, server-to-server OAuth where officially supported, SDK JWT, or Build-platform credentials.
Confirm the target API, SDK, or app surface, because scopes and token audiences differ by surface.
Choose the grant flow: authorization code with PKCE for public clients, authorization code for confidential web apps, device authorization where appropriate, or account credentials for supported account-level automation.
Store refresh tokens as single-use values: persist the replacement refresh token returned by each refresh response before reusing the old one.
Validate requests against redirect URI, account ID, scopes, app publication state, and token expiration before changing application code.
For local development, keep access tokens out of logs and treat refresh tokens as single-use when rotating credentials.
References
Full preserved guide: references/full-guide.md
OAuth flows: concepts/oauth-flows.md
Token lifecycle: concepts/token-lifecycle.md
Scope architecture: concepts/scopes-architecture.md
Granular scopes: references/granular-scopes.md
OAuth errors: references/oauth-errors.md
Redirect URI issues: troubleshooting/redirect-uri-issues.md
Files
- ATTRIBUTION.mdAdded by AutoGineer862 B
- LICENSELicence file added by AutoGineer1.1 KiB
- RUNBOOK.md3.3 KiB
- SKILL.md1.8 KiB
- agents/openai.yaml131 B
- concepts/oauth-flows.md22.2 KiB
- concepts/pkce.md13.8 KiB
- concepts/scopes-architecture.md6.7 KiB
- concepts/state-parameter.md8.1 KiB
- concepts/token-lifecycle.md15.1 KiB
- examples/device-flow.md371 B
- examples/pkce-implementation.md379 B
- examples/s2s-oauth-basic.md375 B
- examples/s2s-oauth-redis.md6.6 KiB
- examples/token-refresh.md373 B
- examples/user-oauth-basic.md376 B
- examples/user-oauth-mysql.md376 B
- references/classic-scopes.md2.5 KiB
- references/environment-variables.md863 B
- references/full-guide.md31.7 KiB
- references/granular-scopes.md1.8 KiB
- references/oauth-errors.md4.8 KiB
- troubleshooting/common-errors.md458 B
- troubleshooting/redirect-uri-issues.md229 B
- troubleshooting/scope-issues.md222 B
- troubleshooting/token-issues.md222 B
examples/s2s-oauth-redis.md
S2S OAuth with Redis Caching (Production Pattern)
Production-ready Server-to-Server OAuth implementation with Redis token caching and auto-refresh middleware.
Architecture
Express App
↓
tokenCheck Middleware (automatic token management)
↓
Redis Cache (TTL-based expiration)
↓
Zoom API Routes (protected)Complete Implementation
1. Dependencies
npm install express redis axios query-string dotenv2. Redis Configuration
// configs/redis.js
const redis = require('redis');
const client = redis.createClient({
url: process.env.REDIS_URL || 'redis://YOUR_REDIS_HOST:6379'
});
client.on('error', (err) => console.error('Redis error:', err));
client.on('connect', () => console.log('Connected to Redis'));
module.exports = client;3. Token Utilities
// utils/token.js
const axios = require('axios');
const qs = require('query-string');
const { ZOOM_ACCOUNT_ID, ZOOM_CLIENT_ID, ZOOM_CLIENT_SECRET } = process.env;
const getToken = async () => {
try {
const response = await axios.post(
'https://zoom.us/oauth/token',
qs.stringify({
grant_type: 'account_credentials',
account_id: ZOOM_ACCOUNT_ID
}),
{
headers: {
'Authorization': `Basic ${Buffer.from(
`${ZOOM_CLIENT_ID}:${ZOOM_CLIENT_SECRET}`
).toString('base64')}`,
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);
return response.data; // { access_token, expires_in, scope }
} catch (error) {
throw new Error(`Token request failed: ${error.response?.data?.message || error.message}`);
}
};
const setToken = async (redis, { access_token, expires_in }) => {
// Cache with TTL (10 second buffer before actual expiration)
await redis.setex('access_token', expires_in - 10, access_token);
};
module.exports = { getToken, setToken };4. Token Check Middleware
// middlewares/tokenCheck.js
const redis = require('../configs/redis');
const { getToken, setToken } = require('../utils/token');
const tokenCheck = async (req, res, next) => {
let token = await redis.get('access_token');
// Redis returns null if key doesn't exist
if (!token) {
try {
const { access_token, expires_in, error } = await getToken();
if (error) {
return res.status(401).json({
message: `Authentication failed: ${error.message}`
});
}
// Cache token
await setToken(redis, { access_token, expires_in });
token = access_token;
} catch (err) {
return res.status(500).json({
message: 'Token generation failed',
error: err.message
});
}
}
// Attach token to request for route handlers
req.headerConfig = {
headers: {
Authorization: `Bearer ${token}`
}
};
next();
};
module.exports = { tokenCheck };5. Main Application
// index.js
require('dotenv').config();
const express = require('express');
const redis = require('./configs/redis');
const { tokenCheck } = require('./middlewares/tokenCheck');
const app = express();
const PORT = process.env.PORT || 8080;
// Connect to Redis
(async () => {
await redis.connect();
})();
// Add global middlewares
app.use(express.json());
// Apply tokenCheck to all API routes
app.use('/api/users', tokenCheck, require('./routes/api/users'));
app.use('/api/meetings', tokenCheck, require('./routes/api/meetings'));
const server = app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});
// Graceful shutdown
const cleanup = async () => {
console.log('Shutting down gracefully...');
await redis.del('access_token'); // Clear cached token
server.close(() => {
redis.quit(() => process.exit());
});
};
process.on('SIGTERM', cleanup);
process.on('SIGINT', cleanup);6. Example API Route
// routes/api/users.js
const express = require('express');
const axios = require('axios');
const router = express.Router();
const ZOOM_API_BASE = 'https://api.zoom.us/v2';
// List users
router.get('/', async (req, res) => {
try {
const response = await axios.get(
`${ZOOM_API_BASE}/users`,
req.headerConfig // Token from middleware
);
res.json(response.data);
} catch (error) {
res.status(error.response?.status || 500).json({
message: 'Failed to list users',
error: error.response?.data || error.message
});
}
});
// Get user
router.get('/:userId', async (req, res) => {
try {
const response = await axios.get(
`${ZOOM_API_BASE}/users/${req.params.userId}`,
req.headerConfig
);
res.json(response.data);
} catch (error) {
res.status(error.response?.status || 500).json({
message: 'Failed to get user',
error: error.response?.data || error.message
});
}
});
module.exports = router;7. Environment Variables
# .env
ZOOM_ACCOUNT_ID=your_account_id
ZOOM_CLIENT_ID=your_client_id
ZOOM_CLIENT_SECRET=your_client_secret
REDIS_URL=redis://YOUR_REDIS_HOST:6379
PORT=8080How It Works
Request arrives at protected route (e.g., GET /api/users)
tokenCheck middleware runs:
Checks Redis for cached token
If missing: Requests new token from Zoom
Caches token with TTL (expires_in - 10 seconds)
Token attached to
req.headerConfigRoute handler makes API request with token
Token auto-refreshes when Redis TTL expires
Benefits
✅ Automatic token management - No manual refresh logic ✅ Single token for all requests - Account-wide access ✅ TTL-based expiration - Redis handles cleanup ✅ 10-second buffer - Prevents race conditions ✅ Graceful shutdown - Clears token on exit
Testing
# Start Redis
docker run -d -p 6379:6379 redis
# Start app
npm start
# Test endpoints
API_BASE_URL="http://YOUR_API_HOST:8080"
curl "$API_BASE_URL/api/users"Docker Deployment
# Dockerfile
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "index.js"]# docker-compose.yml
version: '3.8'
services:
redis:
image: redis:7-alpine
ports:
- "6379:6379"
app:
build: .
ports:
- "8080:8080"
environment:
- ZOOM_ACCOUNT_ID=${ZOOM_ACCOUNT_ID}
- ZOOM_CLIENT_ID=${ZOOM_CLIENT_ID}
- ZOOM_CLIENT_SECRET=${ZOOM_CLIENT_SECRET}
- REDIS_URL=redis://redis:6379
depends_on:
- redisRelated
S2S OAuth flow → ../concepts/oauth-flows.md#server-to-server-s2s-oauth
Token lifecycle → ../concepts/token-lifecycle.md
Requirements and compatibility
Written for: OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Zoom
- Publisher
- OpenAI (Official)
- Distributed by
- OpenAI
- Source
- openai/plugins · plugins/zoom/skills/oauth · commit d420d13
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from plugins/zoom/LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Zoom or OpenAI.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download zoom-oauth.zip