Authentication & Authorization

Secure your application and authorize Sinch client (user) registrations.

When you start SinchClient or register user via UserController you have to provide user identity. The first time the application instance and the Sinch client are running on behalf of a particular user, it's required to register against the Sinch service. The step of registering a user identity against the Sinch service requires the application instance to provide a token that authenticates the Application and grants permission (authorizes) the user to register. Once the application instance has successfully registered the user identity, the client will have obtained the necessary credentials to perform further authorized requests on behalf of the Application and for that specific user to make and receive calls.

Token-based User Registration - Overview

To authorize the registration of a user, the application must provide a registration token to the SinchClient or UserController. This token should be in the form of a JSON Web Token (JWT) signed with a signing key derived from the Application Secret.

The recommended way to implement this authentication scheme is that the Application Secret should be kept securely on your server-side backend, the signed token should be created and signed on your server, then passed via a secure channel to the application instance and Sinch client running on a device.

Token-based User Registration

The following sections describes, in detail, how to create and sign the JWT, and how to provide it to the SinchClient or UserController.

Creating a Registration Token

JWT Header

A registration token is a JWT with the following JWT header parameters:

Header Parameter Value Note
alg HS256
kid hkdfv1-{DATE} Where {DATE} is current date in UTC on format YYYYMMDD

Example of JWT header:

Copy
Copied
    {
      "alg": "HS256",
      "kid": "hkdfv1-20200102"
    }
Note:

The {DATE} passed into the kid parameter has to be valid current date at the time the challenge is made (onCredentialsRequired is called).

JWT Claims

The JWT must contain the following claims:

Claim Value / Description
iss //rtc.sinch.com/applications/{APPLICATION_KEY}
sub //rtc.sinch.com/applications/{APPLICATION_KEY}/users/{USER_ID}
iat See JWT RFC 7519 section-4.1.1
exp See JWT RFC 7519 section-4.1.4
nonce A unique cryptographic nonce
info

The expiration time for the token itself (exp) should be set so that the Time-to-Live of the token is not less than 1 minute.

Signing the JWT

The JWT should be signed using a signing key derived from the Sinch Application Secret as follows. Given:

  • A function HMAC256(key, message) .
  • A date-formatting function FormatDate(date, format) .
  • The current date as variable now .
  • Sinch Application Secret as variable applicationSecret , holding the secret as a base64 encoded string.

Derive the signing key as follows:

Copy
Copied
signingKey = HMAC256(BASE64-DECODE(applicationSecret), UTF8-ENCODE(FormatDate(now, "YYYYMMDD")))

Examples

Given the following input:

  • Application Key = a32e5a8d-f7d8-411c-9645-9038e8dd051d
  • Application Secret = ax8hTTQJF0OPXL32r1LHMA== (in base64 encoded format)
  • User ID = foo
  • nonce = 6b438bda-2d5c-4e8c-92b0-39f20a94b34e
  • Current time: 2018-01-02 03:04:05 UTC
  • JWT expiry = 600 seconds ( exp will be set to 600 seconds after iat )

We can construct a JWT as follows:

JWT Header:

Copy
Copied
{
   "alg": "HS256",
    "kid": "hkdfv1-20180102"
}

JWT Payload:

Copy
Copied
    {
      "iss": "//rtc.sinch.com/applications/a32e5a8d-f7d8-411c-9645-9038e8dd051d",
      "sub": "//rtc.sinch.com/applications/a32e5a8d-f7d8-411c-9645-9038e8dd051d/users/foo",
      "iat": 1514862245,
      "exp": 1514862845,
      "nonce":"6b438bda-2d5c-4e8c-92b0-39f20a94b34e"
    }

Derived Signing Key:

The derived signing key would in this case be AZj5EsS8S7wb06xr5jERqPHsraQt3w/+Ih5EfrhisBQ= (base64 encoded format)

Final Encoded JWT:

Note

The encoded JWT output below contains both the JSON header and payload as shown above, with the claims in exactly the same order as the example and the JSON in minified format.

Copy
Copied
eyJhbGciOiJIUzI1NiIsImtpZCI6ImhrZGZ2MS0yMDE4MDEwMiJ9.eyJpc3MiOiIvL3J0Yy5zaW5jaC5jb20vYXBwbGljYXRpb25zL2EzMmU1YThkLWY3ZDgtNDExYy05NjQ1LTkwMzhlOGRkMDUxZCIsInN1YiI6Ii8vcnRjLnNpbmNoLmNvbS9hcHBsaWNhdGlvbnMvYTMyZTVhOGQtZjdkOC00MTFjLTk2NDUtOTAzOGU4ZGQwNTFkL3VzZXJzL2ZvbyIsImlhdCI6MTUxNDg2MjI0NSwiZXhwIjoxNTE0ODYyODQ1LCJub25jZSI6IjZiNDM4YmRhLTJkNWMtNGU4Yy05MmIwLTM5ZjIwYTk0YjM0ZSJ9.EUltTTD4fxhkwCgLgj6qSQXKawpwQ952Ywm3OwQSARo

More detailed examples on how to implement this in various languages, including reference data for unit tests, is available at https://github.com/sinch/sinch-rtc-api-auth-examples.

For additional information about JWT, along with a list of available libraries for generating signed JWTs, see https://jwt.io. For detailed information about the JWT specification, see https://tools.ietf.org/html/rfc7519.

Providing a Registration Token to SinchClient

When starting the client (SinchClient.start()) for the first time with a new user, the client will ask for a token via SinchClientListener.onCredentialsRequired()

Copy
Copied
    // Instantiate a SinchClient using the SinchClientBuilder.
    val sinchClient = SinchClient.builder()
            .context(applicationContext)
            .userId("<user id>")
            .applicationKey("<application key>")
            .environmentHost("ocra.api.sinch.com")
            .build()
    sinchClient.addSinchClientListener(SinchClientListener())
    sinchClient.start()

In your SinchClientListener class:

Copy
Copied
    override fun onCredentialsRequired(clientRegistration: ClientRegistration) {

        yourAuthServer.getRegistrationToken(userId, YourAuthCallback() {
            fun onSuccess(token: String) {
                clientRegistration.register(token)
            }
            fun onFailure() {
                clientRegistration.registerFailed()
            }
        })
    }
Note:

The client MAY also ask for a registration token on subsequent starts via SinchClientListener.onCredentialsRequired() and only then should provide a new JWT.

Providing a Registration Token to UserController

UserController is a component that serves the purpose of registration against the Sinch Backend to receive incoming calls via Managed Push. More about it can be found later in the chapter UserController.

To provide the registration token to UserController use the similar scheme:

Copy
Copied
    val userController = UserController.builder()
                .context(applicationContext)
                .applicationKey("<application key>")
                .userId("<user id>")
                .environmentHost("ocra.api.sinch.com")
                .pushConfiguration(pushConfiguration)
                .build()
    userController.registerUser(userRegistrationCallback, pushTokenRegistrationCallback)

Provide signed registration token in your UserRegistrationCallback.onCredentialsRequired()

Copy
Copied
    // Your UserRegistrationCallback implementation

    override fun onCredentialsRequired(clientRegistration: ClientRegistration) {

        yourAuthServer.getRegistrationToken(userId, YourAuthCallback() {
            fun onSuccess(token: String) {
                clientRegistration.register(token)
            }
            fun onFailure() {
                clientRegistration.registerFailed()
            }
        })
    }
Note:

If you receive Instance creation rate exceeded messages on user registration, please verify you are using SinchClientListener.onCredentialsRequired() correctly and investigate why your application is re-registering the same user with such frequency. If you are not sure, please contact support.

Limiting Client Registration with Expiration Time

Depending on your security requirements, you may want to limit a client registration time-to-live (TTL). Limiting the client registration will effectively limit the Sinch client acting on behalf of the User on the particular device after the TTL has expired. This will prevent the client from making or receiving calls after the registration TTL has expired.

To limit the registration in time, create the JWT as described in the sections above, but with the additional claim sinch:rtc:instance:exp. The value for this claim should be in the same format as claims iat and exp, a JSON numeric value representing the number of seconds since Unix Epoch (UTC).

Example JWT Payload:

Copy
Copied
{
      "iss": "//rtc.sinch.com/applications/a32e5a8d-f7d8-411c-9645-9038e8dd051d",
      "sub": "//rtc.sinch.com/applications/a32e5a8d-f7d8-411c-9645-9038e8dd051d/users/foo",
      "iat": 1514862245,
      "exp": 1514862845,
      "nonce":"6b438bda-2d5c-4e8c-92b0-39f20a94b34e",
      "sinch:rtc:instance:exp": 1515035045
}
Important!

TTL of the registration must be >= 48 hours. In other words: sinch:rtc:instance:exp - iat >= 48 * 3600.

When a Sinch client registers with a User registration token, the registration is also bound to the particular device. Limiting the TTL of the registration is device-specific and doesn't affect other potential registrations for the same User on other devices.

Don't confuse sinch:rtc:instance:exp with the standard JWT claim exp - they're separate claims. The former is for limiting the client registration. The latter is only for limiting the TTL of the JWT itself.

Recommendation

We recommend a 12 months expiry, unless you have reasons to keep it shorter.

Important!

The instance expiry specified in sinch:rtc:instance:exp will be used:

  1. as the initial instance expiry when a new instance is created
  2. as the new instance expiry when an existing instance tries to prolong its lifetime

Automatic Extension of Client Registration Time-to-Live (TTL)

The Sinch client will automatically request to extend the TTL of its registration by invoking SinchClientListener.onCredentialsRequired()

The request to extend the client registration TTL is triggered when the Sinch client is started and the expiry of TTL is detected to be near in the future. "Near in the future" is subject to internal implementation details, but the Sinch client will regularly try to extend its registration and will adjust the interval according to the TTL.

Note:

You can't provide a JWT without specifying registration TTL if a JWT with a limited TTL has been used before for the given User on the specific device. Once a Sinch client registration has initially been constrained with a TTL, the registration can be extended in time, but not extended indefinitely.

We'd love to hear from you!
Rate this content:
Still have a question?
 
Ask the community.