Skip to content

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:

  1. 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.

  2. SDK Integration - Enable this feature in the Braze SDK and request the JWT generated from your server.

  3. Adding Public Keys - Add your public key to the Braze dashboard in the Manage Settings page.

  4. 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.

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.
aud No The “audience” claim is optional, and if set should equal braze
iss No The “issuer” claim is optional, and if set should equal your SDK API Key.

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.

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.

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.

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);
  appboy.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:

  1. Go to the Braze dashboard and select Settings > App Settings.
  2. Choose an app from your list of available apps.
  3. Under SDK Authentication, select Add Public Key.
  4. 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:

  1. Go to the Braze dashboard and select Settings > App Settings.
  2. Choose an app from your list of available apps.
  3. 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:

  1. Go to the Braze dashboard and select Settings > App Settings.
  2. Choose an app from your list of available apps.
  3. 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.

A chart showing the number of instances of authentication errors. Also shown are the total number of errors, error type, and adjustable date range.

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.

HOW HELPFUL WAS THIS PAGE?
New Stuff!