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.
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:
{
"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:
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 afteriat
)
We can construct a JWT as follows:
JWT Header:
{
"alg": "HS256",
"kid": "hkdfv1-20180102"
}
JWT Payload:
{
"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.
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()
// 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:
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:
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()
// 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:
{
"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:
- as the initial instance expiry when a new instance is created
- 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.