# Meeting SDK authorization Zoom Meeting SDKs use [JSON Web Tokens (JWT)](https://datatracker.ietf.org/doc/html/rfc7519) for authorization, Zoom Access Key (ZAK) tokens for authorizing users, and On Behalf Of (OBF) tokens for authorizing apps that join meetings hosted by external accounts. JWTs are the base authorization token and are generated by your app. Apps can join a Zoom meeting or webinar within the app owner's account as a participant or non-login user using only a JWT. Use ZAK and OBF tokens to authorize specific users (ZAK) or apps (OBF). These are retrieved from Zoom using an API call. When ZAK tokens are combined with a JWT, the app can start and join meetings. | App action | Required tokens | Notes | | ------------------------------------------------------------------------------------------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Start or join a Zoom meeting or webinar as a Zoom user | JWT + ZAK token | The ZAK token is associated with a Zoom user. | | Join a Zoom meeting or webinar **within the app owner's account** as a participant or user | JWT | Only the JWT is needed because the meeting or webinar is within the app owner's account and the app is not the host. Users do not need to log into Zoom to join. | | Join a Zoom meeting or webinar **outside the app owner's account** associated with a Zoom user account | JWT + OBF token | On behalf of (OBF) tokens can only be used for joining. They require an associated user with a ZAK token, and that user must already be in the meeting for the join to succeed. | Get started with tokens. - [Generate a Meeting SDK JWT](#generate-a-meeting-sdk-jwt) using your app credentials - [Start Meetings and Webinars with a Zoom user's ZAK token](#start-meetings-and-webinars-with-a-zoom-users-zak-token) - [Join Meetings or Webinars with an OBF token](#join-meetings-and-webinars-with-an-obf-token) The Meeting SDK respects all Zoom meeting and webinar security settings. You must go through additional configuration steps if you want to enable [registration](https://support.zoom.us/hc/en-us/articles/360054446052-Managing-meeting-and-webinar-registration) and [require user authentication](https://support.zoom.us/hc/en-us/articles/4406604615693-Requiring-authentication-to-join-a-meeting-webinar). See instructions in the [platform-specific Meeting SDK documentation](/docs/meeting-sdk/#platforms) for details. > **Requirements for external meeting access** > > To join meetings outside of your developer account, your app must: > > - Be [reviewed by Zoom](/docs/build-flow/submitting-apps-for-review/). > - Authenticate with either a ZAK or On Behalf Of (OBF) token to attribute to a user in a meeting. > > You can: > > - [Publish the approved app on the Zoom App Marketplace](/docs/distribute/). > - Set your app to [unlisted](/docs/build-flow/app-listing/eu-and-discoverability/#app-discoverability). > - Contact the [Integrated Software Vendor (ISV) sales team](https://explore.zoom.us/en/isv/#isv) for other options. ## Generate a Meeting SDK JWT After [getting your Meeting SDK credentials](/docs/meeting-sdk/get-credentials/), generate a JWT for authorizing each request to start or join a Zoom meeting or webinar. You should generate this where you can securely store your Meeting SDK credentials, such as through a backend (server-side) function. > **Meeting SDK JWT credentials** > > To generate the Meeting SDK JWT, use either development or production credentials and your **Client ID** and **Client Secret** app credentials. JWTs consist of three core parts: **Header**, **Payload**, and **Signature**. ### Header The header includes the specification of the signing algorithm and the type of token. | Key | Value | | ----- | ------- | | `alg` | `HS256` | | `typ` | `JWT` | **Sample header** ```javascript { "alg": "HS256", "typ": "JWT" } ``` ### Payload A JWT's payload contains the token's claims, pieces of information about the user, and any required metadata. The necessity of these keys depend on whether you are using Meeting SDK for native platforms (such as Android, iOS, Linux, macOS, Windows) or web. _The asterisks in the necessity column indicate that the value column contains more details._ | Key | Necessity | Value | | ------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `appKey` | Required | Your Client ID (from your app credentials). | | `mn` | Required (web)\* | **Optional for native.** The Zoom meeting or webinar number. | | `role` | Required (web)\* | **Optional for native.** The user role. `0` to specify participant, `1` to specify host. | | `iat` | Required | The token issue timestamp. | | `exp` | Required | The JWT expiration timestamp. Mininum = 1800 seconds greater than the `iat` value, maximum recommended = 48 hours greater than the `iat` value. In epoch format. | | `tokenExp` | Required | SDK authenticated token expiration timestamp. Minimum = 1800 seconds greater than the `iat` value, maximum recommended = 48 hours greater than `iat` value. In epoch format. | | `video_webrtc_mode` | Optional\* | Web only. `0` to disable WebRTC video. `1` to enable WebRTC video. If you do not define this value in the JWT, Zoom determines whether to enable WebRTC video based on various factors determined by Zoom at the time.

\*On browsers that do not support WebRTC video, even if you set this value to `1`, Zoom will use WebAssembly video. | **Sample payload** ```javascript { "appKey": ZOOM_CLIENT_ID, "mn": MEETING_NUMBER, "role": ROLE, "iat": 1646937553, "exp": 1646944753, "tokenExp": 1646944753, "video_webrtc_mode": 0 } ``` > **Cross‑platform Meeting SDK JWT properties** > > For a Meeting SDK JWT that works across the Meeting SDKs for web, native, and wrappers such as Ionic and Electron, include the `appKey`, `exp`, `tokenExp`, `mn`, and `role` properties. The values for `exp` and `tokenExp` will be the same. ### Signature To create a signature for the JWT, you must encrypt the header and payload with the Client Secret through an HMAC SHA256 algorithm. | Value | Description | | -------------------- | ----------------------------------- | | `ZOOM_CLIENT_SECRET` | Your app's Client Secret. Required. | **Sample signature** ```javascript HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), ZOOM_CLIENT_SECRET, ); ``` **Sample Meeting SDK JWT** ```plaintext ``` ### Sample code The Node.js sample code below shows how to generate a Meeting SDK JWT using [jsrsasign](https://www.npmjs.com/package/jsrsasign), an open source cryptographic JavaScript library. For additional JWT libraries and code samples in more languages, see [JWT.io](https://jwt.io/libraries). Commented out properties are optional for all platforms. ```javascript const KJUR = require("jsrsasign"); // https://www.npmjs.com/package/jsrsasign const iat = Math.round(new Date().getTime() / 1000) - 30; const exp = iat + 60 * 60 * 2; const oHeader = { alg: "HS256", typ: "JWT" }; const oPayload = { appKey: process.env.ZOOM_CLIENT_ID, mn: "ZOOM_MEETING_NUMBER", role: 0, iat: iat, exp: exp, tokenExp: exp, // video_webrtc_mode: 0 }; const sHeader = JSON.stringify(oHeader); const sPayload = JSON.stringify(oPayload); const MEETING_SDK_JWT = KJUR.jws.JWS.sign( "HS256", sHeader, sPayload, process.env.ZOOM_CLIENT_SECRET, ); console.log(MEETING_SDK_JWT); ``` ## Test SDK JWT generator Use the [Meeting SDK auth endpoint sample](https://github.com/zoom/meetingsdk-auth-endpoint-sample/) to quickly, easily, and securely generate a Meeting SDK JWT.
Next, start or join meetings or webinars [with a JWT and a ZAK token](#start-meetings-and-webinars-with-a-zoom-users-zak-token) for hosts or attendees, [with only a JWT](#join-meetings-and-webinars-with-the-meeting-sdk-jwt) for API users, or [with a JWT and an OBF token](#join-meetings-and-webinars-with-an-obf-token) for apps outside an app owner's account. ## Start meetings and webinars with a Zoom user's ZAK token Pass in a user's **Zoom Access Key (ZAK)** token to start or join a meeting with their Zoom identity. You can also pass in an [alternative host's](https://support.zoom.us/hc/en-us/articles/208220166-Designating-an-alternative-host) `zak` token to start a webinar. Meeting SDK is a feature of an [OAuth](/docs/integrations/oauth/) app. In the scopes section, select `user:read` or `user:read:admin` to get a Zoom user's ZAK token. _**Note: The user can choose to [Sign Me Out from All Devices](https://support.zoom.com/hc/en/article?id=zm_kb&sysparm_article=KB0060639) from their Zoom profile. This will invalidate their ZAK token.**_ 1. [Implement an OAuth flow](/docs/integrations/oauth/#step-1-request-user-authorization) and request an access token. Use this access token to authorize subsequent API requests. > **OAuth login and app review requirements** > > - Zoom OAuth supports Zoom login, SSO, and signing in with Apple, Google, and Facebook. > - All apps must [go through app review](/docs/distribute/) in order to access meetings or webinars hosted outside the developer account that created the app. Additionally, the user must authorize your app. 2. Use the [Get a user's token](/docs/api/users/#tag/users/get/users/{userId}/token) API to get a ZAK token. When you use this API, tokens expire after: - 2 hours for regular users identified by `userID`. - 90 days for API users created with `custCreate`. You can also set a custom time to live (TTL) for the token, up to a maximum of one year. It's a best practice to get the ZAK token right before you start the meeting. **Endpoint:** `GET https://api.zoom.us/v2/users/{userId}/token?type=zak` **Request Header:** ```json { "Authorization": "Bearer {{ACCESS_TOKEN}}" } ``` **Response Code:** `200 OK` **Response Body:** ```json { "token": "{{ZAK_TOKEN}}" } ``` 3. Pass the ZAK token to the Meeting SDK to start the Zoom user's meeting or webinar. ```kotlin private fun startMeeting(zak: String) { val meetingService = ZoomSDK.getInstance().meetingService val startParams = StartMeetingParamsWithoutLogin().apply { zoomAccessToken = zak meetingNo = "" } meetingService.addListener(meetingServiceListener) val result = meetingService.startMeetingWithParams(this, startParams, StartMeetingOptions()) if (result == MeetingError.MEETING_ERROR_SUCCESS) { // The SDK will attempt to join the meeting. } } ``` ```swift private func startMeeting(zak: String) { let startParams = MobileRTCMeetingStartParam4WithoutLoginUser() startParams.zak = zak startParams.meetingNumber = "" // TODO: Add your meeting number startParams.userID = "" // TODO: Add your display name let meetingService = MobileRTC.shared().getMeetingService() meetingService?.delegate = self let meetingResult = meetingService?.startMeeting(with: startParams) if (meetingResult == .success) { // The SDK will attempt to join the meeting, see onMeetingStateChange callback. } } ``` ```swift private func startMeeting(zak: String) { let startParams = ZoomSDKStartMeetingUseZakElements() startParams.meetingNumber = 0 startParams.zak = zak startParams.userId = "someFakeID" startParams.displayName = "" // TODO: Enter your display name startParams.userType = SDKUserType_EmailLogin let meetingService = zoomSdk.getMeetingService() meetingService?.delegate = self let meetingResult = meetingService?.startMeeting(withZAK: startParams) if (meetingResult == ZoomSDKError_Success) { // The SDK will attempt to join the meeting, see onMeetingStatusChange callback. } } ``` **Web (component view)** ```javascript client.join({ signature: signature, // role in SDK Signature needs to be 1 meetingNumber: meetingNumber, password: password, userName: userName, zak: zakToken, // the host's zak token }); ``` **Web (client view)** ```javascript ZoomMtg.join({ signature: signature, // role in SDK Signature needs to be 1 meetingNumber: meetingNumber, passWord: passWord, userName: userName, zak: zakToken, // the host's zak token success: (success) => { console.log(success); }, error: (error) => { console.log(error); }, }); ``` ```cpp void startMeeting() { // Start meeting for API user with StartParam object ZOOM_SDK_NAMESPACE::StartParam startMeetingParam; // Meeting credentials - replace with actual values const UINT64 meetingNumber = 1234567890; const wchar_t* zakToken = L"ZAK token for user"; const wchar_t* userID = L"User ID or email for user"; const wchar_t* userName = L"Display name for user"; // Provide meeting credentials for API user using StartParam4WithoutLogin ZOOM_SDK_NAMESPACE::StartParam4WithoutLogin startMeetingWithoutLoginParam; startMeetingParam.userType = ZOOM_SDK_NAMESPACE::SDK_UT_WITHOUT_LOGIN; startMeetingWithoutLoginParam.zoomuserType = ZOOM_SDK_NAMESPACE::ZoomUserType_APIUSER; startMeetingWithoutLoginParam.meetingNumber = meetingNumber; // Starting a meeting without the user logging in requires a ZAK token startMeetingWithoutLoginParam.userZAK = zakToken; startMeetingWithoutLoginParam.userID = userID; startMeetingWithoutLoginParam.userName = userName; startMeetingParam.param.withoutloginStart = startMeetingWithoutLoginParam; ZOOM_SDK_NAMESPACE::SDKError startMeetingCallReturnValue(ZOOM_SDK_NAMESPACE::SDKERR_UNKNOWN); startMeetingCallReturnValue = yourMeetingServiceInstance->Start(startMeetingParam); if (startMeetingCallReturnValue == ZOOM_SDK_NAMESPACE::SDKError::SDKERR_SUCCESS) { // Start meeting call succeeded, listen for start meeting result using the onMeetingStatusChanged callback } } ``` ## Join meetings and webinars with the Meeting SDK JWT Now that you have a Meeting SDK JWT, use it to join the Zoom meeting or webinar. Or [start meetings and webinars with a Zoom user's ZAK token](#start-meetings-and-webinars-with-a-zoom-users-zak-token). ```kotlin val listener = object : ZoomSDKInitializeListener { override fun onZoomSDKInitializeResult(p0: Int, p1: Int) { } override fun onZoomAuthIdentityExpired() { } } val initParams = ZoomSDKInitParams().apply { jwtToken = "" // Pass in your Meeting SDK JWT domain = "zoom.us" enableLog = true } ZoomSDK.getInstance().initialize(context, listener, initParams) ``` ```swift let sdk = MobileRTC.shared() let initContext = MobileRTCSDKInitContext() initContext.domain = "https://zoom.us" let initResult = sdk.initialize(initContext) guard let authService = sdk.getAuthService(), initResult else { return } authService.delegate = self authService.jwtToken = "" // Pass in your Meeting SDK JWT authService.sdkAuth() ``` ```swift guard let sdk = ZoomSDK.shared() else { return } let initParams = ZoomSDKInitParams() sdk.initSDK(with: initParams) sdk.zoomDomain = "zoom.us" guard let authService = sdk.getAuthService() else { return } let authContext = ZoomSDKAuthContext() authContext.jwtToken = "" // Pass in your Meeting SDK JWT authService.delegate = self authService.sdkAuth(authContext) ``` **Web (component view)** ```javascript client.join({ signature: signature, // Pass in your Meeting SDK JWT meetingNumber: meetingNumber, password: password, userName: userName, }); ``` **Web (client view)** ```javascript ZoomMtg.join({ signature: signature, // Pass in your Meeting SDK JWT meetingNumber: meetingNumber, passWord: passWord, userName: userName, success: (success) => { console.log(success); }, error: (error) => { console.log(error); }, }); ``` **Windows C++** ```cpp ZOOM_SDK_NAMESPACE::InitParam initParam; initParam.strWebDomain = L"zoom.us"; ZOOM_SDK_NAMESPACE::InitSDK(initParam); ZOOM_SDK_NAMESPACE::IAuthService* authService; ZOOM_SDK_NAMESPACE::CreateAuthService(authService); ZOOM_SDK_NAMESPACE::AuthContext authContext; authContext.jwt_token = L""; // Pass in your Meeting SDK JWT authService->SDKAuth(authContext); ``` **Windows C#** ```csharp ZOOM_SDK_DOTNET_WRAP.InitParam initParams = new ZOOM_SDK_DOTNET_WRAP.InitParam(); initParams.web_domain = "zoom.us" ZOOM_SDK_DOTNET_WRAP.CZoomSDKeDotNetWrap.Instance.Initialize(initParam); ZOOM_SDK_DOTNET_WRAP.AuthContext authParam = new ZOOM_SDK_DOTNET_WRAP.AuthContext(); authParam.jwt_token = ""; // Pass in your Meeting SDK JWT ZOOM_SDK_DOTNET_WRAP.CZoomSDKeDotNetWrap.Instance.GetAuthServiceWrap().SDKAuth(param); ``` ## Join meetings and webinars with an OBF token For apps that need to join the meeting as individual users, pass their OBF token to join a meeting or webinar with its association - verified through OAuth - with a user's Zoom identity. 1. [Implement an OAuth flow](/docs/integrations/oauth/#step-1-request-user-authorization) and request an access token. Use this access token to authorize subsequent API requests. > **OAuth login and app review requirements** > > - Zoom OAuth supports Zoom login, SSO, and signing in with Apple, Google, and Facebook. > - All apps must [go through app review](/docs/distribute/) in order to access meetings or webinars hosted outside the developer account that created the app. Additionally, the user must authorize your app. 2. Use the [Users API](/docs/api/users/#tag/users/get/users/{userId}/token) to get an OBF token. When you use this API, tokens expire after 2 hours. You can also set a custom time to live (TTL) for the token, up to a maximum of one year. It's a best practice to get the OBF token right before you start the meeting. **Endpoint**: `GET https://api.zoom.us/v2/users/{userId}/token?type=onbehalf` **Request Header:** ```json { "Authorization": "Bearer {{ACCESS_TOKEN}}" } ``` **Response Code**: `200 OK` **Response Body**: ```json { "token": "{{OBF_TOKEN}}" } ``` 3. Pass the OBF token to the Meeting SDK to start the Zoom user's meeting or webinar. OBF apps can only join a meeting once their verified associated owner is in a meeting or webinar, and must leave a meeting or webinar when their owner leaves. ```kotlin private fun startMeeting(obf: String) { val meetingService = ZoomSDK.getInstance().meetingService val startParams = StartMeetingParamsWithoutLogin().apply { onBehalfToken = obf meetingNo = "" } meetingService.addListener(meetingServiceListener) val result = meetingService.startMeetingWithParams(this, startParams, StartMeetingOptions()) if (result == MeetingError.MEETING_ERROR_SUCCESS) { // The SDK will attempt to join the meeting. } } ``` ```swift private func startMeeting(obf: String) { let startParams = MobileRTCMeetingStartParam4WithoutLoginUser() startParams.onBehalfToken = obf startParams.meetingNumber = "" // TODO: Add your meeting number startParams.userID = "" // TODO: Add your display name let meetingService = MobileRTC.shared().getMeetingService() meetingService?.delegate = self let meetingResult = meetingService?.startMeeting(with: startParams) if (meetingResult == .success) { // The SDK will attempt to join the meeting, see onMeetingStateChange callback. } } ``` ```swift private func startMeeting(obf: String) { let startParams = ZoomSDKStartMeetingUseObfElements() startParams.meetingNumber = 0 startParams.onBehalfToken = obf startParams.userId = "someFakeID" startParams.displayName = "" // TODO: Enter your display name startParams.userType = SDKUserType_EmailLogin let meetingService = zoomSdk.getMeetingService() meetingService?.delegate = self let meetingResult = meetingService?.startMeeting(withOBF: startParams) if (meetingResult == ZoomSDKError_Success) { // The SDK will attempt to join the meeting, see onMeetingStatusChange callback. } } ``` **Web (component view)** ```javascript client.join({ signature: signature, // role in SDK Signature needs to be 1 meetingNumber: meetingNumber, password: password, userName: userName, obfToken: obfToken, // the host's OBF token }); ``` **Web (client view)** ```javascript ZoomMtg.join({ signature: signature, // role in SDK Signature needs to be 1 meetingNumber: meetingNumber, passWord: passWord, userName: userName, obfToken: obfToken, // the host's OBF token success: (success) => { console.log(success); }, error: (error) => { console.log(error); }, }); ``` ```cpp void startMeeting() { // Start meeting for API user with StartParam object ZOOM_SDK_NAMESPACE::StartParam startMeetingParam; // Meeting credentials - replace with actual values const UINT64 meetingNumber = 1234567890; const wchar_t* zakToken = L"ZAK token for user"; const wchar_t* userID = L"User ID or email for user"; const wchar_t* userName = L"Display name for user"; // Provide meeting credentials for API user using StartParam4WithoutLogin ZOOM_SDK_NAMESPACE::StartParam4WithoutLogin startMeetingWithoutLoginParam; startMeetingParam.userType = ZOOM_SDK_NAMESPACE::SDK_UT_WITHOUT_LOGIN; startMeetingWithoutLoginParam.zoomuserType = ZOOM_SDK_NAMESPACE::ZoomUserType_APIUSER; startMeetingWithoutLoginParam.meetingNumber = meetingNumber; // Starting a meeting without the user logging in with an OBF token startMeetingWithoutLoginParam.onBehalfToken = obfToken; startMeetingWithoutLoginParam.userID = userID; startMeetingWithoutLoginParam.userName = userName; startMeetingParam.param.withoutloginStart = startMeetingWithoutLoginParam; ZOOM_SDK_NAMESPACE::SDKError startMeetingCallReturnValue(ZOOM_SDK_NAMESPACE::SDKERR_UNKNOWN); startMeetingCallReturnValue = yourMeetingServiceInstance->Start(startMeetingParam); if (startMeetingCallReturnValue == ZOOM_SDK_NAMESPACE::SDKError::SDKERR_SUCCESS) { // Start meeting call succeeded, listen for start meeting result using the onMeetingStatusChanged callback } } ``` ## Troubleshooting SDK JWTs If you have issues generating or using your SDK JWT, make sure that: 1. Your developer account is a valid account that allows using Meeting SDK. 2. You're using the credentials from your app details page on the Zoom App Marketplace. 3. Your JWT is signed with the correct Client Secret shown on the Marketplace. 4. The JWT's `exp` value is not greater than 48 hours after the `iat`. 5. The expiry is 1800 seconds or greater than current time. ## Pick a platform and get started **Select a platform** and start a Zoom meeting or webinar!