# Push Notifications EOL NOTICE: Sinch currently requests FCM push notifications using the FCM legacy endpoint, which is deprecated and will reach End of Life in June 2024 (see [Firebase FAQ](https://firebase.google.com/support/faq#fcm-23-deprecation)). To continue using FCM, your Sinch application must be migrated to use the FCM v1 API instead, which is the current supported method from Firebase/Google. If you want Sinch to keep sending FCM push notifications to your Android devices after June 2024, you **must** follow [this migration guide](/docs/in-app-calling/android/migration-to-fcm-v1), or Sinch won't be able to request FCM push messages on your behalf. ## Receiving Incoming Calls via Push Notifications To receive incoming calls via Push Notifications, the *application instance* has to be registered on the Sinch backend using [UserController.registerUser()](https://download.sinch.com/android/latest/reference/com/sinch/android/rtc/UserController.html). IMPORTANT: If you're using Sinch Client directly (and omit [User Controller](/docs/in-app-calling/android/user-controller) creation) this step happens under the hood when Sinch Client is started. Same steps described below should be applied in both cases (but with *SinchClient* API). Push Notifications allow reception of an incoming call even if the phone is locked, or the application is in the background or closed. Sinch SDK supports both currently available major Push Notification platforms on Android - [Google's Firebase Cloud Messages](#google-fcm-push-notifications) (later FCM) and [Huawei Mobile Services Push Notifications](#huawei-hms-notifications) (Huawei Push or HMS Push). Note: *application instance* must register itself on the Sinch backend to receive Push Notifications using either FCM or HMS. Note: If your application is registered on old version of the developer portal () you **MUST** migrate to Sinch dashboard () in order to be able to configure the push notifications properly. If you want to migrate to dashboard either create a new account there or contact [support@sinch.com](mailto:support@sinch.com) requesting RTC Account Migration to dashboard.sinch.com. Registering towards the Sinch backend to receive incoming call push notifications via FCM or HMS Push is quite [similar](#fcm-vs-hms-push-registration-steps-comparison) and consists of several topics, which are covered below. ## FCM vs HMS Push Registration Steps Comparison | Step | FCM Specific | Notes | | --- | --- | --- | | 1. [Enable support of the push notifications in *Sinch Client*](#fcm-step-1-enable-support-for-push-notifications) | Happends automatically when `FcmPushConfiguration` is provided | | | 2. [Update Sinch dashboard with required provider identification](#fcm-step-2-update-sinch-dashboard-with-required-provider-identification) | Add FCM identification in the Sinch dashboard | | | 3. [Provision your application with FCM support code](#fcm-step-3-provision-application-with-support-code) | Use **gooogle-services.json** | Acquire files online in FCM Consoles | | 4. [Acquire a unique *push configuration* from FCM](#fcm-step-4-acquire-fcm-push-configuration) | Automatic | | | 5. [Register the *push configuration* on the Sinch backend](#fcm-step-5-register-fcm-push-configuration-on-sinch-backend) | Use `FcmPushConfigurationBuilder` | | | 6. [Implement *listening service*](#fcm-step-6-implement-listening-service) | Derive from `FirebaseMessagingService`Use `RemoteMessage.getData()` | Minor differences in the `RemoteMessage` API | | Step | HMS Specific | Notes | | --- | --- | --- | | 1. [Enable support of the push notifications in *Sinch Client*](#hms-step-1-enable-support-for-push-notifications) | Happens automatically when `HMSPushConfiguration` is provided | | | 2. [Update Sinch dashboard with required provider identification](#hms-step-2-update-sinch-dashboard-with-required-provider-identification) | Specify HMS authentication mode in the Sinch dashboard | | | 3. [Provision your application with HMS support code](#hms-step-3-provision-application-with-support-code) | Use **agconnect-services.json** | Acquire files online in HMS Console | | 4. [Acquire a unique *push configuration* from HMS](#hms-step-4-acquire-hms-device-token) | | | | 5. [Register the *push configuration* on the Sinch backend](#hms-step-5-register-hms-device-token-on-sinch-backend) | Use `HmsPushConfigurationBuilder` | | | 6. [Implement *listening service*](#hms-step-6-implement-listening-service) | Derive from `HmsMessageService`Use `RemoteMessage.getDataOfMap()` | Minor differences in `RemoteMessage` API | ## Google FCM Push Notifications The following sections take you through the process of registering Google FCM push notifications: ### FCM Step 1. Enable support for Push Notifications This step is almost the same for both FCM and HMS. Enabling managed push notifications happens automatically once `PushConfiguration` is specified during Sinch Client creation. ```kotlin val sinchClient = SinchClient.builder().context(context) .pushConfiguration(fcmPushConfiguration) ... .build() ... sinchClient.start() ``` ### FCM Step 2. Update Sinch dashboard with required provider identification Sinch SDK sends FCM push messages using your app specific sender id. This means that you need to provide required FCM identification data (FCM Sender ID and Server Key) in the Sinch dashboard. Both values can be found in the Cloud Messaging tab of your FCM project setting in the [firebase console](https://console.firebase.google.com/). Simply copy paste them into *Google FCM Identification* section of your application page in the Sinch [developer dashboard](https://dashboard.sinch.com/). ### FCM Step 3. Provision application with support code Provisioning your application with the support code to receive the FCM Push Notifications is easy. You'll need to acquire a configuration file **google-services.json** from the FCM console, and add it to your project. You can add Firebase to your app either semi-automatically using Android Studio, or manually [following this step-by-step official guide](https://firebase.google.com/docs/android/setup). In brief, to perform manual setup, you first need to register your application in the [firebase console](https://console.firebase.google.com/). If your project already uses FCM, the console will prompt you to import it as a new Firebase Cloud Messaging project. Register your application using the console, and download relevant *google-services.json* into your project's main folder. Sample SDK projects *sinch-rtc-sample-push* and *sinch-rtc-sample-video-push* will require you to supply your own *google-services.json* in order to be built. In the absence of this file, gradle will show a relevant error with explanation and relevant links and stop the build. That *google-services.json* file is the main mean of automatization of support of Firebase services to your app. Android Studio's plugin `com.google.gms.google-services` parses and adds relevant resources and permissions to your applications manifest automatically. ### FCM Step 4. Acquire FCM push configuration Note: When using FCM your app has to include `com.google.firebase:firebase-messaging` as a dependency. Snippets below use APIs provided by that library. For using FCM push configuration to work you have to obtain 2 string properties: - Sender ID - Registration token Getting sender ID is easy. Once the firebase app is created it doesn't change and is bundled into the **google-services.json** file. You can either check it in your firebase console project setting tab or get it synchronically during app runtime like: ```kotlin val APP_FCM_SENDER_ID: String = FirebaseApp.getInstance().options.gcmSenderId.orEmpty() ``` Registration token might be a bit more problematic as getting it must be done asynchronically and it can change during the lifetime of your application. Use below code to acquire the registration token: ```kotlin FirebaseMessaging.getInstance().token.addOnCompleteListener { task: Task -> if (task.isSuccessful) { val registrationToken = task.result } } ``` You should also track any token changes in the [onNewToken](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/firebasemessagingservice#public-void-onnewtoken-string-token) callback of the FirebaseMessagingService and recreate your SinchClient instance in such a case (see `sinch-rtc-sample-push` for a complete use case). ### FCM Step 5. Register FCM push configuration on Sinch backend Create an instance of *FcmPushConfiguration* using the [FcmPushConfigurationBuilder](https://download.sinch.com/android/latest/reference/com/sinch/android/rtc/FcmPushConfigurationBuilder.html) and use it to initiate the *UserController*. Then after calling [UserController.registerUser()](https://download.sinch.com/android/latest/reference/com/sinch/android/rtc/UserController.html) to register the FCM push configuration on the Sinch Backend wait for the callbacks that reports whether registration succeeded. Please see dedicated `UserContoller` documentation [here](/docs/in-app-calling/android/user-controller). ```kotlin val pushConfiguration = PushConfiguration.fcmPushConfigurationBuilder() .senderID(APP_FCM_SENDER_ID) .registrationToken(registrationToken) .build() val userController = UserController.builder() .context(getApplicationContext()) .applicationKey("") .userId("") .environmentHost("ocra.api.sinch.com") .pushConfiguration(pushConfiguration) .build() // parameters are `UserRegistrationCallback` // and `PushTokenRegistrationCallback` userController.registerUser(this, this) ``` ### FCM Step 6. Implement *Listening Service* Implement your *FcmListerningService* by extending the *FirebaseMessagingService*: ```kotlin ... class FcmListenerService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage){ if (SinchPush.isSinchPushPayload(remoteMessage.data)) { val result = SinchPush.queryPushNotificationPayload(context, remoteMessage.data) sinchClient.relayRemotePushNotification(result) ... } else { // it's NOT Sinch message - process yourself } }} ``` ## Huawei HMS Notifications The following sections take you through the process of registering Huawei HMS push notifications: ### Prerequisites To use Huawei Push notifications you must also implement the [Huawei OAuth 2.0 Flow](#huawei-oauth-20-flow). ### HMS Step 1. Enable support for Push Notifications This step is almost the same for both FCM and HMS. Enabling managed push notifications happens automatically once `PushConfiguration` is specified during Sinch Client creation. ```kotlin val sinchClient = SinchClient.builder().context(context) .pushConfiguration(hmsPushConfiguration) ... .build() ... sinchClient.start() ``` Note: Using the HMS Push platform requires that the *Huawei Mobile Services* is installed on the device. The UI prompt to install the *HMS* will appear automatically the very first time a unique *HMS device token* is being acquired. ### HMS Step 2. Update Sinch dashboard with required provider identification ### HMS Step 3. Provision application with support code Follow the [Huawei Push Kit Devlopment Process](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-config-agc-0000001050170137) to acquire **agconnect-services.json**. Refer to [How to Integrate HMS Core SDK](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-integrating-sdk-0000001050040084) for the necessary changes in the **gradle** build files. ### HMS Step 4. Acquire HMS device token Sinch SDK expects the application to acquire the *HMS device token* **before** creating *UserController*. A good example of how to acquire an *HMS device token* asynchronously is provided in the `RegisterToHmsTask` class of the `sinch-rtc-sample-hms-push` sample application. The task extends `AsyncTask` and returns both known beforehand `hmsApplicationId` and unique `hmsDeviceToken`, which Huawei recommends to re-acquire on each application start. To read the HMS Application ID and acquire the HMS Device token, use following methods: ```kotlin val hmsApplicationId = AGConnectServicesConfig.fromContext(context).getString("client/app_id") val hmsDeviceToken = HmsInstanceId.getInstance(context).getToken(appId, "HCM") ``` Note: For this operation to succeed, your application must be signed and its signature fingerprint registered in the `Huawei AGConnect` console. To learn more, please follow this [link](https://developer.huawei.com/consumer/en/doc/HMSCore-Guides-V5/android-config-agc-0000001050170137-V5#EN-US_TOPIC_0000001050170137__section193351110105114). Checklist for Obtaining the HMS Device Token - Application is registered on Huawei AGConnect Console (IMPORTANT: package name should match). - The AGConnect Console's application *project* has PushKit enabled. - The application is **signed**. - Fingerprint of the signature is registered in the AGConnect Console . - HMS is installed on the device (User will get UI Prompt automatically). - Device is connected to the internet. ### HMS Step 5. Register HMS device token on Sinch backend Create an instance of *HmsPushConfiguration* using the [HmsPushConfigurationBuilder](https://download.sinch.com/android/latest/reference/com/sinch/android/rtc/HmsPushConfigurationBuilder.html) and use it to initiate the *UserController*. Then call [UserController.registerUser()](https://download.sinch.com/android/latest/reference/com/sinch/android/rtc/UserController.html) to register the HMS token on the Sinch backend and wait for the callbacks that reports whether registration succeeded. Please see dedicated `UserContoller` documentation [here](/docs/in-app-calling/android/user-controller). ```kotlin val pushConfiguration = PushConfiguration.hmsPushConfigurationBuilder() .applicationId(hmsApplicationId) .deviceToken(hmsDeviceToken) .build() val userController = UserController.builder() .context(applicationContext) .applicationKey("") .userId("") .environmentHost("ocra.api.sinch.com") .pushConfiguration(pushConfiguration) .build() // parameters are `UserRegistrationCallback` // and `PushTokenRegistrationCallback` userController.registerUser(this, this) ``` ### HMS Step 6. Implement *Listening Service* Implement your *HmsListerningService* by extending the *HmsMessageService*: ```kotlin class HmsListenerService : HmsMessageService() { override fun onMessageReceived(message: RemoteMessage) { if (SinchPush.isSinchPushPayload(message.dataOfMap)) { val result = SinchPush.queryPushNotificationPayload(context, message.dataOfMap) sinchClient.relayRemotePushNotification(result) ... } else { // it's NOT Sinch message - process yourself } }} ``` ## Sample Applications As a developer, you will be responsible for implementing the code that receives the push message. - For FCM example, please see the sample apps `sinch-rtc-sample-push` and `sinch-rtc-sample-video-push`. - For HMS example, please see the sample app `sinch-rtc-sample-hms-push`. The following sections cover how to support receiving calls and messages via push notifications. ## Receive and Forward Push Notifications to a Sinch Client Once you have received the `RemoteMessage` in your *listening service* you should forward it to the Sinch client. In order to do so: 1. Verify it is a Sinch payload by calling [SinchPush.isSinchPushPayload](https://download.sinch.com/android/latest/reference/com/sinch/android/rtc/SinchPush.html). 2. If it is transform it into the [CallNotificationResult](https://download.sinch.com/android/latest/reference/com/sinch/android/rtc/calling/SinchPush.html) by calling [SinchPush.queryPushNotificationPayload](https://download.sinch.com/android/latest/reference/com/sinch/android/rtc/SinchPush.html). 3. At this point CallNotificationResult can be inspected and provide information about participants, whether the call timed out and whether the call offers video. 4. Forward provided CallNotificationResult to the Sinch Client by calling [SinchClient.relayRemotePushNotificationPayload](https://download.sinch.com/android/latest/reference/com/sinch/android/rtc/SinchClient.html). ## Send and Receive Custom Headers via Sinch Managed Push The Sinch SDK supports adding custom headers in push notification messages when initiating a call, so developers don't need to implement their own push mechanism if they only need to deliver small pieces of information along the Sinch managed push between their app instances. The Sinch SDK allows up to *1024* bytes of custom headers. Setting custom headers on the sender side when initiating a call: ```kotlin val headers = hashMapOf( "First key" to "123", "Second key" to "second value" ) val call = callController.callUser(userId, new MediaConstraints(false), headers) ``` If custom headers were supplied by call initiator, they can be retrieved from notification result using `callResult.callHeaders` API: ```kotlin if (SinchPush.isSinchPushPayload(remoteMessage.data)) { val result = SinchPush.queryPushNotificationPayload(context, remoteMessage.data) // For HMS use remoteMessage.dataOfMap API instead. val customHeaders = callResult.callHeaders } ``` Note: It's possible to retrieve custom headers without starting the client (doing it allows you to create some early reject logic without the need to initiate a more resource demanding process of creating and starting the Sinch client). ## Unregister a Device If the user of the application logs out or performs a similar action, which implies that this device shouldn't receive incoming calls push notifications any longer, the push notification device token can be unregistered via [UserController.unregisterPushToken()](https://download.sinch.com/android/latest/reference/com/sinch/android/rtc/UserController.html). Note: If your application assumes frequent change of users (logging in and out), it's imperative to unregister the device by using `UserController.unregisterPushToken()` on each log out to guarantee that a new user won't receive incoming calls intended to the previous one. ## GCM to FCM Migration Sinch SDK moved from deprecated *Google Cloud Messaging* (GCM) to its most up-to-date and Google-recommended version *Firebase Cloud Messaging* (FCM), which requires client app to be modified in accordance with the Google's official [GCM to FCM migration guide](https://developers.google.com/cloud-messaging/android/android-migrate-fcm) ## Permissions Required You don't need to manually add any permission to the application manifest - all required changes will be added automatically by the **gradle** depending on the configuration file you provide (**google-service.json** or **agconnect-services.json**). ## Huawei OAuth 2.0 Flow Sinch supports Huawei push messages via [Huawei Push Kit](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/service-introduction-0000001050040060) (*HPK*) which is part of [Huawei Mobile Services](https://developer.huawei.com/consumer/en/hms) (*HMS*). The Sinch SDKs and platform can take care of sending push notification messages via *Huawei Push Kit* on your behalf as part our *Sinch Managed Push Notifications* functionality. To enable Huawei push messages for Android devices you will need to do two things: 1. Make use of the *Huawei* (*HMS*) APIs in the Sinch Android SDK [as described above](#huawei-hms-notifications). 2. Implement an *OAuth 2.0 Authorization Server* endpoint that can provide Sinch with *OAuth 2.0* *access_tokens* required to send messages via *HPK* on your behalf. Sinch will send push notification messages via the [Huawei Push Kit API](https://developer.huawei.com/consumer/en/doc/HMSCore-References-V5/https-send-api-0000001050986197-V5). Huawei Push Kit supports (and requires) an *OAuth 2.0* *Client Credentials* flow to authenticate against the *Huawei Push Kit* server endpoint(s). Your Huawei app in Huawei *AppGallery Connect* will have an *App ID* and an *App secret*. These are to be used as OAuth client credentials: `client_id` and `client_secret` respectively. Sinch supports Huawei OAuth flow by delegation. You will keep your `client_secret` on your backend, and Sinch will request an HMS OAuth `access_token` via an server-side HTTP API endpoint that you implement. Sinch supports two different alternatives for how to provide Sinch with an HMS access token: - **A)** A HMS token endpoint protected by a standard OAuth 2.0 *Client Credentials* flow. This is a good fit if you have an existing OAuth 2.0 Authorization Server that's used to protect and grant access access to your server-side endpoints. - **B)** A HMS token endpoint protected using your existing Sinch credentials (Sinch *Application Key* and *Application Secret*). This is a good fit if you don't have an OAuth 2.0 Authorization Server to grant access to your server-side endpoints. ### Alt A) Huawei OAuth Flow Using Your OAuth 2.0 Domain This flow assumes you have an OAuth 2.0 conforming *Authorization Server* that supports the *Client Credentials* grant type. The flow is implemented in terms of two key steps: 1. You create a set of OAuth 2.0 *Client Credentials* that are valid within **your** OAuth domain, and configure those for your *Sinch Application*. 2. You implement a HMS token endpoint that provides a HMS access token (labeled `$push_token_endpoint` in diagram below). In the [Sinch Dashboard](https://dashboard.sinch.com/) you should configuring the following: - *OAuth 2.0 access token endpoint* (URL) - *Client Credentials* (`client_id` and `client_secret`) - An OAuth *scope* (optional to specify, will default to `https://push-api.cloud.huawei.com`) - HMS token endpoint (URL) The overall flow is depicted below: ![Sinch - Huawei OAuth Flow using your OAuth domain](/assets/20201130-sinch-huawei-hpk-oauth-a.93a909375870fb2e4d74211035a0562ab98063e72adaf10b608288330f3fa0a4.5f3ff73a.png) Key takeaways: 1. The component labeled *Your Resource Server* in the diagram is your *Resource Server* in the terminology of OAuth and the *resource* here being an *HMS access token*. 2. When Sinch needs a (new) HMS `access_token` required to send a push message to *Huawei Push Kit* server, it will first make a request to your *Authorization Server* to obtain an `access_token` valid for your security domain (labeled as `access_token_RO` in the diagram, *RO* as in *Resource Owner*). 3. Having obtained `access_token_RO`, Sinch will make a subsequent request to your HMS access token endpoint (labeled `$push_token_endpoint` in the diagram), providing `access_token_RO` as a *Bearer* token. 4. Your *Resource Server* should obtain a HMS `access_token` using the Huawei HMS OAuth Authorization Server endpoint and your Huawei *App ID* and *App secret* as `client_id` and `client_secret`. 5. Your *Resource Server* should pass the HMS `access_token` (as received from HMS) in the response back to Sinch. Sinch will only make requests to your Authorization Server access token endpoint and your HMS token endpoint as needed, not for every push message sent. Sinch will cache the HMS access token in accordance to the value of `expires_in`. Note: You can think of the step where you configure OAuth *Client Credentials* for your Sinch *Application* as a way of enabling Sinch and your Sinch *Application* in particular to make requests to your HMS token endpoint (*Resource Server*). #### Implementing the HMS Token Endpoint As described in the overview, Sinch will make a request to your *Resource Server* HMS token endpoint, requesting a *HMS* `access_token`. The request will be on the following form: ```text POST / Authorization: Bearer Content-Type: application/x-www-form-urlencoded grant_type=client_credentials& hms_application_id= ``` Your implementation of this resource endpoint should obtain an HMS `access_token` using the Huawei HMS OAuth endpoint, using your Huawei *App ID* and *App secret* as `client_id` and `client_secret`. The access token received from Huawei should then be included in the response back to Sinch. See [Huawei documentation](https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/38054564) for how to implement requesting an OAuth *access token* using Huawei HMS. Example response to Sinch: ```json HTTP/1.1 200 OK Content-Type: application/json;charset=utf-8 { "access_token": "", "expires_in": 3600, "token_type": "Bearer" } ``` Sinch will then be able to use this `access_token` to send push messages to your end-user devices until the token expires, upon which Sinch will issue a new token request to your *Authorization Server* and *Resource Server*. Attention! You will receive your *HMS App ID* as a request parameter (`hms_application_id`) and you can use that for a given request to map it to your corresponding *HMS App*. Note: [OAuth 2.0 access token response](https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/). ### Alt B) Huawei OAuth Flow Using Sinch Application Credentials The overall flow is depicted below: ![Sinch - Huawei OAuth Flow using Sinch credentials](/assets/20201130-sinch-huawei-hpk-oauth-b.9f8c7574a6130ed00bce3a9fd7ab47277314293e9db7f51ae274fe68f36e60df.5f3ff73a.png) Key takeaways: 1. When Sinch needs an `access_token` required to send a push message to *Huawei Push Kit* server, it will make an OAuth request using a *Client Credentials* grant type to your *Authorization Server*. This request will be specifying an `client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer` as the value for `client_assertion` provides a JWT that's symmetrically signed with your *Sinch Application Secret*. 2. Your *Authorization Server* should validate the JWT provided as `client_assertion` by Sinch and that the signed JWT is signed with your *Sinch Application Secret*. 3. Your *Authorization Server* should obtain a HMS `access_token` using the Huawei HMS OAuth Authorization Server endpoint and your Huawei *App ID* and *App secret* as `client_id` and `client_secret`. 4. Your *Authorization Server* should pass the HMS `access_token` (as received from HMS) in the response back to Sinch. Sinch will only make requests to your Authorization Server access token endpoint and your HMS token endpoint as needed and not for every push message sent. Sinch will cache the HMS access token in accordance to the value of `expires_in`. Details on how to validate the JWT provided by Sinch as `client_assertion` are available in the following sections. (The use of `client_assertion` and `client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer` is based on [RFC 7523](https://tools.ietf.org/html/rfc7523) and [RFC 7521](https://tools.ietf.org/html/rfc7521#section-4.2) and part of the [OpenID Connect Core 1.0 standard](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication).) #### Validating the `client_assertion` JWT provided by Sinch As described in the overview, Sinch will make a request to your OAuth *Authorization Server* endpoint, requesting a *HMS* `access_token`. The request will be on the following form: ```text POST /sinch/rtc/push/oauth2/v1/huawei-hms/token HTTP 1.1 Host: as.your-domain.com Content-Type: application/x-www-form-urlencoded grant_type=client_credentials& scope=https%3A%2F%2Fpush-api.cloud.huawei.com& client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer client_assertion= ``` Note: The value of `scope` and `client_assertion_type` in the example above are URL-encoded The `JWT` will be making use of the standard JWT header parameters `alg` and `kid`, and the standard claims `iss`, `sub`, `iat`, `exp`, `nonce` and `aud`. Before we jump to the details of how to validate this token, here is an example: ```json // JWT Header (example) { "alg": "HS256", "kid": "hkdfv1-20200901", "sinch:rtc:application_key": "a32e5a8d-f7d8-411c-9645-9038e8dd051d" } // JWT Payload (example) { "iss": "//rtc.sinch.com/applications/a32e5a8d-f7d8-411c-9645-9038e8dd051d", "sub": "123456789", "aud": "https://as.your-domain.com/sinch/rtc/push/oauth2/v1/huawei-hms/token", "scope": "https://push-api.cloud.huawei.com", "sinch:rtc:application_key": "a32e5a8d-f7d8-411c-9645-9038e8dd051d", "iat": 1600780504, "exp": 1600784104, "nonce": "6b438bda-2d5c-4e8c-92b0-39f20a94b34e" } ``` - Claim `iss` is on the form `//rtc.sinch.com/applications/` (canonical form) - Claim `sub` is your *HMS App ID* (as specified via `HmsPushBuilder.applicationId(String)` on the Android client). - Claim `aud` will be set to the *Authorization Server* token endpoint you have configured with Sinch. example `https://as.your-domain.com/sinch/rtc/push/oauth2/v1/huawei-hms/token` - Claim `scope` will be `https://push-api.cloud.huawei.com` (representing the Huawei *Push Kit* server domain) - Claim `sinch:rtc:application_key` will contain your *Sinch Application Key*`. - Claims `iat`, `exp`, `nonce` are standard JWT claims (see [JWT RFC 7519](https://tools.ietf.org/html/rfc7519)) Note: Your *Sinch Application Key* is present both in the JWT header and the JWT payload (as header parameter and claim `sinch:rtc:application_key`). The reason, is that it allows you to implement validating the JWT signature without accessing the payload, and once you have validated the JWT signature, you can strip away the header and all the data you need for further processing is self contained in the payload. ##### `kid` and Deriving a Signing Key from Sinch Application Secret The `kid` parameter in the JWT header is on the form `hkdfv1-{DATE}` where `{DATE}` is date of signing in UTC on format `YYYYMMDD`. When validating the JWT, use a signing key that's derived from your *Sinch Application Secret* as follows. Given: - A function `HMAC256(key, message)`. - A date-formatting function `FormatDate(date, format)`. - The date of signing as variable `signedAt`. This is to be extracted from the JWT header parameter `kid`. - *Sinch Application Secret* as variable `applicationSecret`, holding the secret as a *base64* encoded string. Derive the signing key as follows: ```text signingKey = HMAC256(BASE64-DECODE(applicationSecret), UTF8-ENCODE(FormatDate(signedAt, "YYYYMMDD"))) ``` Note: This is the same key derivation scheme as used for [Token-based User registration](/docs/in-app-calling/android/application-authentication) ##### Validating the JWT Your *Authorization Server* should validate the JWT in accordance with section [RFC 7523 - Section 3. JWT Format and Processing Requirements](https://tools.ietf.org/html/rfc7523#section-3). Here is a rough outline of the steps necessary: 1. Use JWT header parameter `sinch:rtc:application_key` to lookup your corresponding *Application Key* and *Application Secret* (at this point the token is still unvalidated) 2. Derive the signing key (as detailed in the previous section) 3. Validate that the JWT has a valid signature given the signing that you have derived. 4. Validate the JWT payload in terms of `iat`, `exp`, `nonce` etc. 5. Validate that the claim `scope` is `https://push-api.cloud.huawei.com` Examples of JWT creation Visit [https://github.com/sinch/sinch-rtc-api-auth-examples](https://github.com/sinch/sinch-rtc-api-auth-examples) for example implementations of JWT generation. #### Acquiring an `access_token` from Huawei HMS After validating the JWT client assertion, your *Authorization Server* should in turn request an `access_token` from the Huawei HMS OAuth endpoint, using your Huawei *App ID* and *App secret* as `client_id` and `client_secret`. The access token received from Huawei should then be included in the response back to Sinch. See [Huawei documentation](https://developer.huawei.com/consumer/en/doc/HMSCore-Guides/open-platform-oauth-0000001050123437-V5#EN-US_TOPIC_0000001050123437__section12493191334711) for how to implement requesting an OAuth *access token* using Huawei HMS. Example response to Sinch: ```json HTTP/1.1 200 OK Content-Type: application/json;charset=utf-8 { "access_token": "", "expires_in": 3600, "token_type": "Bearer" } ``` Sinch will then be able to use this `access_token` to send push messages to your end-user devices until the token expires, upon which Sinch will issue a new token request to your *Authorization Server*. Note: You will receive your *HMS App ID* in JWT claim `sub` and you can use that to for a given request map it to your corresponding *HMS App*. You will also be able to access your *Sinch Application Key* as the JWT claim `sinch:rtc:application_key` if you need that as input at this stage. #### Rejecting the `access_token` grant request If your *Authorization Server* rejects the access token request from Sinch, it should respond with a HTTP response that's compliant with the [OAuth 2.0 standard](https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/). Example: ```json HTTP/1.1 400 Bad Request Content-Type: application/json;charset=utf-8 { "error": "unauthorized_client", "error_description": "Your helpful error description" } ``` Please see [https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/](https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/) for details on how to formulate conformant OAuth error responses. ### More Resources on Huawei Push and OAuth - Examples how to validate a client assertion JWT at [https://github.com/sinch/sinch-rtc-api-auth-examples](https://github.com/sinch/sinch-rtc-api-auth-examples). - [Huawei HMS OAuth Client Credentials Flow](https://developer.huawei.com/consumer/en/doc/HMSCore-Guides/open-platform-oauth-0000001050123437-V5#EN-US_TOPIC_0000001050123437__section12493191334711) - [OAuth 2.0 Client Credentials](https://www.oauth.com/oauth2-servers/access-tokens/client-credentials/) - [OAuth `client_secret_jwt`](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) - [RFC 7523 - JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants](https://tools.ietf.org/html/rfc7523) - [RFC 7521 - Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants](https://tools.ietf.org/html/rfc7521#section-4.2)