zoom: add redirectUri config for OAuth
Add redirectUri prompt in onboarding and update docs. Fixes OAuth token exchange by ensuring redirect_uri matches Zoom app settings.
This commit is contained in:
parent
bc109c4ae3
commit
2310030f5f
@ -25,6 +25,7 @@ Status: production-ready plugin for Zoom Team Chat direct messages via Team Chat
|
||||
clientSecret: "YOUR_CLIENT_SECRET",
|
||||
botJid: "YOUR_BOT_JID@xmppdev.zoom.us",
|
||||
secretToken: "YOUR_SECRET_TOKEN",
|
||||
redirectUri: "https://yourdomain.com/api/zoomapp/auth",
|
||||
dm: { policy: "open" }
|
||||
}
|
||||
}
|
||||
@ -81,6 +82,12 @@ Replace `gateway-host` with:
|
||||
Subscribe to these event types:
|
||||
- `bot_notification` - Required for receiving messages
|
||||
|
||||
**Important**: The `redirectUri` in your Moltbot config must EXACTLY match the OAuth Redirect URL configured in your Zoom app. OAuth will fail if these don't match.
|
||||
|
||||
Examples:
|
||||
- Local dev: `https://abc123.ngrok.io/api/zoomapp/auth`
|
||||
- Production: `https://yourdomain.com/api/zoomapp/auth`
|
||||
|
||||
### 3. Install App to Your Account
|
||||
|
||||
1) In app settings, click "Install"
|
||||
@ -100,6 +107,7 @@ Add to `~/.clawdbot/moltbot.json`:
|
||||
clientSecret: "YOUR_CLIENT_SECRET",
|
||||
botJid: "YOUR_BOT_JID@xmppdev.zoom.us",
|
||||
secretToken: "YOUR_SECRET_TOKEN",
|
||||
redirectUri: "https://yourdomain.com/api/zoomapp/auth", // Must match Zoom app OAuth Redirect URL
|
||||
apiHost: "https://zoomdev.us", // Use https://api.zoom.us for production
|
||||
oauthHost: "https://zoomdev.us", // Use https://zoom.us for production
|
||||
dm: {
|
||||
|
||||
@ -129,6 +129,43 @@ export const zoomOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
}),
|
||||
).trim();
|
||||
|
||||
// Prompt for OAuth Redirect URI
|
||||
await prompter.note(
|
||||
[
|
||||
"OAuth Redirect URL is the public URL where Zoom will redirect after installation.",
|
||||
"",
|
||||
"Examples:",
|
||||
"- Local dev: https://abc123.ngrok.io/api/zoomapp/auth (use ngrok http 3001)",
|
||||
"- Production: https://yourdomain.com/api/zoomapp/auth",
|
||||
"",
|
||||
"This MUST exactly match the OAuth Redirect URL configured in your Zoom app.",
|
||||
].join("\n"),
|
||||
"OAuth Redirect URL",
|
||||
);
|
||||
|
||||
const redirectUri = String(
|
||||
await prompter.text({
|
||||
message: "OAuth Redirect URL",
|
||||
placeholder: "https://yourdomain.com/api/zoomapp/auth",
|
||||
validate: (value) => {
|
||||
const trimmed = String(value ?? "").trim();
|
||||
if (!trimmed) return "OAuth Redirect URL is required";
|
||||
try {
|
||||
const url = new URL(trimmed);
|
||||
if (!url.protocol.startsWith("http")) {
|
||||
return "URL must start with http:// or https://";
|
||||
}
|
||||
if (!trimmed.endsWith("/api/zoomapp/auth")) {
|
||||
return "URL should end with /api/zoomapp/auth";
|
||||
}
|
||||
return undefined;
|
||||
} catch {
|
||||
return "Invalid URL format";
|
||||
}
|
||||
},
|
||||
}),
|
||||
).trim();
|
||||
|
||||
// Determine environment from Bot JID
|
||||
const isDev = botJid.includes("@xmppdev");
|
||||
const apiHost = isDev ? "https://zoomdev.us" : "https://api.zoom.us";
|
||||
@ -145,6 +182,7 @@ export const zoomOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
clientSecret,
|
||||
botJid,
|
||||
secretToken,
|
||||
redirectUri,
|
||||
apiHost,
|
||||
oauthHost,
|
||||
},
|
||||
@ -152,19 +190,16 @@ export const zoomOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
};
|
||||
|
||||
// Show URL configuration instructions
|
||||
const gatewayHost = redirectUri.replace(/\/api\/zoomapp\/auth$/, "");
|
||||
await prompter.note(
|
||||
[
|
||||
"IMPORTANT: Configure these URLs in your Zoom app:",
|
||||
"",
|
||||
"Webhook URL (for receiving messages):",
|
||||
" https://gateway-host/webhooks/zoom",
|
||||
` ${gatewayHost}/webhooks/zoom`,
|
||||
"",
|
||||
"OAuth Redirect URL (for app installation):",
|
||||
" https://gateway-host/api/zoomapp/auth",
|
||||
"",
|
||||
"Replace gateway-host with:",
|
||||
"- For local dev: Use ngrok (ngrok http 3001)",
|
||||
"- For production: Your server's public domain",
|
||||
` ${redirectUri}`,
|
||||
"",
|
||||
"Subscribe to event type: bot_notification",
|
||||
].join("\n"),
|
||||
|
||||
@ -7,6 +7,7 @@ export const ZoomConfigSchema = z.object({
|
||||
clientSecret: z.string().optional(),
|
||||
botJid: z.string().optional(),
|
||||
secretToken: z.string().optional(),
|
||||
redirectUri: z.string().optional(),
|
||||
apiHost: z.string().optional(),
|
||||
oauthHost: z.string().optional(),
|
||||
dm: z
|
||||
@ -26,6 +27,7 @@ export type ResolvedZoomAccount = {
|
||||
clientSecret?: string;
|
||||
botJid?: string;
|
||||
secretToken?: string;
|
||||
redirectUri?: string;
|
||||
apiHost: string;
|
||||
oauthHost: string;
|
||||
config: ZoomConfig;
|
||||
@ -46,6 +48,7 @@ export function resolveZoomAccount(params: {
|
||||
clientSecret: config.clientSecret,
|
||||
botJid: config.botJid,
|
||||
secretToken: config.secretToken,
|
||||
redirectUri: config.redirectUri,
|
||||
apiHost: config.apiHost || "https://api.zoom.us",
|
||||
oauthHost: config.oauthHost || "https://zoom.us",
|
||||
config,
|
||||
|
||||
@ -178,7 +178,10 @@ export async function monitorZoomProvider(opts: MonitorZoomOpts = {}): Promise<v
|
||||
const tokenUrl = `${account.oauthHost}/oauth/token`;
|
||||
const tokenParams = new URLSearchParams();
|
||||
tokenParams.set("grant_type", "authorization_code");
|
||||
tokenParams.set("redirect_uri", `${req.protocol}://${req.get("host")}/api/zoomapp/auth`);
|
||||
// Use configured redirectUri or fall back to dynamically built one
|
||||
const redirectUri =
|
||||
account.redirectUri || `${req.protocol}://${req.get("host")}/api/zoomapp/auth`;
|
||||
tokenParams.set("redirect_uri", redirectUri);
|
||||
tokenParams.set("code", code);
|
||||
|
||||
const authHeader = Buffer.from(`${account.clientId}:${account.clientSecret}`).toString(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user