diff --git a/vc_zoom/README.md b/vc_zoom/README.md index 1cf6b0e..4148674 100644 --- a/vc_zoom/README.md +++ b/vc_zoom/README.md @@ -15,6 +15,7 @@ - Adapt to Indico 3.3 changes - Support Python 3.12 +- Drop support for discontinued JWT authentication ### 3.2.5 @@ -151,12 +152,6 @@ The scopes to select when creating the app are: - `webinar:write:admin` (optional, only needed when using webinars) -### Zoom API key/secret (JWT, deprecated) - -Zoom deprecated JWTs in June 2023, existing ones still work but no new ones can be created. -As soon as Zoom fully dropped them, JWT support will also be removed from this plugin. - - ## Intellectual Property Developed by Giovanni Mariano @ **ENEA Frascati**, based on the Vidyo Plugin by the Indico Team at **CERN**. Further diff --git a/vc_zoom/indico_vc_zoom/api/client.py b/vc_zoom/indico_vc_zoom/api/client.py index a72bfad..3cd84c8 100644 --- a/vc_zoom/indico_vc_zoom/api/client.py +++ b/vc_zoom/indico_vc_zoom/api/client.py @@ -7,7 +7,6 @@ import time -import jwt import requests from pytz import utc from requests import Session @@ -172,11 +171,9 @@ class ZoomClient: 'webinar': WebinarComponent } - def __init__(self, api_key, api_secret, account_id, client_id, client_secret, timeout=15): + def __init__(self, account_id, client_id, client_secret, timeout=15): """Create a new Zoom client. - :param api_key: the Zoom JWT API key - :param api_secret: the Zoom JWT API Secret :param account_id: the Zoom Server OAuth Account ID :param client_id: the Zoom Server OAuth Client ID :param client_secret: the Zoom Server OAuth Client Secret @@ -184,8 +181,6 @@ class ZoomClient: """ # Setup the config details config = { - 'api_key': api_key, - 'api_secret': api_secret, 'account_id': account_id, 'client_id': client_id, 'client_secret': client_secret, @@ -217,8 +212,6 @@ class ZoomIndicoClient: def __init__(self): from indico_vc_zoom.plugin import ZoomPlugin self.client = ZoomClient( - ZoomPlugin.settings.get('api_key'), - ZoomPlugin.settings.get('api_secret'), ZoomPlugin.settings.get('account_id'), ZoomPlugin.settings.get('client_id'), ZoomPlugin.settings.get('client_secret'), @@ -262,36 +255,31 @@ def get_zoom_token(config, *, force=False): client_id = config['client_id'] client_secret = config['client_secret'] - if account_id and client_id and client_secret: - ZoomPlugin.logger.debug(f'Using Server-to-Server-OAuth ({force=})') - hash_key = '-'.join((account_id, client_id, client_secret)) - cache_key = f'token-{crc32(hash_key)}' - if not force and (token_data := token_cache.get(cache_key)): - expires_in = int(token_data['expires_at'] - time.time()) - ZoomPlugin.logger.debug('Using token from cache (%s, %ds remaining)', cache_key, expires_in) - return token_data['access_token'], token_data['expires_at'] - try: - resp = requests.post( - 'https://zoom.us/oauth/token', - params={'grant_type': 'account_credentials', 'account_id': account_id}, - auth=(client_id, client_secret) - ) - resp.raise_for_status() - except HTTPError as exc: - ZoomPlugin.logger.error('Could not get zoom token: %s', exc.response.text if exc.response else exc) - raise Exception('Could not get zoom token; please contact an admin if this problem persists.') - token_data = resp.json() - assert 'access_token' in token_data - ZoomPlugin.logger.debug('Got new token from Zoom (expires_in=%s, scope=%s)', token_data['expires_in'], - token_data['scope']) - expires_at = int(time.time() + token_data['expires_in']) - token_data.setdefault('expires_at', expires_at) # zoom doesn't include this. wtf. - token_cache.set(cache_key, token_data, token_data['expires_in']) - return token_data['access_token'], token_data['expires_at'] - elif config['api_key'] and config['api_secret']: - ZoomPlugin.logger.warning('Using JWT (deprecated)') - header = {'alg': 'HS256', 'typ': 'JWT'} - payload = {'iss': config['api_key'], 'exp': int(time.time() + 3600)} - return jwt.encode(payload, config['api_secret'], algorithm='HS256', headers=header), None - else: + if not (account_id and client_id and client_secret): raise Exception('Zoom authentication not configured') + + ZoomPlugin.logger.debug(f'Using Server-to-Server-OAuth ({force=})') + hash_key = '-'.join((account_id, client_id, client_secret)) + cache_key = f'token-{crc32(hash_key)}' + if not force and (token_data := token_cache.get(cache_key)): + expires_in = int(token_data['expires_at'] - time.time()) + ZoomPlugin.logger.debug('Using token from cache (%s, %ds remaining)', cache_key, expires_in) + return token_data['access_token'], token_data['expires_at'] + try: + resp = requests.post( + 'https://zoom.us/oauth/token', + params={'grant_type': 'account_credentials', 'account_id': account_id}, + auth=(client_id, client_secret) + ) + resp.raise_for_status() + except HTTPError as exc: + ZoomPlugin.logger.error('Could not get zoom token: %s', exc.response.text if exc.response else exc) + raise Exception('Could not get zoom token; please contact an admin if this problem persists.') + token_data = resp.json() + assert 'access_token' in token_data + ZoomPlugin.logger.debug('Got new token from Zoom (expires_in=%s, scope=%s)', token_data['expires_in'], + token_data['scope']) + expires_at = int(time.time() + token_data['expires_in']) + token_data.setdefault('expires_at', expires_at) # zoom doesn't include this. wtf. + token_cache.set(cache_key, token_data, token_data['expires_in']) + return token_data['access_token'], token_data['expires_at'] diff --git a/vc_zoom/indico_vc_zoom/plugin.py b/vc_zoom/indico_vc_zoom/plugin.py index 69abd34..86ed274 100644 --- a/vc_zoom/indico_vc_zoom/plugin.py +++ b/vc_zoom/indico_vc_zoom/plugin.py @@ -42,8 +42,7 @@ from indico_vc_zoom.util import (UserLookupMode, ZoomMeetingType, fetch_zoom_mee class PluginSettingsForm(VCPluginSettingsFormBase): _fieldsets = [ - (_('API Credentials (Server-to-Server OAuth)'), ['account_id', 'client_id', 'client_secret', 'webhook_token']), - (_('API Credentials (Legacy JWT, deprecated)'), ['api_key', 'api_secret']), + (_('API Credentials'), ['account_id', 'client_id', 'client_secret', 'webhook_token']), (_('Zoom Account'), ['user_lookup_mode', 'email_domains', 'authenticators', 'enterprise_domain', 'allow_webinars', 'phone_link']), (_('Room Settings'), ['mute_audio', 'mute_host_video', 'mute_participant_video', 'join_before_host', @@ -52,8 +51,6 @@ class PluginSettingsForm(VCPluginSettingsFormBase): (_('Access'), ['managers', 'acl']) ] - api_key = StringField(_('API Key'), []) - api_secret = IndicoPasswordField(_('API Secret'), [], toggle=True) account_id = StringField(_('Account ID'), []) client_id = StringField(_('Client ID'), []) client_secret = IndicoPasswordField(_('Client Secret'), [], toggle=True) @@ -137,8 +134,6 @@ class ZoomPlugin(VCPluginMixin, IndicoPlugin): vc_room_attach_form = VCRoomAttachForm friendly_name = 'Zoom' default_settings = VCPluginMixin.default_settings | { - 'api_key': '', - 'api_secret': '', 'account_id': '', 'client_id': '', 'client_secret': '', diff --git a/vc_zoom/setup.cfg b/vc_zoom/setup.cfg index 9d61925..7841ac5 100644 --- a/vc_zoom/setup.cfg +++ b/vc_zoom/setup.cfg @@ -24,7 +24,6 @@ include_package_data = true python_requires = >=3.9.0, <3.13 install_requires = indico>=3.3.dev0 - PyJWT>=2.0.0,<3 [options.entry_points] indico.plugins =