# Issue with python code to decrypt the app context

**URL:** <https://devforum.zoom.us/t/issue-with-python-code-to-decrypt-the-app-context/105920>\
**Category:** Zoom Apps\
**Tags:** sample-app\
**Created:** [March 17, 2024, 11:51pm UTC](https://devforum.zoom.us/t/issue-with-python-code-to-decrypt-the-app-context/105920 "2024-03-17T23:51:13Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![attentiontag](https://sea2.discourse-cdn.com/flex016/user_avatar/devforum.zoom.us/attentiontag/32/50828_2.png) [@attentiontag](https://devforum.zoom.us/u/attentiontag)\
**Post date:** [March 17, 2024, 11:51pm UTC](https://devforum.zoom.us/t/issue-with-python-code-to-decrypt-the-app-context/105920/1 "2024-03-17T23:51:13Z")

</div>

**Zoom Apps Configuration**  
The app frontend is React + NextJS, and the backend is running Python + Django, with auth purely on our side (no 3rd party). The app is to be embedded in a Zoom desktop.

We are trying the in-client auth feature with the goal to verify & identify the logged in user in Desktop & then issue him Application Auth token pair, so that the user can access Application functionality. Here are the steps:

1. Verify in the frontend from the user is authorized or not. If not authorized, call ZoomSdk.authorize
2. If authorized, get App Context & send it our backend API
3. The backend API decrypts the token (the Zoom app client secret used to decode it also verifies it), identifies the user & issues the application token pair.
4. The frontend continues to listen to:

- userContext changes to see if it needs to authorize again
- periodically refresh the application token pair using the refresh token

1. The frontend gets the application data using the token pair above

I have 2 questions:

1. Is the above flow correct? Specifically - also in this case in the frontend, is ZoomSdk.authorize() or .promptAuthorize() better to call?
2. I’m getting an error when decrypting the Zoom App token in the backend. Logic & sample code is below. Need help debugging this.

**Error?**

```auto
    def decode_base64url(self, input_str):
        # Replace base64url characters with base64 equivalents
        input_str = input_str.replace('-', '+').replace('_', '/')
        # Pad with '=' to make the length a multiple of 4
        padding = len(input_str) % 4
        if padding > 0:
            input_str += '=' * (4 - padding)
        return base64.b64decode(input_str)

    def decrypt_zoom_app_context(self, context, secret_key=None):
        client_secret = settings.ZOOM_PLUGIN.get('SPEAKER').get('CLIENT_SECRET')
        context_bytes = self.decode_base64url(context)

        iv_length = context_bytes[0]

        iv = context_bytes[1:1 + iv_length]
        aad_length = int.from_bytes(context_bytes[1 + iv_length:3 + iv_length], byteorder='little')

        aad = context_bytes[3 + iv_length:3 + iv_length + aad_length]
        cipher_length = int.from_bytes(context_bytes[3 + iv_length + aad_length:7 + iv_length + aad_length], byteorder='little')

        cipher_text = context_bytes[7 + iv_length + aad_length:7 + iv_length + aad_length + cipher_length]
        tag = context_bytes[7 + iv_length + aad_length + cipher_length:]

                derive = HKDF(
            algorithm=hashes.SHA256(),
            length=32,
            salt=None,
            info=b'handshake data',
            backend=default_backend()
        )
        key = derive.derive(client_secret.encode())
        # Setup cipher
        cipher = Cipher(
            algorithms.AES(key),
            modes.GCM(iv, tag),
            backend=default_backend()
        )
        decryptor = cipher.decryptor()
        decrypted = decryptor.update(cipher_text) + decryptor.finalize()

        return json.loads(decrypted.decode('utf-8'))

```

I’m getting the below error in decryptor.finalize()

```auto
    decrypted = decryptor.update(cipher_text) + decryptor.finalize()
                                                ^^^^^^^^^^^^^^^^^^^^
  File "/Users/arvind/venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/ciphers/base.py", line 229, in finalize
    data = self._ctx.finalize()
           ^^^^^^^^^^^^^^^^^^^^
  File "/Users/arvind/venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/ciphers.py", line 200, in finalize
    raise InvalidTag
cryptography.exceptions.InvalidTag

```

**Troubleshooting Routes**  
The intermediate debugging values look reasonable - ie iv has length 12, AAD is 0 length, cipher is 216 chars & tag 16 chars. Base64 encoded input was 335 length, with the decoded one 251.

**How To Reproduce**

Call decrypt\_zoom\_app\_context with this one value I got from zoom -

> DGXwvj7p0Xbh7brIgAAA2AAAAEikLl9deBnPNlaABm9UNSyNOQttdQ9Lr9Nw9XDbhX09MtgaO\_jAZkObkQDwjb13y-fB75FksOvhWJlKA\_jiH5zq18Q4jrNUYigklz\_syAKMDTu\_k5L1eDRX\_iMgjfAiIIh8r9uwzZp\_oqIdiqEHYpm-8EF74B88Dnjx-lDmarqvz8dwi5RvajwqdQJVXDRqVNCOGz9JR90r1Fw75I9AKNDCOw7KeLja96ofIlA3f3tEbAU7ozDBQ63uosCaoqoU0lLi8sb8sRxRdQLaKHgZokyLewHLRNBeuEv\_oQLQO6Qvtp8659xPo84

---

<div class="post-metadata">

**Author:** ![MaxM](https://sea2.discourse-cdn.com/flex016/user_avatar/devforum.zoom.us/maxm/32/42303_2.png) [@MaxM](https://devforum.zoom.us/u/MaxM)\
**Post date:** [March 20, 2024, 1:06am UTC](https://devforum.zoom.us/t/issue-with-python-code-to-decrypt-the-app-context/105920/2 "2024-03-20T01:06:03Z")

</div>

> [@attentiontag](#):
>
> Is the above flow correct? Specifically - also in this case in the frontend, is ZoomSdk.authorize() or .promptAuthorize() better to call?

Yes, this is correct. You only need to use promptAuthorize if the user is not signed in and you are supporting [Guest Mode](https://developers.zoom.us/docs/zoom-apps/guides/guest-mode/#:~:text=To%20nudge%20the,the%20config%20method.).

More information on that function is [here](https://appssdk.zoom.us/classes/ZoomSdk.ZoomSdk.html#promptAuthorize%60).

> [@attentiontag](#):
>
> I’m getting an error when decrypting the Zoom App token in the backend. Logic & sample code is below. Need help debugging this.

We have a code sample on using python to decrypt the context that you can find here:

> **[Zoom Apps Context](https://developers.zoom.us/docs/zoom-apps/zoom-app-context/#decryption-examples)**
>
> The Zoom Developer Platform is an open platform that allows third-party developers to build applications and integrations upon Zoom’s video-first unified communications platform.

Let me know if that helps.

---

<div class="post-metadata">

**Author:** ![attentiontag](https://sea2.discourse-cdn.com/flex016/user_avatar/devforum.zoom.us/attentiontag/32/50828_2.png) [@attentiontag](https://devforum.zoom.us/u/attentiontag)\
**Post date:** [March 22, 2024, 12:39pm UTC](https://devforum.zoom.us/t/issue-with-python-code-to-decrypt-the-app-context/105920/3 "2024-03-22T12:39:57Z")

</div>

> [@MaxM](#):
>
> here

Hi @MaxM great - this is helpful. I’m able to use this code to decode the App context. Quick question - how do I convert the Zoom uids eg - `MPjq6UmhQWWBP4Yuf-FARg` to get the user’s email? Our user ids are emails, and we need this email to look it up.

We don’t have the user’s JWT on the server (not keeping it so far) - so we can’t lookup the the user details via the below API

> url = f"[https://api.zoom.us/v2/users/{user\_uid}](https://api.zoom.us/v2/users/%7Buser_uid%7D)"  
> headers = {  
> “Authorization”: f"Bearer {jwt\_token}",  
> “Content-Type”: “application/json”  
> }

Any other way to translate the uid to emails on the server using the application client id / secrets?  
Thanks  
Arvind

---

<div class="post-metadata">

**Author:** ![MaxM](https://sea2.discourse-cdn.com/flex016/user_avatar/devforum.zoom.us/maxm/32/42303_2.png) [@MaxM](https://devforum.zoom.us/u/MaxM)\
**Post date:** [April 11, 2024, 4:52pm UTC](https://devforum.zoom.us/t/issue-with-python-code-to-decrypt-the-app-context/105920/4 "2024-04-11T16:52:08Z")

</div>

We do not have a method to obtain emails (or any other PII) without the use of our API as that allows for a more granular authorization flow.

If there is a moment when you have the JWT on the server, you can make a request to obtain user information at that point. Otherwise, you would want to securely store the token for use later.
