11from datetime import datetime # generate_token
2+ import re
23from typing import List , Optional # imports List, Optional type hint
34import calendar # generate_token
45import base64 # generate_token
78import hmac # _sign_string
89import hashlib
910from typing import List
11+ import uuid
1012import requests # create_session, archiving
1113import json # archiving
1214import platform # user-agent
1921
2022
2123# compat
22- from six . moves . urllib .parse import urlencode
24+ from urllib .parse import urlencode
2325from six import text_type , u , b , PY3
2426from enum import Enum
2527
@@ -111,6 +113,11 @@ class ArchiveModes(Enum):
111113class Client (object ):
112114 """Use this SDK to create tokens and interface with the server-side portion
113115 of the Opentok API.
116+
117+ You can also interact with this client object with Vonage credentials. Instead of passing
118+ on OpenTok API key and secret, you can pass in a Vonage application ID and private key,
119+ e.g. api_key=VONAGE_APPLICATION_ID, api_secret=VONAGE_PRIVATE_KEY. You do not need to set the API
120+ URL differently, the SDK will set this for you.
114121 """
115122
116123 TOKEN_SENTINEL = "T1=="
@@ -124,11 +131,25 @@ def __init__(
124131 timeout = None ,
125132 app_version = None ,
126133 ):
134+
135+ if isinstance (api_secret , (str , bytes )) and re .search (
136+ "[.][a-zA-Z0-9_]+$" , api_secret
137+ ):
138+ # We have a private key so we assume we are using Vonage credentials
139+ self ._using_vonage = True
140+ self ._api_url = 'https://video.api.vonage.com'
141+ with open (api_secret , "rb" ) as key_file :
142+ self .api_secret = key_file .read ()
143+ else :
144+ # We are using OpenTok credentials
145+ self ._using_vonage = False
146+ self .api_secret = api_secret
147+ self ._api_url = api_url
148+
127149 self .api_key = str (api_key )
128- self .api_secret = api_secret
129150 self .timeout = timeout
130151 self ._proxies = None
131- self .endpoints = Endpoints (api_url , self .api_key )
152+ self .endpoints = Endpoints (self . _api_url , self .api_key )
132153 self ._app_version = __version__ if app_version == None else app_version
133154 self ._user_agent = (
134155 f"OpenTok-Python-SDK/{ self .app_version } python/{ platform .python_version ()} "
@@ -306,24 +327,41 @@ def generate_token(
306327
307328 if use_jwt :
308329 payload = {}
309- payload ['iss' ] = self .api_key
310- payload ['ist' ] = 'project'
330+
331+ payload ['session_id' ] = session_id
332+ payload ['role' ] = role .value
311333 payload ['iat' ] = now
312334 payload ["exp" ] = expire_time
313- payload ['nonce' ] = random .randint (0 , 999999 )
314- payload ['role' ] = role .value
315335 payload ['scope' ] = 'session.connect'
316- payload [ 'session_id' ] = session_id
336+
317337 if initial_layout_class_list :
318338 payload ['initial_layout_class_list' ] = (
319339 initial_layout_class_list_serialized
320340 )
321341 if data :
322342 payload ['connection_data' ] = data
323343
324- headers = {'alg' : 'HS256' , 'typ' : 'JWT' }
344+ if not self ._using_vonage :
345+ payload ['iss' ] = self .api_key
346+ payload ['ist' ] = 'project'
347+ payload ['nonce' ] = random .randint (0 , 999999 )
348+
349+ headers = {'alg' : 'HS256' , 'typ' : 'JWT' }
325350
326- token = encode (payload , self .api_secret , algorithm = "HS256" , headers = headers )
351+ token = encode (
352+ payload , self .api_secret , algorithm = "HS256" , headers = headers
353+ )
354+ else :
355+ payload ['application_id' ] = self .api_key
356+ payload ['jti' ] = str (uuid .uuid4 ())
357+ payload ['subject' ] = 'video'
358+ payload ['acl' ] = {'paths' : {'/session/**' : {}}}
359+
360+ headers = {'alg' : 'RS256' , 'typ' : 'JWT' }
361+
362+ token = encode (
363+ payload , self .api_secret , algorithm = "RS256" , headers = headers
364+ )
327365
328366 return token
329367
@@ -500,39 +538,54 @@ def create_session(
500538 "POST to %r with params %r, headers %r, proxies %r" ,
501539 self .endpoints .get_session_url (),
502540 options ,
503- self .get_headers (),
541+ self .get_json_headers (),
504542 self .proxies ,
505543 )
506- response = requests .post (
507- self .endpoints .get_session_url (),
508- data = options ,
509- headers = self .get_headers (),
510- proxies = self .proxies ,
511- timeout = self .timeout ,
512- )
544+ if not self ._using_vonage :
545+ response = requests .post (
546+ self .endpoints .get_session_url (),
547+ data = options ,
548+ headers = self .get_headers (),
549+ proxies = self .proxies ,
550+ timeout = self .timeout ,
551+ )
552+ else :
553+ headers = self .get_headers ()
554+ headers ['Accept' ] = 'application/json'
555+ response = requests .post (
556+ self .endpoints .get_session_url (),
557+ data = options ,
558+ headers = headers ,
559+ proxies = self .proxies ,
560+ timeout = self .timeout ,
561+ )
513562 response .encoding = "utf-8"
514-
515563 if response .status_code == 403 :
516564 raise AuthError ("Failed to create session, invalid credentials" )
517565 if not response .content :
518566 raise RequestError ()
519- dom = xmldom .parseString (response .content .decode ("utf-8" ))
520567 except Exception as e :
521568 raise RequestError ("Failed to create session: %s" % str (e ))
522569
523570 try :
524- error = dom .getElementsByTagName ("error" )
525- if error :
526- error = error [0 ]
527- raise AuthError (
528- "Failed to create session (code=%s): %s"
529- % (
530- error .attributes ["code" ].value ,
531- error .firstChild .attributes ["message" ].value ,
571+ content_type = response .headers ["Content-Type" ]
572+ if content_type != "application/json" :
573+ dom = xmldom .parseString (response .content .decode ("utf-8" ))
574+ error = dom .getElementsByTagName ("error" )
575+ if error :
576+ error = error [0 ]
577+ raise AuthError (
578+ "Failed to create session (code=%s): %s"
579+ % (
580+ error .attributes ["code" ].value ,
581+ error .firstChild .attributes ["message" ].value ,
582+ )
532583 )
584+ session_id = (
585+ dom .getElementsByTagName ("session_id" )[0 ].childNodes [0 ].nodeValue
533586 )
534-
535- session_id = dom . getElementsByTagName ( "session_id" )[0 ]. childNodes [ 0 ]. nodeValue
587+ else :
588+ session_id = response . json ( )[0 ][ "session_id" ]
536589 return Session (
537590 self ,
538591 session_id ,
@@ -546,12 +599,18 @@ def create_session(
546599
547600 def get_headers (self ):
548601 """For internal use."""
602+ if not self ._using_vonage :
603+ return {
604+ "User-Agent" : "OpenTok-Python-SDK/"
605+ + self .app_version
606+ + " python/"
607+ + platform .python_version (),
608+ "X-OPENTOK-AUTH" : self ._create_jwt_auth_header (),
609+ }
549610 return {
550- "User-Agent" : "OpenTok-Python-SDK/"
551- + self .app_version
552- + " python/"
553- + platform .python_version (),
554- "X-OPENTOK-AUTH" : self ._create_jwt_auth_header (),
611+ "User-Agent" : self .user_agent + " OpenTok-With-Vonage-API-Backend" ,
612+ "Authorization" : "Bearer " + self ._create_jwt_auth_header (),
613+ "Accept" : "application/json" ,
555614 }
556615
557616 def headers (self ):
@@ -2090,14 +2149,21 @@ def _sign_string(self, string, secret):
20902149 def _create_jwt_auth_header (self ):
20912150 payload = {
20922151 "ist" : "project" ,
2093- "iss" : self .api_key ,
20942152 "iat" : int (time .time ()), # current time in unix time (seconds)
20952153 "exp" : int (time .time ())
20962154 + (60 * self ._jwt_livetime ), # 3 minutes in the future (seconds)
2097- "jti" : "{0}" .format (0 , random .random ()),
20982155 }
20992156
2100- return encode (payload , self .api_secret , algorithm = "HS256" )
2157+ if not self ._using_vonage :
2158+ payload ["iss" ] = self .api_key
2159+ payload ["jti" ] = str (random .random ())
2160+ return encode (payload , self .api_secret , algorithm = "HS256" )
2161+
2162+ payload ["application_id" ] = self .api_key
2163+ payload ["jti" ] = str (uuid .uuid4 ())
2164+ headers = {"typ" : "JWT" , "alg" : "RS256" }
2165+
2166+ return encode (payload , self .api_secret , algorithm = 'RS256' , headers = headers )
21012167
21022168 def mute_all (
21032169 self , session_id : str , excludedStreamIds : Optional [List [str ]]
0 commit comments