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
- Generate a signing key pair. The private key stays on your servers.
- Send Optimove the handoff package described below — public key,
kid, algorithm, tenant ID, and starting enforcement mode. - Optimove registers the key and sets enforcement to
optional. - Enable auth on every client you ship — Web, iOS, and Android.
- Once adoption looks healthy, ask Optimove to move you from
optionaltoenforced.
Partial enablement plus
enforcedreturns401on the clients you skipped. Do not requestenforceduntil 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.
| Parameter | Required | Description |
|---|---|---|
| Tenant ID | Yes | Your Optimove tenant identifier. |
| Public key (PEM) | Yes | SPKI format — the block that opens with the standard PEM public-key header. Never send the private key. |
kid | Yes | The key ID you will place in every JWT header. A stable string, maximum 128 characters. Once retired, a kid can never be reused. |
| Algorithm | Yes | RS256 (RSA) or ES256 (ECDSA P-256). Must match the key type. |
| Starting enforcement mode | Yes | Use optional for first enablement. Do not start on enforced unless every client already sends JWTs. |
| Platforms you will enable | Yes | Web, 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.pemES256 (ECDSA P-256)
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out private.pem
openssl pkey -in private.pem -pubout -out public.pempublic.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
kid- Uniqueness — pick a unique, readable value per key, for example
2026-08-prod-1. - Consistency — put the same
kidin 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
kidcannot 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
| Parameter | Required | Description |
|---|---|---|
alg | Yes | RS256 or ES256. Must match the registered key. |
kid | Yes | Must match the kid registered with Optimove. A missing or unknown kid is rejected. |
Payload
| Parameter | Required | Description |
|---|---|---|
sub | Yes | The same user ID you pass to the Optimove SDK via setUserId (your customer ID). A mismatch is rejected. |
exp | Yes | Unix 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
setUserIdassociates 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: 1even when no token provider is configured. Inenforcedmode that case is a401withMISSING_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
| Mode | What happens | When to use |
|---|---|---|
off | No JWT checks. The default before onboarding. | Auth not in use. |
optional | JWTs are verified when present, but requests are never rejected. Adoption is counted. | First enablement — ship your clients and watch adoption. |
enforced | Invalid 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
- Generate a new key pair and a new
kid. Do not reuse a retiredkid. - Send Optimove the new public key,
kidand algorithm. It is added alongside the existing key, up to the 10-active-key limit. - Wait up to 1 hour, then start minting JWTs with the new
kid. - Once no traffic uses the old
kid, ask Optimove to retire it. - Keep signing with the new key. A retired
kidcannot be brought back.
To turn auth off entirely, ask Optimove to set enforcement to
offbefore the last active key is removed. Removing the last key while enforcement isoptionalorenforcedis blocked.
Updated about 2 hours ago
