SDK Authentication
SDK Authentication allows you to supply cryptographic proof (generated server-side) to SDK requests made on behalf of logged-in users.
After you enable this feature in your app, you can configure the Braze dashboard to reject any requests with an invalid or missing JSON Web Token (JWT) signature, which includes:
- Sending custom events, attributes, purchases, and session data
- Creating new users in your Braze workspace
- Updating standard user profile attributes
- Receiving or triggering messages
Now you can prevent unauthenticated logged-in users from using your app’s SDK API key to preform malicious actions, such as impersonating your other users.
Getting started
There are four high-level steps to get started:
- Server-Side Integration - Generate a public and private key-pair, and use your private key to create a JWT for the current logged-in user.
- SDK Integration - Enable this feature in the Braze SDK and request the JWT generated from your server.
- Adding Public Keys - Add your public key to the Braze dashboard in the Manage Settings page.
- Toggle Enforcement within the Braze dashboard - Toggle this feature’s enforcement within the Braze dashboard on an app-by-app basis.
Server-side integration
Generate a public/private key-pair
Generate an RSA256 public/private key-pair. The public key will eventually be added to the Braze dashboard, while the private key should be stored securely on your server.
We recommend an RSA Key with 2048 bits for use with the RS256 JWT algorithm.
Remember to keep your private keys private. Never expose or hard-code your private key in your app or website. Anyone who knows your private key can impersonate or create users on behalf of your application.
Create a JSON Web Token for the current user
Once you have your private key, your server-side application should use it to return a JWT to your app or website for the currently logged-in user.
Typically, this logic could go wherever your app would normally request the current user’s profile; such as a login endpoint or wherever your app refreshes the current user’s profile.
When generating the JWT, the following fields are expected:
JWT Header
Field | Required | Description |
---|---|---|
alg |
Yes | The supported algorithm is RS256 . |
typ |
Yes | The type should equal JWT . |
JWT Payload
Field | Required | Description |
---|---|---|
sub |
Yes | The “subject” should equal the User ID you supply Braze SDK when calling changeUser |
exp |
Yes | The “expiration” of when you want this token to expire. |
JWT libraries
To learn more about JSON Web Tokens, or to browse the many open source libraries that simplify this signing process, check out https://jwt.io.
SDK integration
This feature is available as of the following SDK versions:
Enable this feature in the Braze SDK.
When this feature is enabled, the Braze SDK will append the current user’s last known JWT to network requests made to Braze Servers.
Don’t worry, initializing with this option alone won’t impact data collection in any way, until you start enforcing authentication within the Braze dashboard.
When calling initialize
, set the optional enableSdkAuthentication
property to true
.
1
2
3
4
5
import * as braze from"@braze/web-sdk";
braze.initialize("YOUR-API-KEY-HERE", {
baseUrl: "YOUR-SDK-ENDPOINT-HERE",
enableSdkAuthentication: true,
});
When configuring the Appboy instance, call setIsSdkAuthenticationEnabled
to true
.
1
2
3
BrazeConfig.Builder brazeConfigBuilder = new BrazeConfig.Builder()
.setIsSdkAuthenticationEnabled(true);
Braze.configure(this, brazeConfigBuilder.build());
Alternatively, you can add <bool name="com_braze_sdk_authentication_enabled">true</bool>
to your braze.xml.
When configuring the Appboy instance, call setIsSdkAuthenticationEnabled
to true
.
1
2
3
BrazeConfig.Builder brazeConfigBuilder = BrazeConfig.Builder()
.setIsSdkAuthenticationEnabled(true)
Braze.configure(this, brazeConfigBuilder.build())
Alternatively, you can add <bool name="com_braze_sdk_authentication_enabled">true</bool>
to your braze.xml.
To enable SDK Authentication, set the configuration.api.sdkAuthentication
property of your BRZConfiguration
object to YES
before initializing the Braze instance:
1
2
3
4
5
6
BRZConfiguration *configuration =
[[BRZConfiguration alloc] initWithApiKey:@"{BRAZE_API_KEY}"
endpoint:@"{BRAZE_ENDPOINT}"];
configuration.api.sdkAuthentication = YES;
Braze *braze = [[Braze alloc] initWithConfiguration:configuration];
AppDelegate.braze = braze;
To enable SDK Authentication, set the configuration.api.sdkAuthentication
property of your Braze.Configuration
object to true
when initializing the SDK:
1
2
3
4
5
let configuration = Braze.Configuration(apiKey: "{YOUR-BRAZE-API-KEY}",
endpoint: "{YOUR-BRAZE-ENDPOINT}")
configuration.api.sdkAuthentication = true
let braze = Braze(configuration: configuration)
AppDelegate.braze = braze
Currently, SDK Authentication must be enabled as part of initializing the SDK in native iOS and Android code. To enable SDK Authentication in the Flutter SDK, follow the integrations for iOS and Android from the other tabs. After SDK Authentication is enabled, the rest of the feature can be integrated in Dart.
Set the current user’s JWT token
Whenever your app calls the Braze changeUser
method, also supply the JWT token that was generated server-side.
You can also configure the token to refresh mid-session for the current user.
Keep in mind that changeUser
should only be called when the User ID has actually changed. You should not use this method as a way to update the signature if the user ID has not changed.
Supply the JWT Token when calling changeUser
:
1
2
import * as braze from "@braze/web-sdk";
braze.changeUser("NEW-USER-ID", "JWT-TOKEN-FROM-SERVER");
Or, when you have refreshed the user’s token mid-session:
1
2
import * as braze from"@braze/web-sdk";
braze.setSdkAuthenticationSignature("NEW-JWT-TOKEN-FROM-SERVER");
Supply the JWT Token when calling appboy.changeUser
:
1
Braze.getInstance(this).changeUser("NEW-USER-ID", "JWT-TOKEN-FROM-SERVER");
Or, when you have refreshed the user’s token mid-session:
1
Braze.getInstance(this).setSdkAuthenticationSignature("NEW-JWT-TOKEN-FROM-SERVER");
Supply the JWT Token when calling appboy.changeUser
:
1
Braze.getInstance(this).changeUser("NEW-USER-ID", "JWT-TOKEN-FROM-SERVER")
Or, when you have refreshed the user’s token mid-session:
1
Braze.getInstance(this).setSdkAuthenticationSignature("NEW-JWT-TOKEN-FROM-SERVER")
Supply the JWT Token when calling changeUser
:
1
[AppDelegate.braze changeUser:@"userId" sdkAuthSignature:@"signature"];
Or, when you have refreshed the user’s token mid-session:
1
[AppDelegate.braze setSDKAuthenticationSignature:@"signature"];
Supply the JWT Token when calling changeUser
:
1
AppDelegate.braze?.changeUser(userId: "userId", sdkAuthSignature: "signature")
Or, when you have refreshed the user’s token mid-session:
1
AppDelegate.braze?.set(sdkAuthenticationSignature: "signature")
Supply the JWT Token when calling changeUser
:
1
braze.changeUser("userId", sdkAuthSignature: "signature")
Or, when you have refreshed the user’s token mid-session:
1
braze.setSdkAuthenticationSignature("signature")
Register a callback function for invalid tokens
When this feature is set as Required, the following scenarios will cause SDK requests to be rejected by Braze:
- JWT was expired by the time is was received by the Braze API
- JWT was empty or missing
- JWT failed to verify for the public keys you uploaded to the Braze dashboard
You can use subscribeToSdkAuthenticationFailures
to subscribe to be notified when the SDK requests fail for one of these reasons. A callback function contains an object with the relevant errorCode
, reason
for the error, the userId
of the request (if the user is not anonymous), and the authentication signature
that caused the error.
Failed requests will periodically be retried until your app supplies a new valid JWT. If that user is still logged in, you can use this callback as an opportunity to request a new JWT from your server and supply the Braze SDK with this new valid token.
These callback methods are a great place to add your own monitoring or error-logging service to keep track of how often your Braze requests are being rejected.
1
2
3
4
5
6
7
import * as braze from"@braze/web-sdk";
braze.subscribeToSdkAuthenticationFailures((error) => {
// TODO: Optionally log to your error-reporting service
// TODO: Check if the `user_id` within the `error` matches the currently logged-in user
const updated_jwt = await getNewTokenSomehow(error);
braze.setSdkAuthenticationSignature(updated_jwt);
});
1
2
3
4
5
6
Braze.getInstance(this).subscribeToSdkAuthenticationFailures(error -> {
// TODO: Optionally log to your error-reporting service
// TODO: Check if the error user matches the currently logged-in user
String newToken = getNewTokenSomehow(error);
Braze.getInstance(getContext()).setSdkAuthenticationSignature(newToken);
});
1
2
3
4
5
6
Braze.getInstance(this).subscribeToSdkAuthenticationFailures({ error: BrazeSdkAuthenticationErrorEvent ->
// TODO: Optionally log to your error-reporting service
// TODO: Check if the `user_id` within the `error` matches the currently logged-in user
val newToken: String = getNewTokenSomehow(error)
Braze.getInstance(getContext()).setSdkAuthenticationSignature(newToken)
})
1
2
3
4
5
6
7
8
9
10
11
12
Braze *braze = [[Braze alloc] initWithConfiguration:configuration];
braze.sdkAuthDelegate = delegate;
AppDelegate.braze = braze;
// Method to implement in delegate
- (void)braze:(Braze *)braze sdkAuthenticationFailedWithError:(BRZSDKAuthenticationError *)error {
// TODO: Optionally log to your error-reporting service
// TODO: Check if the `user_id` within the `error` matches the currently logged-in user
NSLog(@"Invalid SDK Authentication signature.");
NSString *newSignature = getNewSignatureSomehow(error);
[AppDelegate.braze setSDKAuthenticationSignature:newSignature];
}
1
2
3
4
5
6
7
8
9
10
11
12
let braze = Braze(configuration: configuration)
braze.sdkAuthDelegate = delegate
AppDelegate.braze = braze
// Method to implement in delegate
func braze(_ braze: Braze, sdkAuthenticationFailedWithError error: Braze.SDKAuthenticationError) {
// TODO: Optionally log to your error-reporting service
// TODO: Check if the `user_id` within the `error` matches the currently logged-in user
print("Invalid SDK Authentication signature.")
let newSignature = getNewSignatureSomehow(error)
AppDelegate.braze?.set(sdkAuthenticationSignature: newSignature)
}
1
2
3
4
5
6
7
braze.setBrazeSdkAuthenticationErrorCallback((BrazeSdkAuthenticationError error) async {
// TODO: Optionally log to your error-reporting service
// TODO: Check if the `user_id` within the `error` matches the currently logged-in user
print("Invalid SDK Authentication signature.")
let newSignature = getNewSignatureSomehow(error)
braze.setSdkAuthenticationSignature(newSignature);
});
Managing public keys
Adding a public key
You can add up to three public keys for each app: a primary, a secondary, and a tertiary. You can also add the same key to more than one app if needed. To add a public key:
- Go to the Braze dashboard and select Settings > App Settings.
- Choose an app from your list of available apps.
- Under SDK Authentication, select Add Public Key.
- Enter an optional description, paste in your public key, then select Add Public Key.
Assign a new primary key
To assign a secondary or tertiary key as your new primary key:
- Go to the Braze dashboard and select Settings > App Settings.
- Choose an app from your list of available apps.
- Under SDK Authentication, choose a key and select Manage > Make Primary Key.
Deleting a key
To delete a primary key, first assign a new primary, then delete your key. To delete a non-primary key:
- Go to the Braze dashboard and select Settings > App Settings.
- Choose an app from your list of available apps.
- Under SDK Authentication, choose a non-primary key and select Manage > Delete Public Key.
Enabling in the Braze dashboard
Once your Server-side Integration and SDK Integration are complete, you can begin to enable this feature for those specific apps.
Keep in mind that SDK requests will continue to flow as usual without authentication unless the app’s SDK Authentication setting is set to Required in the Braze dashboard.
Should anything go wrong with your integration (for example, your app is incorrectly passing tokens to the SDK, or your server is generating invalid tokens), disable this feature in the Braze dashboard, and data will resume to flow as usual without verification.
Enforcement options
In the dashboard Manage Settings page, each app has three SDK Authentication states which control how Braze verifies requests.
Setting | Description |
---|---|
Disabled | Braze will not verify the JWT supplied for a user. (Default Setting) |
Optional | Braze will verify requests for logged-in users, but will not reject invalid requests. |
Required | Braze will verify requests for logged-in users and will reject invalid JWTs. |
The Optional setting is a useful way to monitor the potential impact this feature will have on your app’s SDK traffic.
Invalid JWT signatures will be reported in both Optional and Required states, however only the Required state will reject SDK requests causing apps to retry and request new signatures.
Analytics
Each app will show a breakdown of SDK Authentication errors collected while this feature is in the Optional and Required state.
Data is available in real-time, and you can hover over points in the chart to see a breakdown of errors for a given date.
Error codes
Error Code | Error Reason | Description |
---|---|---|
10 | EXPIRATION_REQUIRED |
Expiration is a required field for Braze usage. |
20 | DECODING_ERROR |
Non-matching public key or a general uncaught error. |
21 | SUBJECT_MISMATCH |
The expected and actual subjects are not the same. |
22 | EXPIRED |
The token provided has expired. |
23 | INVALID_PAYLOAD |
The token payload is invalid. |
24 | INCORRECT_ALGORITHM |
The algorithm of the token is not supported. |
25 | PUBLIC_KEY_ERROR |
The public key could not be converted into the proper format. |
26 | MISSING_TOKEN |
No token was provided in the request. |
27 | NO_MATCHING_PUBLIC_KEYS |
No public keys matched the provided token. |
28 | PAYLOAD_USER_ID_MISMATCH |
Not all user ids in the request payload match as is required. |
Frequently asked questions
Does this feature need to be enabled on all of my apps at the same time?
No, this feature can be enabled for specific apps and doesn’t need to be used on all of your apps, all at once.
What happens to users who are still on older versions of my app?
When you begin to enforce this feature, requests made by older app versions will be rejected by Braze and retried by the SDK. After users upgrade their app to a supported version, those enqueued requests will begin to be accepted again.
If possible, you should push users to upgrade as you would for any other mandatory upgrade. Alternatively, you can keep the feature Optional until you see that an acceptable percentage of users have upgraded.
What expiration should I use when generating JWT tokens?
We recommend using the higher value of average session duration, session cookie/token expiration, or the frequency at which your application would otherwise refresh the current user’s profile.
What happens if a JWT expires in the middle of a user’s session?
Should a user’s token expire mid-session, the SDK has a callback function it will invoke to let your app know that a new JWT token is needed to continue sending data to Braze.
What happens if my server-side integration breaks and I can no longer create a JWT?
If your server is not able to provide JWT tokens or you notice some integration issue, you can always disable the feature in the Braze dashboard.
Once disabled, any pending failed SDK requests will eventually be retried by the SDK and accepted by Braze.
Why does this feature use public/private keys instead of shared secrets?
When using shared secrets, anyone with access to that shared secret, such as the Braze dashboard page, would be able to generate tokens and impersonate your end-users.
Instead, we use public/private keys so that not even Braze Employees (let alone your dashboard users) have access to your private keys.
How will rejected requests be retried?
When a request is rejected because of an authentication error, the SDK will invoke your callback used to refresh the user’s JWT signature.
Requests will retry periodically using an exponential backoff approach. After 50 consecutive failed attempts, retries will be paused until the next session start. Each SDK also has a method to manually request a data flush.