diff --git a/CHANGES.rst b/CHANGES.rst index 9e50d8e..5ff7548 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -158,3 +158,7 @@ Unreleased - Replace deprecated urllib3.Retry options v2 .. _Ɓukasz Rogowski: https://github.com/Rogalek + +- Explicitly handle 403 SENDER_ID_MISMATCH response + +.. _James Priebe: https://github.com/J-Priebe/ diff --git a/README.md b/README.md index 8888b22..d89c675 100644 --- a/README.md +++ b/README.md @@ -72,8 +72,18 @@ proxy_dict = { } fcm = FCMNotification(service_account_file="", project_id="", proxy_dict=proxy_dict) +# OR using credentials from environment variable +# Often you would save service account json in evironment variable +# Assuming GCP_CREDENTIALS contains the data (TIP: use "export GCP_CREDENTIALS=$(filename.json)" to quickly load the json) + +from google.oauth2 import service_account +gcp_json_credentials_dict = json.loads(os.getenv('GCP_CREDENTIALS', None)) +credentials = service_account.Credentials.from_service_account_info(gcp_json_credentials_dict, scopes=['https://www.googleapis.com/auth/firebase.messaging']) +fcm = FCMNotification(service_account_file=None, credentials=credentials, project_id="") + # Your service account file can be gotten from: https://console.firebase.google.com/u/0/project/_/settings/serviceaccounts/adminsdk +# Now you are ready to send notification fcm_token = "" notification_title = "Uber update" notification_body = "Hi John, your order is on the way!" @@ -106,6 +116,15 @@ result = fcm.notify(fcm_token=fcm_token, notification_body=notification_body, da # To a single device result = fcm.notify(fcm_token=fcm_token, data_payload=data_payload) +# Only string key and values are accepted. booleans, nested dicts are not supported +# To send nested dict, use something like +data_payload = { + "foo": "bar", + "data": json.dumps(data). +} +# For more info on format see https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#Message +# and https://firebase.google.com/docs/cloud-messaging/http-server-ref#downstream-http-messages-json + # Use notification messages when you want FCM to handle displaying a notification on your app's behalf. # Use data messages when you just want to process the messages only in your app. # PyFCM can send a message including both notification and data payloads. diff --git a/pyfcm/__meta__.py b/pyfcm/__meta__.py index 2560875..b6480b3 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.6" +__version__ = "2.0.7" __author__ = "Emmanuel Adegbite" __email__ = "olucurious@gmail.com" diff --git a/pyfcm/baseapi.py b/pyfcm/baseapi.py index 147050b..7178dd8 100644 --- a/pyfcm/baseapi.py +++ b/pyfcm/baseapi.py @@ -16,6 +16,7 @@ AuthenticationError, InvalidDataError, FCMError, + FCMSenderIdMismatchError, FCMServerError, FCMNotRegisteredError, ) @@ -185,12 +186,14 @@ def parse_response(self, response): Parses the json response sent back by the server and tries to get out the important return variables Returns: - dict: name (str) - uThe identifier of the message sent, in the format of projects/*/messages/{message_id} + dict: name (str) - The identifier of the message sent, in the format of projects/*/messages/{message_id} Raises: FCMServerError: FCM is temporary not available AuthenticationError: error authenticating the sender account InvalidDataError: data passed to FCM was incorrecly structured + FCMSenderIdMismatchError: the authenticated sender is different from the sender registered to the token + FCMNotRegisteredError: device token is missing, not registered, or invalid """ if response.status_code == 200: @@ -210,6 +213,10 @@ def parse_response(self, response): ) elif response.status_code == 400: raise InvalidDataError(response.text) + elif response.status_code == 403: + raise FCMSenderIdMismatchError( + "The authenticated sender ID is different from the sender ID for the registration token." + ) elif response.status_code == 404: raise FCMNotRegisteredError("Token not registered") else: diff --git a/pyfcm/errors.py b/pyfcm/errors.py index a186fa3..70ae23c 100644 --- a/pyfcm/errors.py +++ b/pyfcm/errors.py @@ -23,6 +23,15 @@ class FCMNotRegisteredError(FCMError): pass +class FCMSenderIdMismatchError(FCMError): + """ + Sender is not allowed for the given device tokens + https://firebase.google.com/docs/reference/fcm/rest/v1/ErrorCode + """ + + pass + + class FCMServerError(FCMError): """ Internal server error or timeout error on Firebase cloud messaging server diff --git a/pyfcm/fcm.py b/pyfcm/fcm.py index ca3f945..13647c9 100644 --- a/pyfcm/fcm.py +++ b/pyfcm/fcm.py @@ -41,13 +41,14 @@ def notify( timeout (int, optional): Set time limit for the request Returns: - dict: Response from FCM server (`multicast_id`, `success`, `failure`, `canonical_ids`, `results`) + dict: name (str) - The identifier of the message sent, in the format of projects/*/messages/{message_id} Raises: - AuthenticationError: If api_key is not set or provided or there is an error authenticating the sender. - FCMServerError: Internal server error or timeout error on Firebase cloud messaging server - InvalidDataError: Invalid data provided - InternalPackageError: Mostly from changes in the response of FCM, contact the project owner to resolve the issue + FCMServerError: FCM is temporary not available + AuthenticationError: error authenticating the sender account + InvalidDataError: data passed to FCM was incorrecly structured + FCMSenderIdMismatchError: the authenticated sender is different from the sender registered to the token + FCMNotRegisteredError: device token is missing, not registered, or invalid """ payload = self.parse_payload( fcm_token=fcm_token,