Federated Authentication in the Optimove SDK

Federated Authentication lets your backend prove which end user is making an Optimove SDK request. You hold the private signing key; Optimove holds only the matching public key. Your apps ask your backend for a short-lived JWT whenever an identified user is active, and the SDK attaches it to outgoing requests.

Visitors — anonymous users with no account — do not need a JWT.

🔒

Optimove never receives your private key and cannot mint tokens on your users' behalf. Store the private key on your backend or in your KMS only.

Prerequisites

  1. Generate a signing key pair. The private key stays on your servers.
  2. Send Optimove the handoff package described below — public key, kid, algorithm, tenant ID, and starting enforcement mode.
  3. Optimove registers the key and sets enforcement to optional.
  4. Enable auth on every client you ship — Web, iOS, and Android.
  5. Once adoption looks healthy, ask Optimove to move you from optional to enforced.
⚠️

Partial enablement plus enforced returns 401 on the clients you skipped. Do not request enforced until every platform you ship is sending JWTs.

After Optimove changes a key or an enforcement mode, allow up to 1 hour before the new setting is visible on every service.

What to Send Optimove

Send this once per tenant, and again each time you rotate a key.

ParameterRequiredDescription
Tenant IDYesYour Optimove tenant identifier.
Public key (PEM)YesSPKI format — the block that opens with the standard PEM public-key header. Never send the private key.
kidYesThe key ID you will place in every JWT header. A stable string, maximum 128 characters. Once retired, a kid can never be reused.
AlgorithmYesRS256 (RSA) or ES256 (ECDSA P-256). Must match the key type.
Starting enforcement modeYesUse optional for first enablement. Do not start on enforced unless every client already sends JWTs.
Platforms you will enableYesWeb, iOS, Android — list every platform you ship. Auth must be live on each before enforced.

Example handoff:

tenant_id: YOUR_TENANT_ID
algorithm: RS256
kid: 2026-08-prod-1
enforcement_mode: optional
platforms: web, ios, android

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
-----END PUBLIC KEY-----

Create a Signing Key

Use OpenSSL, or the equivalent in your KMS or secrets manager. Recommended: RS256 with a 2048-bit RSA key, or ES256 with a P-256 key.

RS256 (RSA 2048)

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem
openssl rsa -in private.pem -pubout -out public.pem

ES256 (ECDSA P-256)

openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem

public.pem must be in SPKI format — the block that opens with the standard PEM public-key header. PKCS#1 blocks, which open with an RSA-specific public-key header, are not accepted.

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
-----END PUBLIC KEY-----

Choosing a kid

  • Uniqueness — pick a unique, readable value per key, for example 2026-08-prod-1.
  • Consistency — put the same kid in the JWT header of every token you mint with that key.
  • Rotation overlap — you can register up to 10 active keys at once.
  • Permanence — a retired kid cannot be registered again.

The JWT Your Backend Mints

Your backend exposes an endpoint your apps call — for example getToken(userId) — which returns a signed JWT.

Header

ParameterRequiredDescription
algYesRS256 or ES256. Must match the registered key.
kidYesMust match the kid registered with Optimove. A missing or unknown kid is rejected.

Payload

ParameterRequiredDescription
subYesThe same user ID you pass to the Optimove SDK via setUserId (your customer ID). A mismatch is rejected.
expYesUnix expiry timestamp. Use a short lifetime of 5–15 minutes. Some Optimove services reject tokens that omit exp — always include it.

iss, aud, iat and jti are not validated. You may include them as convention — aud is often "optimove" — but they are not required.

{
  "sub": "user-123",
  "exp": 1776200000
}

Your SDK token-provider callback must return this JWT as a string. The SDK attaches it to requests as the X-User-JWT header. Mint tokens on your backend only — never in the app, where the signing key would be exposed.

For how setUserId associates a visitor with a known customer, see the Registering a New Customer guide.

Enable the SDKs

Enable auth on every client you ship before asking for enforced.

⚠️

Auth-capable SDK versions send X-Optimove-Auth-Capable: 1 even when no token provider is configured. In enforced mode that case is a 401 with MISSING_JWT — not a silent pass.

Web

Web auth is opt-in through a script attribute and a later configureAuth() call. Both are required — if the script attribute is missing, configureAuth() is ignored.

Add the data-optimove-auth-required="true" attribute to your existing Web SDK script tag:

<script
  src="https://sdk-retrieval-service.optimove.net/websdk/?tenant_id=YOUR_TENANT_ID"
  data-optimove-auth-required="true"> 
</script>
optimoveSDK.API.configureAuth({
  getToken: async (userId) => {
    // Call your backend; return the signed JWT for this userId
    const res = await fetch("/your-auth/token", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ userId })
    });
    const { token } = await res.json();
    return token;
  },
  onAuthError: (error) => {
    // Optional: log / metric
  }
});

Call configureAuth() as soon as the SDK is ready, then call setUserId(). Identified requests wait until configureAuth() has run, and time out if it never does.

iOS

Set the provider on the config builder before initialize(). There is no later configureAuth() equivalent on iOS.

let config = OptimoveConfigBuilder(/* your credentials */)
    .enableAuth { userId, completion in
        // Call your backend; return the signed JWT for this userId
        YourAuthService.fetchToken(for: userId) { jwt, error in
            completion(jwt, error)
        }
    }
    .build()

Optimove.initialize(with: config)

If your provider fails, the SDK drops the request rather than sending it without a JWT.

Android

Same timing as iOS — call enableAuth on the builder before initialize().

OptimoveConfig config = new OptimoveConfig.Builder(optimoveCreds, optimobileCreds)
    .enableAuth((userId, callback) -> {
        // Call your backend; return the signed JWT for this userId
        YourAuthService.fetchToken(userId, (jwt, error) -> {
            callback.onComplete(jwt, error);
        });
    })
    .build();

Optimove.initialize(application, config);

The provider may be invoked from a background thread, and the callback may complete on any thread.

Enforcement Modes

ModeWhat happensWhen to use
offNo JWT checks. The default before onboarding.Auth not in use.
optionalJWTs are verified when present, but requests are never rejected. Adoption is counted.First enablement — ship your clients and watch adoption.
enforcedInvalid or missing JWTs are rejected with 401. Older SDKs that cannot send a JWT are dropped silently on write paths.After Web is live and mobile adoption is high enough.

Web can move from optional to enforced quickly, since it updates through the CDN. Mobile should stay on optional until enough of your install base is running an auth-enabled app version.

Ask Optimove when you want the mode changed — it does not flip automatically. After the change, allow up to 1 hour for every service to pick it up.

Rotate a Key

  1. Generate a new key pair and a new kid. Do not reuse a retired kid.
  2. Send Optimove the new public key, kid and algorithm. It is added alongside the existing key, up to the 10-active-key limit.
  3. Wait up to 1 hour, then start minting JWTs with the new kid.
  4. Once no traffic uses the old kid, ask Optimove to retire it.
  5. Keep signing with the new key. A retired kid cannot be brought back.
⚠️

To turn auth off entirely, ask Optimove to set enforcement to off before the last active key is removed. Removing the last key while enforcement is optional or enforced is blocked.


Did this page help you?