diff --git a/README.md b/README.md index 3d12e3f..494e7ea 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,12 @@ from pyfcm import FCMNotification fcm = FCMNotification(service_account_file="", project_id="") +# Google oauth2 credentials(such as ADC, impersonate credentials) can be used instead of service account file. + +fcm = FCMNotification( + service_account_file=None, credentials=your_credentials, project_id="" +) + # OR initialize with proxies proxy_dict = { diff --git a/pyfcm/__meta__.py b/pyfcm/__meta__.py index 932e00d..8fdb2d3 100644 --- a/pyfcm/__meta__.py +++ b/pyfcm/__meta__.py @@ -2,7 +2,7 @@ __summary__ = "Python client for FCM - Firebase Cloud Messaging (Android, iOS and Web)" __url__ = "https://github.com/olucurious/pyfcm" -__version__ = "2.0.3" +__version__ = "2.0.4" __author__ = "Emmanuel Adegbite" __email__ = "olucurious@gmail.com" diff --git a/pyfcm/baseapi.py b/pyfcm/baseapi.py index 7fb334f..ad4ef98 100644 --- a/pyfcm/baseapi.py +++ b/pyfcm/baseapi.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json import os import time @@ -26,8 +28,9 @@ class BaseAPI(object): def __init__( self, - service_account_file: str, + service_account_file: str | None, project_id: str, + credentials=None, proxy_dict=None, env=None, json_encoder=None, @@ -38,6 +41,7 @@ def __init__( Attributes: service_account_file (str): path to service account JSON file project_id (str): project ID of Google account + credentials (Credentials): Google oauth2 credentials instance, such as ADC proxy_dict (dict): proxy settings dictionary, use proxy (keys: `http`, `https`) env (dict): environment settings dictionary, for example "app_engine" json_encoder (BaseJSONEncoder): JSON encoder @@ -49,10 +53,11 @@ def __init__( self.FCM_REQ_PROXIES = None self.custom_adapter = adapter self.thread_local = threading.local() + self.credentials = credentials - if not service_account_file: + if not service_account_file and not credentials: raise AuthenticationError( - "Please provide a service account file path in the constructor" + "Please provide a service account file path or credentials in the constructor" ) if ( @@ -85,7 +90,12 @@ def requests_session(self): self.thread_local.requests_session = requests.Session() self.thread_local.requests_session.mount("http://", adapter) self.thread_local.requests_session.mount("https://", adapter) + self.thread_local.token_expiry = 0 + + current_timestamp = time.time() + if self.thread_local.token_expiry < current_timestamp: self.thread_local.requests_session.headers.update(self.request_headers()) + self.thread_local.token_expiry = current_timestamp + 1800 return self.thread_local.requests_session def send_request(self, payload=None, timeout=None): @@ -120,17 +130,20 @@ def send_async_request(self, params_list, timeout): def _get_access_token(self): """ - Generates access from refresh token that contains in the service_account_file. + Generates access token from credentials. If token expires then new access token is generated. Returns: str: Access token """ # get OAuth 2.0 access token try: - credentials = service_account.Credentials.from_service_account_file( - self.service_account_file, - scopes=["https://www.googleapis.com/auth/firebase.messaging"], - ) + if self.service_account_file: + credentials = service_account.Credentials.from_service_account_file( + self.service_account_file, + scopes=["https://www.googleapis.com/auth/firebase.messaging"], + ) + else: + credentials = self.credentials request = google.auth.transport.requests.Request() credentials.refresh(request) return credentials.token diff --git a/requirements.txt b/requirements.txt index c1e330e..ef4d0df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ aiohttp>=3.6.2 cachetools==5.3.3 -google-auth==2.29.0 +google-auth==2.22.0 pyasn1==0.6.0 pyasn1-modules==0.4.0 rsa==4.9 diff --git a/tests/test_fcm.py b/tests/test_fcm.py index f7f3b7c..ecabc93 100644 --- a/tests/test_fcm.py +++ b/tests/test_fcm.py @@ -4,7 +4,7 @@ def test_push_service_without_credentials(): try: - FCMNotification(service_account_file="", project_id="") + FCMNotification(service_account_file="", project_id="", credentials=None) assert False, "Should raise AuthenticationError without credentials" except errors.AuthenticationError: pass