33{ {> partial_header} }
44
55
6+ import time
7+ import base64
68import copy
79import http.client as httplib
810import logging
@@ -11,7 +13,7 @@ from logging import FileHandler
1113import multiprocessing
1214{ {/async} }
1315import sys
14- from typing import Any, Callable, ClassVar, Dict, List, Literal, Optional, TypedDict, Union
16+ from typing import Any, ClassVar, Dict, List, Literal, Optional, TypedDict, Union
1517from typing_extensions import NotRequired, Self
1618
1719import urllib3
@@ -167,11 +169,8 @@ class Configuration:
167169 :param username: Username for HTTP basic authentication.
168170 :param password: Password for HTTP basic authentication.
169171 :param access_token: Access token.
170- :param get_access_token: Function to return a string access token.
171- The function takes any arguments.
172- If a function is provided, it will be called every time
173- an access token is required (i.e. for each request).
174- This is useful for refreshing expired tokens.
172+ :param client_id: Client ID for OAuth2 authentication.
173+ :param client_secret: Client Secret for OAuth2 authentication.
175174{ {#hasHttpSignatureMethods} }
176175 :param signing_info: Configuration parameters for the HTTP signature security scheme.
177176 Must be an instance of { {{packageName} }}.signing.HttpSigningConfiguration
@@ -191,6 +190,8 @@ class Configuration:
191190 :param retries: Number of retries for API requests.
192191 :param ca_cert_data: verify the peer using concatenated CA certificate data
193192 in PEM (str) or DER (bytes) format.
193+ :param cert_file: the path to a client certificate file, for mTLS.
194+ :param key_file: the path to a client key file, for mTLS.
194195
195196{ {#hasAuthMethods} }
196197 :Example:
@@ -287,7 +288,8 @@ conf = {{{packageName}}}.Configuration(
287288 username: Optional[str]=None,
288289 password: Optional[str]=None,
289290 access_token: Optional[str]=None,
290- get_access_token: Optional[Callable[..., str]]=None,
291+ client_id: Optional[str]=None,
292+ client_secret: Optional[str]=None,
291293{ {#hasHttpSignatureMethods} }
292294 signing_info: Optional[HttpSigningConfiguration]=None,
293295{ {/hasHttpSignatureMethods} }
@@ -299,6 +301,8 @@ conf = {{{packageName}}}.Configuration(
299301 ssl_ca_cert: Optional[str]=None,
300302 retries: Optional[int] = None,
301303 ca_cert_data: Optional[Union[str, bytes]] = None,
304+ cert_file: Optional[str]=None,
305+ key_file: Optional[str]=None,
302306 *,
303307 debug: Optional[bool] = None,
304308 ) -> None:
@@ -344,8 +348,17 @@ conf = {{{packageName}}}.Configuration(
344348 self.access_token = access_token
345349 """Access token
346350 """
347- self.get_access_token = get_access_token
348- """Function to return a string access token.
351+ self.client_id = client_id
352+ """Client ID for OAuth2 authentication
353+ """
354+ self.client_secret = client_secret
355+ """Client Secret for OAuth2 authentication
356+ """
357+ self.temp_access_token = None
358+ """Temporary access token for OAuth2
359+ """
360+ self.temp_access_token_expires_at = 0
361+ """Expiration time of the temporary access token
349362 """
350363{ {#hasHttpSignatureMethods} }
351364 if signing_info is not None:
@@ -390,10 +403,10 @@ conf = {{{packageName}}}.Configuration(
390403 """Set this to verify the peer using PEM (str) or DER (bytes)
391404 certificate data.
392405 """
393- self.cert_file = None
406+ self.cert_file = cert_file
394407 """client certificate file
395408 """
396- self.key_file = None
409+ self.key_file = key_file
397410 """client key file
398411 """
399412 self.assert_hostname = None
@@ -624,6 +637,34 @@ conf = {{{packageName}}}.Configuration(
624637 basic_auth=username + ':' + password
625638 ).get('authorization')
626639
640+ def get_access_token(self) -> str:
641+ """Gets HTTP bearer authentication header (string).
642+
643+ :return: The token for bearer HTTP authentication.
644+ """
645+ now = int(time.time())
646+ print(f"now: { now} , exp: { self.temp_access_token} , token: { self.temp_access_token_expires_at} ")
647+ if self.temp_access_token and self.temp_access_token_expires_at > now + 60:
648+ return self.temp_access_token
649+ else:
650+ print("Fetching new access token")
651+ _bytes = f"{ self.client_id} :{ self.client_secret} ".encode('utf-8')
652+ _encoded_string = base64.b64encode(_bytes).decode('utf-8')
653+ auth_header = f"Basic { _encoded_string} "
654+ resp = urllib3.request(
655+ 'POST',
656+ 'https://id.bandwidth.com/api/v1/oauth2/token',
657+ headers={
658+ ' Authorization' : auth_header,
659+ ' Content-Type' : ' application/x-www-form-urlencoded' ,
660+ } ,
661+ body='grant_type=client_credentials'
662+ )
663+ body = resp.json()
664+ self.temp_access_token = body['access_token']
665+ self.temp_access_token_expires_at = now + body['expires_in']
666+ return self.temp_access_token
667+
627668 def auth_settings(self)-> AuthSettings:
628669 """Gets Auth Settings dict for api client.
629670
@@ -677,14 +718,14 @@ conf = {{{packageName}}}.Configuration(
677718{ {/isBasic} }
678719{ {#isOAuth} }
679720 if self.access_token is not None:
680- auth['{ {name } } '] = {
721+ auth['OAuth2 '] = {
681722 ' type' : ' oauth2' ,
682723 ' in' : ' header' ,
683724 ' key' : ' Authorization' ,
684725 ' value' : ' Bearer ' + self.access_token
685726 }
686- if self.get_access_token is not None:
687- auth['{ {name } } '] = {
727+ if self.client_id is not None and self.client_secret is not None:
728+ auth['OAuth2 '] = {
688729 ' type' : ' oauth2' ,
689730 ' in' : ' header' ,
690731 ' key' : ' Authorization' ,
0 commit comments