Skip to content

Opti ID Integration Specification

advanced
📜AdvancedOpti ID

Why enterprise identity integration matters

Section titled “Why enterprise identity integration matters”

Organizations adopting Optimizely need their existing identity infrastructure to work seamlessly with the platform. Without proper integration, administrators face manual user provisioning, employees manage yet another password, and security teams lose visibility into access patterns. Opti ID bridges this gap by supporting industry-standard identity protocols.

This reference covers the technical details of connecting Opti ID to your identity provider (IdP) using SAML 2.0 or OpenID Connect, automating user lifecycle management through SCIM, and configuring session and MFA policies.


Opti ID acts as a SAML 2.0 Service Provider (SP). Your enterprise identity provider (IdP) — such as Entra ID, Okta, or Ping Identity — authenticates users and sends SAML assertions to Opti ID.

FieldValue
Entity IDhttps://id.optimizely.com/saml/metadata/{org-id}
ACS URLhttps://id.optimizely.com/saml/acs/{org-id}
SLO URLhttps://id.optimizely.com/saml/slo/{org-id}
Metadata URLhttps://id.optimizely.com/saml/metadata/{org-id}
NameID formaturn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
BindingHTTP-POST (ACS), HTTP-Redirect (SLO)

Replace {org-id} with your Optimizely organization identifier, found in Opti ID Admin > Organization Settings.

Your IdP must send these attributes in the SAML assertion:

AttributeSAML claimRequiredDescription
Emailhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddressYesPrimary user identifier
First namehttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/givennameYesUser’s given name
Last namehttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/surnameYesUser’s family name
Groupshttp://schemas.xmlsoap.org/claims/GroupNoGroup memberships for role mapping
Departmenthttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/departmentNoUsed for organization-level role mapping
<!-- Example IdP metadata requirements -->
<md:EntityDescriptor entityID="https://your-idp.example.com">
<md:IDPSSODescriptor
WantAuthnRequestsSigned="true"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<!-- Your IdP signing certificate -->
</md:KeyDescriptor>
<md:SingleSignOnService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://your-idp.example.com/sso/saml" />
</md:IDPSSODescriptor>
</md:EntityDescriptor>
SettingValue
AuthnRequest signingRequired (RSA-SHA256)
Assertion signature validationRequired
Assertion encryptionOptional (AES-256-CBC)
Certificate formatX.509, PEM-encoded
Minimum key length2048-bit RSA
  1. Download SP metadata from https://id.optimizely.com/saml/metadata/{org-id}
  2. Import SP metadata into your IdP
  3. Configure attribute mappings in your IdP
  4. Export IdP metadata XML
  5. Upload IdP metadata in Opti ID Admin > SSO > SAML Configuration
  6. Test with a non-admin user before enabling for the organization
  7. Enable SAML for the organization

Opti ID supports OIDC as an alternative to SAML. OIDC is typically simpler to configure and better suited for modern identity providers.

Register Opti ID as an OIDC Relying Party (RP) in your IdP:

FieldValue
Client typeConfidential
Redirect URIhttps://id.optimizely.com/oidc/callback/{org-id}
Post-logout redirect URIhttps://id.optimizely.com/oidc/logout-callback/{org-id}
Response typecode
Grant typeAuthorization Code with PKCE
Token endpoint authclient_secret_post or private_key_jwt
ScopePurpose
openidRequired for OIDC
profileFirst name, last name
emailEmail address
groupsGroup memberships (optional, for role mapping)

If your IdP supports OIDC Discovery, provide the well-known URL:

https://your-idp.example.com/.well-known/openid-configuration

Opti ID reads issuer, authorization, token, userinfo, and JWKS endpoints automatically from the discovery document.

If discovery is unavailable, configure endpoints individually in Opti ID Admin > SSO > OIDC Configuration:

EndpointDescription
AuthorizationWhere Opti ID redirects users to authenticate
TokenWhere Opti ID exchanges the authorization code
UserInfoWhere Opti ID fetches user profile attributes
JWKS URIWhere Opti ID retrieves public keys for token validation
End sessionWhere Opti ID redirects for logout (optional)
Opti ID fieldStandard claimFallback claim
Emailemailpreferred_username
First namegiven_namename (split)
Last namefamily_namename (split)
Groupsgroupsroles

The id_token alone often omits name claims (Okta returns them from the userinfo endpoint, not the token). After exchanging the authorization code, the portal calls the userinfo endpoint with the access token and merges those attributes over the id_token claims, then resolves the display name in this order:

  1. name
  2. given_name + family_name (composed)
  3. preferred_username
  4. the email local-part (last-resort fallback)

The userinfo fetch is non-blocking — if it fails, login still succeeds using the id_token claims. Accounts previously stored with the email local-part are repaired automatically on their next sign-in.

Company capture from Opti ID is under evaluation: company is not a standard OIDC claim, so the exact source attribute is being confirmed from live userinfo payloads before it is auto-populated. Admins can set a user’s company manually in Certification Admin > Users in the meantime.


SCIM 2.0 automates user lifecycle management — creating accounts when employees join, updating attributes when roles change, and deactivating accounts when employees leave.

https://id.optimizely.com/scim/v2/{org-id}

SCIM requests require a bearer token:

Authorization: Bearer {scim-token}

Generate SCIM tokens in Opti ID Admin > Provisioning > SCIM Configuration.

Create user:

POST /scim/v2/{org-id}/Users
Content-Type: application/scim+json
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "jane.doe@example.com",
"name": {
"givenName": "Jane",
"familyName": "Doe"
},
"emails": [
{
"primary": true,
"value": "jane.doe@example.com",
"type": "work"
}
],
"active": true
}

Update user (PATCH):

PATCH /scim/v2/{org-id}/Users/{user-id}
Content-Type: application/scim+json
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "replace",
"path": "name.familyName",
"value": "Smith"
}
]
}

Deactivate user:

PATCH /scim/v2/{org-id}/Users/{user-id}
Content-Type: application/scim+json
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "replace",
"path": "active",
"value": false
}
]
}

List users with filter:

GET /scim/v2/{org-id}/Users?filter=userName eq "jane.doe@example.com"

Create group:

POST /scim/v2/{org-id}/Groups
Content-Type: application/scim+json
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "CMS Editors",
"members": [
{ "value": "{user-id}", "display": "Jane Doe" }
]
}

Add member to group:

PATCH /scim/v2/{org-id}/Groups/{group-id}
Content-Type: application/scim+json
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "add",
"path": "members",
"value": [{ "value": "{user-id}" }]
}
]
}

Map IdP groups to Optimizely roles in Opti ID Admin > Provisioning > Role Mapping:

IdP groupOptimizely roleProducts
OptimizelyAdminsOrganization AdminAll
CMSEditorsCMS EditorCMS
CMSAdminsCMS AdminCMS
ExperimentationUsersExperimenterExperimentation
AnalyticsViewersViewerAnalytics
OperationUsersGroups
GET (list)YesYes
GET (single)YesYes
POST (create)YesYes
PUT (replace)YesYes
PATCH (update)YesYes
DELETEDeactivate onlyYes

Opti ID issues two token types after authentication:

Access token (JWT):

ClaimDescription
subUser ID
emailUser email
org_idOrganization ID
rolesArray of role assignments
productsArray of product entitlements
expExpiration timestamp
isshttps://id.optimizely.com
audTarget product identifier

Refresh token (opaque):

  • Used to obtain new access tokens without re-authentication
  • Stored server-side, referenced by opaque handle
  • Bound to the originating device and IP range (configurable)
SettingDefaultConfigurable range
Access token TTL15 minutes5–60 minutes
Refresh token TTL8 hours1–24 hours
Absolute session timeout12 hours4–24 hours
Idle session timeout30 minutes15–120 minutes
Remember me duration30 days7–90 days

Configure session lifetimes in Opti ID Admin > Security > Session Policy.

1. Client detects access token expiration
2. Client sends refresh token to POST /oauth/token
3. Opti ID validates refresh token
4. Opti ID issues new access token + rotated refresh token
5. Old refresh token is invalidated (rotation)
POST /api/v1/sessions/revoke
Authorization: Bearer {admin-access-token}
Content-Type: application/json
{
"user_id": "{user-id}",
"scope": "all" // "all" | "current" | "other"
}

Multi-factor authentication adds a second verification step after password authentication.

MethodDescriptionRecommended for
TOTPTime-based one-time password (authenticator app)All users
WebAuthn/FIDO2Hardware security keys, biometricsHigh-security environments
SMSText message codesFallback only (less secure)
EmailEmail verification codesLow-risk environments

Configure in Opti ID Admin > Security > MFA Policy:

{
"mfa_policy": {
"enforcement": "required",
"allowed_methods": ["totp", "webauthn"],
"grace_period_days": 14,
"remember_device_days": 30,
"admin_override": false,
"bypass_for_sso": true
}
}
SettingOptionsDescription
enforcementdisabled, optional, requiredMFA requirement level
allowed_methodsArray of method identifiersWhich MFA methods users can choose
grace_period_days0–30Days before enforcement after policy change
remember_device_days0–90Days a trusted device skips MFA. 0 means always require.
admin_overridebooleanWhether org admins can exempt specific users
bypass_for_ssobooleanSkip Opti ID MFA when IdP handles MFA via SAML/OIDC
POST /api/v1/mfa/enroll
Authorization: Bearer {user-access-token}
Content-Type: application/json
{
"method": "totp"
}

Response:

{
"method": "totp",
"provisioning_uri": "otpauth://totp/Optimizely:jane@example.com?secret=BASE32SECRET&issuer=Optimizely",
"secret": "BASE32SECRET",
"qr_code_url": "https://id.optimizely.com/mfa/qr/{enrollment-id}"
}
POST /api/v1/mfa/verify
Authorization: Bearer {user-access-token}
Content-Type: application/json
{
"method": "totp",
"code": "123456"
}

CodeHTTP statusDescription
saml_invalid_signature401SAML assertion signature validation failed
saml_expired_assertion401SAML assertion has expired
saml_missing_attribute400Required SAML attribute not present
oidc_invalid_token401OIDC token validation failed
oidc_invalid_redirect400Redirect URI does not match registered URIs
scim_duplicate_user409User with this email already exists
scim_invalid_filter400SCIM filter syntax is invalid
scim_rate_limited429Too many SCIM requests
mfa_invalid_code401MFA verification code is incorrect
session_expired401Session has timed out