Enterprise video SDK integrations frequently expose critical security vulnerabilities. Client-side secrets, unvalidated webhooks, and bypassed authentication layers create attack vectors that persist in production systems. These implementations typically function correctly during development, but fail security audits and present significant risks in enterprise environments.
We’ve built a real-time video platform architecture that addresses these fundamental security gaps. The implementation uses the Zoom Meeting SDK as the video foundation, chosen for its comprehensive enterprise feature set, embedded meeting capabilities, and mature security controls. It also leverages OKTA for enterprise identity management and Auth0 for flexible authentication.
This architecture demonstrates zero-trust security principles applied to video SDK integration. Every component assumes potential compromise. Secrets remain server-side. Authentication flows through enterprise identity providers. Authorization decisions use cryptographically signed claims rather than database lookups.
The complete implementation is available on the WebRTC.ventures Github.
The Founding Principle: Never Trust, Always Verify
Imagine, for a moment, that you hand your house keys to every visitor who walks through your door, trusting them to only use those keys when appropriate. This scenario sounds absurd, yet it’s precisely what happens when developers expose SDK secrets or OAuth credentials to client applications.
In our architecture, we treat secrets with the reverence they deserve. The Zoom Meeting SDK secret, OAuth credentials, and webhook tokens are digital keys to the castle that never leave the secure confines of our server environment. They reside exclusively in environment variables, fed by secure secrets vaults such as AWS Secrets Manager or HashiCorp Vault, and accessed only by server-side processes that have been thoroughly vetted and authenticated.
When a client needs to join a Zoom meeting, it doesn’t receive a key. Instead, it receives a temporary pass in the form of a cryptographically signed token that grants specific, time-limited access to a particular meeting.
Here’s how we generate these secure, temporary signatures on the server:
// server/src/zoom/sdkSig.ts
import { Router } from "express";
import { KJUR } from "jsrsasign";
export const sdkSig = Router();
sdkSig.post("/", (req, res) => {
const { meetingNumber, role } = req.body || {};
// Validate inputs server-side
if (!meetingNumber || role === undefined) {
return res.status(400).json({ error: "meetingNumber and role required" });
}
// Time-box the signature: 2-hour expiry
const iat = Math.floor(Date.now() / 1000);
const exp = iat + 60 * 60 * 2; // Expire in exactly 2 hours
const oPayload = {
sdkKey: process.env.ZOOM_SDK_KEY,
mn: meetingNumber,
role,
iat,
exp,
tokenExp: exp,
};
// Generate signature with SDK SECRET (never exposed to client)
const signature = KJUR.jws.JWS.sign(
"HS256",
JSON.stringify({ alg: "HS256", typ: "JWT" }),
JSON.stringify(oPayload),
process.env.ZOOM_SDK_SECRET // Secret stays server-side
);
return res.json({ signature }); // Only return the signature
});
This signature, generated server-side using the closely guarded SDK secret, becomes worthless after two hours. This ensures that even if intercepted, its value diminishes rapidly.
The OKTA Identity Layer
Who gets these temporary passes? This is where OKTA enters our narrative as the trusted gatekeeper. When users attempt to access our system, they’re redirected to OKTA’s secure authentication infrastructure.
Here’s how we protect our endpoints with Auth0/OKTA JWT validation:
//server/src/auth/auth0.ts
import { auth } from "express-oauth2-jwt-bearer";
export const checkJwt = auth({
audience: process.env.AUTH0_AUDIENCE,
issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}/`,
});
// In server/src/index.ts - Protected endpoint
app.use("/api/sdk-signature", checkJwt, sdkSig);
This makes sure every user that wants to access a sensitive route to go through OKTA, which doesn’t just tell us who someone is—it tells us their story. Through rich claims embedded in the authentication token, we learn about their department, their role, their group memberships, and their organizational hierarchy.
The Authentication Ballet: OKTA, Auth0, and JWT Orchestration
Picture a carefully choreographed ballet where each dancer knows their role perfectly. This is our authentication flow. It is a seamless dance between OKTA, Auth0, and our application. When a user clicks “Login with SSO,” they begin a journey that traverses multiple security checkpoints.
Here’s how the frontend initiates this secure authentication flow:
//web/src/App.tsx
import { Auth0Provider } from "@auth0/auth0-react";
export default function App() {
return (
<Auth0Provider
domain={import.meta.env.VITE_AUTH0_DOMAIN}
clientId={import.meta.env.VITE_AUTH0_CLIENT_ID}
authorizationParams={{
redirect_uri: window.location.origin,
// Critical for API access
audience: import.meta.env.VITE_AUTH0_AUDIENCE,
}}
>
<Router>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
</Router>
</Auth0Provider>
);
}
Behind the Scenes: Auth0 receives the initial request. Recognizing the enterprise configuration, it hands off to OKTA. OKTA validates credentials against the organization’s directory services, enforces security policies, and challenges with multi-factor authentication. Upon success, it generates a JSON Web Token (JWT) containing not just identity, but the entire organizational context.
The following diagram illustrates the process:
This JWT carries claims about the user’s groups, department, and organization. Every API call includes this token, and every server endpoint validates these claims to make authorization decisions.
Here’s how the client uses these secure tokens:
//web/src/pages/Home.tsx
const startMeeting = async () => {
const token = await getAccessTokenSilently(); // Get OKTA-enriched token
// Request signature with OKTA token for authorization
const resSignature = await fetch(
"http://localhost:4000/api/sdk-signature",
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`, // Contains OKTA claims
"Content-Type": "application/json",
},
body: JSON.stringify({ meetingNumber: data.id, role: 1 }),
}
);
const { signature } = await resSignature.json();
// Client receives only the signature, never the secret
};
The Elegance of Claims-Based Authorization
Traditional authorization relies on database lookups for every request. Our OKTA-integrated approach is fundamentally different. Authorization information arrives with every request, embedded in cryptographically verified JWT claims.
When our server receives a request, it doesn’t need to query a database. The authorization check is a simple cryptographic validation followed by a claim examination.
Multi-Tenancy Through Identity
This approach also allows us to maintain proper isolation between users from multiple organizations. Our OKTA integration provides this naturally through organizational claims embedded in every token.
Here’s how we use S2S OAuth tokens to create meetings with organizational context:
//server/src/zoom/meetings.ts
import { getS2SToken } from "./s2s.js";
// POST /api/meetings - Create meeting with org context
meetings.post("/", async (req, res) => {
try {
const { userId = "me", ...body } = req.body || {};
const token = await getS2SToken(); // Get S2S token
const r = await axios.post(
`https://api.zoom.us/v2/users/${userId}/meetings`,
body,
{
headers: { Authorization: `Bearer ${token}` },
}
);
res.status(201).json(r.data);
} catch (e: any) {
// Never expose token or internal errors to client
res
.status(e?.response?.status || 500)
.json({ error: "zoom_create_failed" });
}
});
When a user from “Acme Corp” creates a meeting, that meeting becomes part of Acme Corp’s meeting space. Users from other organizations cannot see or modify it without explicit invitation.
This isn’t enforced through complex database queries: it’s a natural consequence of claims-based authorization. The beauty becomes apparent at scale: perfect isolation regardless of organization size.
The Speed of Trust
It is a common misconception that robust security necessarily means degraded performance. Our architecture proves otherwise.
- By embedding authorization in JWT tokens, we eliminate database lookups.
- By caching S2S OAuth tokens, we minimize authentication overhead.
- By validating webhook signatures efficiently, we process events in real-time. (More on this in a minute!)
The result is a system that handles authentication and authorization in milliseconds, not seconds. Users experience instantaneous permission checks, immediate meeting access, and real-time updates—all while maintaining enterprise-grade security.
Scaling with Confidence
As organizations grow, our security model scales linearly, not exponentially.
Adding users means adding them to OKTA groups. Creating organizational boundaries requires configuring OKTA organizations. Implementing new permission levels involves defining new groups. The core security architecture remains constant, proven, and performant.
Time-Boxing and the Principle of Least Privilege
Every signature we generate carries within it the seeds of its own expiration. Like Cinderella’s carriage turning back into a pumpkin at midnight, our meeting signatures become invalid exactly two hours after their creation.
This isn’t arbitrary: it’s a carefully considered balance between security and usability. The signature validation happens server-side, where we can enforce role restrictions based on OKTA claims. A user requesting host privileges doesn’t automatically receive them—the server validates their group membership first.
Scope Minimization in S2S OAuth
Server-to-Server OAuth tokens represent the application’s identity. We apply the principle of least privilege with surgical precision, requesting only the minimum scopes necessary:
// server/src/zoom/s2s.ts
import axios from "axios";
let cached: { access_token: string; expires_at: number } | null = null;
export async function getS2SToken() {
const now = Date.now() / 1000;
// Use cached token if still valid (with 60-second buffer)
if (cached && cached.expires_at - 60 > now) {
return cached.access_token;
}
// Request new token with minimal scopes (configured in Zoom app)
const res = await axios.post("https://api.zoom.us/oauth/token", null, {
params: {
grant_type: "account_credentials",
account_id: process.env.ZOOM_ACCOUNT_ID,
},
auth: {
username: process.env.ZOOM_CLIENT_ID!,
password: process.env.ZOOM_CLIENT_SECRET!, // Never exposed
},
});
// Cache token with expiration
cached = {
access_token: res.data.access_token,
expires_at: now + res.data.expires_in,
};
return cached.access_token;
}
Our application requests only two scopes from Zoom: meeting:read and meeting:write. We don’t ask for user management capabilities or administrative privileges we don’t need. Every additional scope represents an expanded attack surface.
Security isn’t about making things impossible to breach. It’s about making breaches so difficult, so time-consuming, and so detectable that attackers move on to easier targets.
Webhook Security: Validating the Messenger
Webhooks present a unique security challenge. Unlike our other endpoints, protected behind layers of authentication, webhook endpoints must be publicly accessible. The solution lies in cryptographic signatures—every legitimate webhook from Zoom includes an HMAC signature proving authenticity.
Here’s how we validate webhook authenticity with forensic precision:
//server/src/zoom/webhook.ts
import crypto from "crypto";
import express from "express";
// Capture raw body for signature verification
function rawBodySaver(req: Request, res: Response, buf: Buffer) {
(req as any).rawBody = buf.toString("utf8");
}
export const jsonWithRaw = express.json({ verify: rawBodySaver });
webhook.post("/", (req: Request, res: Response) => {
const secret = process.env.ZOOM_WEBHOOK_SECRET_TOKEN!;
const ts = req.headers["x-zm-request-timestamp"];
const rawBody = req.rawBody;
// Step 1: Verify webhook signature
const message = `v0:${ts}:${rawBody}`;
const hash = crypto
.createHmac("sha256", secret)
.update(message)
.digest("hex");
const expected = `v0=${hash}`;
if (req.headers["x-zm-signature"] !== expected) {
return res.status(401).send("Invalid Zoom signature");
}
// Step 2: Handle URL validation challenge
if (body?.event === "endpoint.url_validation") {
const plain = body?.payload?.plainToken;
const encryptedToken = crypto
.createHmac("sha256", secret)
.update(plain)
.digest("hex");
// Must respond within ~3 seconds
return res.json({ plainToken: plain, encryptedToken });
}
// Process verified webhook events...
res.sendStatus(200);
});
We preserve the raw request body exactly as received, compute what the signature should be using our secret, and only proceed if the signatures match perfectly. This ensures the message genuinely originated from Zoom and hasn’t been tampered with.
Audit Trails and Compliance: The Silent Witness
For companies looking to comply with regulations, it’s not enough to say you are secure. You must be able to prove it. Our comprehensive audit logging serves as a silent witness to every significant action in the system.
Each audit entry tells a story: “Alice from Marketing (Acme Corp) created a 60-minute meeting with recording enabled at 2:30 PM EST.” These human-readable narratives make security investigations, compliance audits, and troubleshooting far more manageable.
The OKTA Audit Advantage
Because authentication flows through OKTA, we gain access to OKTA’s enterprise-grade audit capabilities. Failed login attempts, password resets, MFA challenges—all captured in OKTA’s audit logs, providing a complete security narrative across the authentication lifecycle.
This dual-layer audit approach—OKTA for authentication events, our application for authorization and action events—creates a comprehensive security narrative that satisfies even the most stringent compliance requirements.
Next you can find some of the graphs in OKTA dashboard:
Secure Zoom Meeting SDK Implementation Demo
To see this in action, next you will find a quick demo. In the video below, you’ll look at how our Zoom integration works, showing how it delivers a seamless user experience while keeping security front and center.
Best Practices for Secure Enterprise Authentication
Here are some insights and key learning tips from the trenches:
- Technology alone doesn’t create security. People do. Our most important lesson: security measures must be transparent to users when possible, and understandable when not.
Key Learning: Users who understand why they’re authenticating through OKTA are more likely to comply with security policies. Developers who understand why secrets must stay server-side are less likely to accidentally expose them.
- Secure defaults are more valuable than extensive documentation. When our system automatically assigns participant roles to non-host users, it prevents privilege escalation without requiring developers to remember to check permissions.
Key Learning: Make the secure path the easy path. Developers should have to work harder to do something insecure than to do something secure.
- No single security measure is perfect, but layers of imperfect measures can create robust security. OKTA might have a vulnerability, but it would need to be combined with a JWT validation bypass to be exploitable. A meeting signature might be intercepted, but it’s useless without the meeting password and expires in two hours anyway.
Key Learning: No single measure can guarantee safety, but combining multiple layers of security creates a resilient defense that’s far harder to break.
Security isn’t a destination. It’s a journey.
The system we’ve built represents current best practices, but the threat landscape constantly evolves. New vulnerabilities are discovered, new attack vectors emerge, and new compliance requirements arise.
What remains constant is WebRTC.ventures’ commitment to fundamental security principles: never trust the client application, validate everything, minimize privileges, audit comprehensively, and maintain defense in depth. These principles, embodied in our OKTA-integrated, zero-trust architecture, provide a solid foundation for whatever security challenges the future may bring.
As we continue this journey, we’re reminded that the best security is invisible to authorized users but insurmountable to attackers. Through the careful integration of OKTA for identity, Auth0 for flexible authentication, and Zoom Meeting SDK for powerful video capabilities—all wrapped in layers of cryptographic validation and audit logging—we’ve created a system that achieves this ideal.
The result isn’t just a secure video integration—it’s a blueprint for building enterprise applications that refuse to compromise between functionality and security, proving that with thoughtful architecture and careful implementation, we can have both.
Ready to Build Your Secure Zoom Meeting SDK Integration?
Don’t risk your enterprise security with DIY implementations. Contact the experts at WebRTC.ventures today.
Further Reading:
- WebRTC Security in 2025: Protocols, Vulnerabilities, and Best Practices
- Embed or Create? Zoom Web SDK Guide: Meeting vs Video
- Zoom Developer Summit 2025: RTMS, Vision-Based RAG, Secure CX & Next-Gen Dev Tools
- Getting Started with WebRTC in the Zoom Video SDK for Web Applications
- Zoom’s WebRTC-Powered Video SDK: A Powerful Addition to the CPaaS Landscape