Skill 126 · Zoom Meeting SDK Windows
Subchapter 126.9
examples/authentication-pattern.mdMarkdown21 KBView on GitHub
Complete guide to authenticating with the Zoom Windows SDK using JWT tokens.
Authentication is the first required step before joining meetings. The SDK uses JWT (JSON Web Token) authentication for Meeting SDK apps.
1. Initialize SDK (InitSDK)
2. [OPTIONAL] Register Network Connection Handler for proxy detection
2a. Wait for onProxyDetectComplete() callback
3. Create Auth Service (CreateAuthService)
4. Set Event Listener (SetEvent)
5. Call SDKAuth with JWT token
6. Process Windows messages (CRITICAL!)
7. Wait for onAuthenticationReturn callback
8. Create Meeting Service (CreateMeetingService)
9. Join/Start meeting
10. Wait for MEETING_STATUS_INMEETING callback
11. NOW safe to use controllers (GetMeetingAudioController, etc.)┌──────────────┐ InitSDK() ┌──────────────┐
│ UNINITIALIZED │ ─────────────► │ INITIALIZED │
└──────────────┘ └───────┬──────┘
│
│ CreateAuthService() + SDKAuth()
▼
┌──────────────┐ onAuthenticationReturn ┌──────────────┐
│ MEETING │ ◄─────────────────── │ AUTHENTICATING │
│ READY │ (AUTHRET_SUCCESS) └──────────────┘
└───────┬──────┘
│
│ Join() or Start()
▼
┌──────────────┐ onMeetingStatusChanged ┌──────────────┐
│ IN MEETING │ ◄─────────────────── │ JOINING │
│ (Controllers OK)│ (MEETING_STATUS_ └──────────────┘
└──────────────┘ INMEETING)#include <windows.h>
#include <cstdint>
#include <zoom_sdk.h>
#include <auth_service_interface.h>
#include <iostream>
#include <chrono>
#include <thread>
using namespace ZOOM_SDK_NAMESPACE;
// Global state
bool g_authenticated = false;
bool g_exit = false;
IAuthService* g_authService = nullptr;
If your app needs to work behind corporate proxies, register the network connection handler after InitSDK but before authentication:
#include <network_connection_handler_interface.h>
class MyNetworkHandler : public INetworkConnectionHandler {
public:
void onProxyDetectComplete() override {
std::cout << "[NETWORK] Proxy detection complete" << std::endl;
// NOW safe to proceed with authentication
g_proxyDetected = true;
}
void onProxySettingNotification(IProxySettingHandler* handler) override {
// Handle proxy settings if needed
std::cout << "[NETWORK] Proxy settings notification" << std::endl;
}
void onSSLCertVerifyNotification(ISSLCertVerificationHandler* handler) override {
// Handle SSL cert verification if needed
std::cout << "[NETWORK] SSL cert verification" << std::endl;
}
};
bool WaitForProxyDetection() {
// Create network helper
INetworkConnectionHelper* networkHelper = nullptr;
CreateNetworkConnectionHelper(&networkHelper);
if (networkHelper) {
networkHelper->RegisterNetworkConnectionHandler(new MyNetworkHandler());
// Wait for onProxyDetectComplete callback
while (!g_proxyDetected) {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return true;
}
return false;
}When to use: Only needed if you’re behind a corporate proxy or need SSL certificate handling. Most apps can skip this step.
Valid JWT token:
eyJ.) separating three partseyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBLZXk...#include <json/json.h>
#include <fstream>
bool LoadJWTFromConfig(std::wstring& jwt_token) {
std::ifstream f("config.json");
if (!f.is_open()) {
std::cerr << "ERROR: config.json not found" << std::endl;
return false;
}
Json::Value config;
try {
f >> config;
} catch (const std::exception& e) {
std::cerr << "ERROR: Failed to parse config.json: " << e.what() << std::endl;
return false;
}
if (config["sdk_jwt"].empty()) {
std::cerr << "ERROR: sdk_jwt not found in config.json" << std::endl;
return false;
}
std::string jwt_str = config["sdk_jwt"].asString();
jwt_token = std::wstring(jwt_str.begin(), jwt_str.end());
return true;
}config.json:
{
"sdk_jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"meeting_number": "1234567890",
"passcode": "meeting_password"
}| Code | Enum | Meaning | Solution |
|---|---|---|---|
| 0 | AUTHRET_SUCCESS | ✓ Success | Continue to join meeting |
| 1 | AUTHRET_KEYORSECRETEMPTY | SDK Key/Secret empty | Check JWT token |
| 3 | AUTHRET_JWTTOKENWRONG | Invalid JWT | Regenerate token |
| 4 | AUTHRET_OVERTIME | Request timeout | Check network |
| 5 | AUTHRET_NETWORKISSUE | Network problem | Check firewall/internet |
| 7 | AUTHRET_CLIENT_INCOMPATIBLE | SDK version mismatch | Update SDK |
| 10 | AUTHRET_JWTTOKENEXPIRED | JWT expired | Generate fresh token |
Symptom: Waiting forever, no callback received
Causes:
Debug:
void onAuthenticationReturn(AuthResult ret) override {
std::cout << "CALLBACK FIRED! Result: " << ret << std::endl; // Does this print?
}If you never see “CALLBACK FIRED!”, you’re not processing Windows messages!
Symptom: AUTHRET_JWTTOKENWRONG (code 3)
Causes:
Solution:
Symptom: AUTHRET_NETWORKISSUE (code 5) or timeout
Solutions:
bool ValidateJWT(const std::wstring& jwt_token) {
// Check length
if (jwt_token.length() < 100) {
std::cerr << "JWT token too short: " << jwt_token.length() << std::endl;
return false;
}
// Check starts with "eyJ"
if (jwt_token.substr(0, 3) != L"eyJ") {
std::cerr << "JWT token doesn't start with 'eyJ'" << std::endl;
return false;
}
// Check has two dots (three parts)
int dotCount = 0;
for (wchar_t c : jwt_token) {
if (c == L'.') dotCount++;
}
if (dotCount != 2) {
std::cerr << "JWT token should have 2 dots, found: " << dotCount << std::endl;
return false;
}
return true;
}bool WaitForAuthenticationWithProgress(int timeoutSeconds = 30) {
auto startTime = std::chrono::steady_clock::now();
int lastProgressSeconds = 0;
while (!g_authenticated && !g_exit) {
// Process messages
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Show progress every 5 seconds
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::steady_clock::now() - startTime).count();
if (elapsed > lastProgressSeconds && elapsed % 5 == 0) {
std::cout << "Still waiting... (" << elapsed << "s)" << std::endl;
lastProgressSeconds = elapsed;
}
if (elapsed >= timeoutSeconds) {
return false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return g_authenticated;
}void Cleanup() {
if (g_authService) {
DestroyAuthService(g_authService);
g_authService = nullptr;
}
CleanUPSDK();
}
int main() {
if (!InitializeSDK()) {
return 1;
}
if (!AuthenticateSDK(jwt_token)) {
Cleanup(); // Always cleanup on failure
return 1;
}
if (!WaitForAuthentication()) {
Cleanup();
return 1;
}
// ... use SDK ...
Cleanup(); // Cleanup on success too
return 0;
}Once authenticated, create the meeting service and join:
#include <meeting_service_interface.h>
IMeetingService* g_meetingService = nullptr;
bool g_inMeeting = false;
class MyMeetingListener : public IMeetingServiceEvent {
public:
void onMeetingStatusChanged(MeetingStatus status, int iResult) override {
std::cout << "[MEETING] Status changed: " << status <<
| When | Controllers Available? |
|---|---|
Before MEETING_STATUS_INMEETING | NO - Returns nullptr |
After MEETING_STATUS_INMEETING | YES - Safe to use |
After MEETING_STATUS_ENDED | NO - Pointers invalid |
Common mistake: Getting controllers before joining. ALWAYS wait for the MEETING_STATUS_INMEETING callback!
std::cout << "JWT length: " << jwt_token.length() << std::endl;
std::cout << "JWT preview: " << jwt_token.substr(0, 30) << "..." << std::endl;Expected output:
JWT length: 358
JWT preview: eyJhbGciOiJIUzI1NiIsInR5cCI...SDKError err = g_authService->SDKAuth(authContext);
std::cout << "SDKAuth returned: " << err << std::endl;0 (SDKERR_SUCCESS): Good, wait for callbackAdd logging in onAuthenticationReturn:
void onAuthenticationReturn(AuthResult ret) override {
std::cout << "*** CALLBACK RECEIVED ***" << std::endl; // First line!
// ... rest of code ...
}If you never see “CALLBACK RECEIVED”, you need a message loop!
// Before SDKAuth
std::cout << "Testing network..." << std::endl;
// Try to access zoom.us or check internet connection