On this page
Secure API connections between orgs with OAuth 2.0
This guide explains how to securely configure Okta hub-and-spoke orgs to synchronize users and groups using OAuth 2.0 in a multi-tenant solution. In the hub-and-spoke model, the spoke org is also referred to as the source org and the hub org is the target org. The provisioning connection between Okta orgs is configured as an OAuth 2.0 Org2Org app integration (opens new window). This connection provides API access to scoped data using the OAuth 2.0 Client Credential flow.
Note: Currently, only the Okta API can be used to enable OAuth 2.0-based provisioning.
Learning outcomes
- Set up a hub-and-spoke Okta org provisioning connection to use the OAuth 2.0 Client Credentials grant flow.
- Configure the hub Okta org with service apps for each spoke Okta org.
- Configure an Org2Org app on each spoke Okta org.
- Configure and activate Org2Org provisioning on spoke Okta orgs.
- Rotate keys for the OAuth 2.0 connection.
What you need
Multiple Okta orgs for your multi-tenant solution
Note: The Okta Org2Org app integration isn't available in Okta Developer Edition orgs. If you need to test this feature in your developer org, contact your Okta account team.
OAuth 2.0 for secure API connections in multi-tenant solutions
To secure API connections between orgs in a hub-and-spoke multi-tenant solution model, use the Okta Org2Org integration as an OAuth 2.0 client. In this model, the spoke org is referred to as the source org and the hub org is the target org.
Previously, the Org2Org integration only supported token-based access to the Okta APIs for provisioning requests. You can now configure the Org2Org integration to access Okta APIs as an OAuth 2.0 client using the OAuth 2.0 Client Credentials flow. The OAuth 2.0 API access enables you to increase security by limiting the scope of resource access and providing a better mechanism to rotate credentials.
In this configuration:
The hub org acts as the resource server and the authorization server
Each spoke org represents a service app in the hub org for the OAuth 2.0 Client Credentials flow
At the spoke org level, an Org2Org app represents the OAuth 2.0 client
To limit access to the OAuth 2.0 clients, the hub-org service app needs to be granted with allowed OAuth 2.0 scopes (opens new window).
After you configure the OAuth 2.0 connection, test your connection: push user data from the spoke orgs to the hub org. You can also create a Rotate key schedule to update your credentials regularly.
Note: For provisioning connections that use API tokens instead of OAuth 2.0, see Integrate Okta Org2Org with Okta (opens new window).
Hub and spoke connection configuration with OAuth 2.0
You can push user and group information from a spoke org to a centralized hub org with OAuth 2.0 by performing the following configuration:
- In each spoke org, add an instance of the Org2Org app integration and save the generated JSON Web Key Set (JWKS) public key.
- In the hub org, create an OAuth 2.0 service app for each spoke org with the corresponding Org2Org app JWKS public key. For each hub-org service app (the OAuth 2.0 client), assign admin roles and grant allowed scopes.
- In each spoke org, set and activate provisioning in the Org2Org apps from the Okta API.
- In each spoke org, assign users and groups in the spoke Org2Org apps to synchronize with the hub org.
Add an Org2Org app integration in a spoke org
You use the spoke org to push users and groups to the central hub org. In the spoke org, add an instance of the Org2Org app integration by using the Apps API (opens new window). This generates an integration instance with the key certificates required to connect to the hub org.
Note: You can't use an Okta Developer Edition org as a spoke org since the Okta Org2Org app integration isn't available. If you need to test this feature in your developer org, contact your Okta account team.
As an Okta admin, make a POST /api/v1/apps
request to the spoke org with Okta Org2Org parameters (opens new window):
Parameter | Description/Value |
---|---|
name | okta_org2org |
label | Specify a label for this Org2Org app integration |
baseUrl | Specify the base URL of your hub org |
signOnMode | You can set this parameter to any valid value, but if you specify SAML_2_0 , the Org2Org app signing certificate appears in the Admin Console. |
Request example
curl -v -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS {spokeApiToken}" \
-d '{
"name": "okta_org2org",
"label": "'{spokeOrg2OrgClientLabel}'",
"signOnMode": "SAML_2_0",
"settings": {
"app": {
"baseUrl": "https://'{yourHubOktaDomain}'"
}
}
}' "https://{yourSpokeOktaDomain}/api/v1/apps"
From the response of your POST request, use the id
property of the Org2Org app instance to retrieve the key credentials (opens new window) generated for the app with the GET /api/v1/apps/{id}/credentials/keys
request.
Request example
curl -v -X GET \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS {spokeApiToken}" \
"https://{yourOktaDomain}/api/v1/apps/{yourOrg2OrgAppId}/credentials/keys"
Response example
[
{
"kty": "RSA",
"created": "2022-01-20T19:50:14.000Z",
"lastUpdated": "2022-01-20T19:50:14.000Z",
"expiresAt": "2024-01-20T19:50:13.000Z",
"kid": "sf-jWwRKMUU55 ... GucHLxIh_-fYLAofB8",
"use": "sig",
"x5c": [
"MIIDqDCCApCgAwIBAgIGAX55CiDiMA0GCSqG ... c5Iuo9j3wpemDSgGapXQ=="
],
"x5t#S256": "v-v2V8soFmXuhC ... nrJ4ho-N3P8aASFc",
"e": "AQAB",
"n": "gIxwqCNkdAb1ioyNBY2boqUCrMj_NSFJAl ... 7dZFiAYF7p_k3XMXOh-hsL_D8FDQ"
}
]
Note: The keys are truncated for brevity.
Save the generated credentials to configure the corresponding hub-org service app.
Create an OAuth 2.0 service app in the hub org
In the hub org, create an OAuth 2.0 service app for each spoke org by using the Dynamic Client Registration API (opens new window). As an Okta admin, make a POST /oauth2/v1/clients
request to the hub org with the following required parameters:
Parameter | Description/Value |
---|---|
client_name | Specify a label for this service app to represent the spoke org OAuth 2.0 client |
grant_types | client_credentials |
jwks | Specify the JWKS from the corresponding spoke org’s Org2Org app integration |
response_types | token |
token_endpoint_auth_method | private_key_jwt |
application_type | service |
Request example
curl -X POST \
-H 'Accept: application/json' \
-H "Authorization: SSWS {hubApiToken}" \
-H 'Content-Type: application/json' \
-d '{
"client_name": "'{hubServiceClientLabel}'",
"response_types": [
"token"
],
"grant_types": [
"client_credentials"
],
"token_endpoint_auth_method": "private_key_jwt",
"application_type": "service",
"jwks": {
"keys": [
{
"kty": "RSA",
"created": "2022-01-20T19:50:14.000Z",
"lastUpdated": "2022-01-20T19:50:14.000Z",
"expiresAt": "2024-01-20T19:50:13.000Z",
"kid": "sf-jWwRKMUU55 ... GucHLxIh_-fYLAofB8",
"use": "sig",
"x5c": [
"MIIDqDCCApCgAwIBAgIGAX55CiDiMA0GCSqG ... c5Iuo9j3wpemDSgGapXQ=="
],
"x5t#S256": "v-v2V8soFmXuhC ... nrJ4ho-N3P8aASFc",
"e": "AQAB",
"n": "gIxwqCNkdAb1ioyNBY2boqUCrMj_NSFJAl ... 7dZFiAYF7p_k3XMXOh-hsL_D8FDQ"
}
]
}
}' "https://{yourHubOktaDomain}/oauth2/v1/clients"
Note: You can copy the entire response from the previous
GET /api/v1/apps/{id}/credentials/keys
request into thejwks.keys
array. Thecreated
,lastUpdated
, andexpiresAt
properties are ignored in the POST request. Alternatively, you can remove them from thejwks.keys
parameter body.
Assign admin roles to the OAuth 2.0 service app
Assign admin roles for every OAuth 2.0 service app that you create in the hub org. Service apps with assigned admin roles are constrained to the permissions and resources that are included in the role. This improves security for an org since it ensures that service apps only have access to the resources that are needed to perform their tasks.
You can assign a standard admin role (opens new window) or a custom admin role (opens new window) with permissions to specific resource sets.
Note: To temporarily bypass assigning an admin role, enable the Public client app admins org setting. This automatically assigns the super admin role to custom API service apps that you create after the scopes are granted. Go to Settings > Account > Public client app admins in the Admin Console to edit this setting. See Assign admin roles to apps (opens new window). Disable this setting after you incorporate admin role assignments in your workflow.
For the OAuth 2.0 Org2Org provisioning connection, Okta recommends that you assign the following standard admin roles:
USER_ADMIN
(Group administrator)GROUP_MEMBERSHIP_ADMIN
(Group membership administrator)
You can use the Admin Console to assign an admin role to your service app. See Assign admin roles to apps (opens new window) and go to the Admin roles tab from your app integration details. Alternatively, you can assign the admin role to your service app with the Okta API.
As an Okta super admin, make a POST /oauth2/v1/clients/{yourServiceAppId}/roles
request to the hub org with the following required parameters to assign an admin role:
Parameter | Description/Value |
---|---|
yourServiceAppId | Specify the client_id value from the previous response when the service app was created. In the following role assignment example, the {yourServiceAppId} variable name is used instead of client_id . |
type | Specify the admin role to assign to the service app. Use the recommended standard admin roles (USER_ADMIN , GROUP_MEMBERSHIP_ADMIN ). |
See Assign a Role to a client app (opens new window) in the Role Assignment API reference.
Note: Only Okta super admins (opens new window) can assign roles.
Request example
curl -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS {hubApiToken}" \
-d '{
"type": "USER_ADMIN"
}' "https://{yourHubOrgDomain}/oauth2/v1/clients/{yourServiceAppId}/roles"
Note: The admin roles determine which resources the admin can perform the actions on (such as a specific group of users or a specific set of apps). Scopes determine the action that the admin can perform (such as manage users, read apps). Therefore, the admin roles need to have enough permissions for the scopes provided.
Grant allowed scopes to the OAuth 2.0 client
From the response of the previous POST request (opens new window), use the client_id
property of the hub service app instance to grant the allowed scopes with the POST /api/v1/apps/{client_id}/grants
(opens new window) request. In the following example, the {yourServiceAppId}
variable name is used instead of client_id
.
Note: Currently, both
okta.users.manage
andokta.groups.manage
scopes are required for the service app configuration.
Request examples
curl -X POST \
-H 'Accept: application/json' \
-H "Authorization: SSWS {hubApiToken}" \
-H 'Content-Type: application/json' \
-d ' {
"scopeId": "okta.users.manage",
"issuer": "https://'{yourHubOrgDomain}'"
}' "https://{yourHubOrgDomain}/api/v1/apps/{yourServiceAppId}/grants"
curl -X POST \
-H 'Accept: application/json' \
-H "Authorization: SSWS {hubApiToken}" \
-H 'Content-Type: application/json' \
-d ' {
"scopeId": "okta.groups.manage",
"issuer": "https://'{yourHubOrgDomain}'"
}' "https://{yourHubOrgDomain}/api/v1/apps/{yourServiceAppId}/grants"
Enable provisioning in the Org2Org app
In each spoke org, set and activate provisioning for the Org2Org app integration by using the Apps API (opens new window).
Note: Currently, you can only enable OAuth 2.0-based provisioning with the Okta API.
As an Okta admin, make a POST /api/v1/apps/{Org2OrgAppId}/connections/default?activate=TRUE
(opens new window) request to set provisioning for the spoke org using the following profile parameters:
Parameter | Description/Value |
---|---|
authScheme | OAUTH2 |
clientId | Specify the corresponding service app client ID in your hub org |
Request example
curl -X POST \
-H 'Accept: application/json' \
-H "Authorization: SSWS {spokeApiToken}" \
-H 'Content-Type: application/json' \
-d ' {
"profile": {
"authScheme": "OAUTH2",
"clientId": "'{yourHubOrgServiceAppId}'"
}
}' "https://{yourSpokeOrgDomain}/api/v1/apps/{yourOrg2OrgAppId}/connections/default?activate=TRUE"
Note: After you enable provisioning, if you want to enable app features or edit Org2Org attribute mappings, use the App features operation (opens new window) or the Profile Mappings API (opens new window). Alternatively, go to the Org2Org app Provisioning > To App settings from the Admin Console and edit the Provisioning To App or the Okta Org2Org Attribute Mappings sections.
Assign users and groups in the Org2Org app
In each spoke (source) org, assign the users and groups to the Org2Org app integration by using the Okta Application Users API (opens new window):
POST /api/v1/apps/{yourOrg2OrgAppId}/users
(Assign user to an app for SSO and provisioning) (opens new window)POST /api/v1/apps/{yourOrg2OrgAppId}/groups/{groupId}
(Assign group to an app) (opens new window)
Alternatively, you can assign users and groups for provisioning using the Okta Admin Console. See Assign an app integration to a user (opens new window) and Assign an app integration to a group (opens new window).
After you've assigned your users or groups in the spoke org, validate that the same users or groups appear in your hub org.
Key rotation
An advantage to using the OAuth 2.0 connection is that you can rotate keys to adhere to cryptographic best practices. You can rotate keys for a specific OAuth 2.0 connection by following these API steps:
- Generate a new key for the Org2Org app
- Register the new key with the corresponding service app
- Update the current credentials for the Org2Org app
Note: If you want to minimize downtime during key rotation, you can update the service app (step two) with both the old and new keys, since
jwks.keys
is an array that can handle differentkid
identifiers. You can remove the old key after you verify that provisioning works with the new key.
Generate a new key for the Org2Org app
From your spoke org, make a request to generate a new app key credential (opens new window) as an Okta admin.
Request example
curl -X POST \
-H 'Accept: application/json' \
-H "Authorization: SSWS {spokeApiToken}" \
-H 'Content-Type: application/json' \
"https://{yourSpokeOrgDomain}/api/v1/apps/{yourOrg2OrgAppId}/credentials/keys/generate?validityYears={validYears}"
Response example
{
"kty": "RSA",
"created": "2022-01-20T19:50:14.000Z",
"lastUpdated": "2022-01-20T19:50:14.000Z",
"expiresAt": "2024-01-20T19:50:13.000Z",
"kid": "sf-jWwRKMUU5588aokhj-xu_mGucHLxIh_-fYLAofB8",
"use": "sig",
"x5c": [
"MIIDqDCCApCgAwIBAgIGAX55C ... Iuo9j3wpemDSgGapXQ=="
],
"x5t#S256": "v-v2V8soFmXuhCAhta8wycA9lXKnrJ4ho-N3P8aASFc",
"e": "AQAB",
"n": "gIxwqCNkdAb1ioyNBY2boqUCrMj_NS ... FiAYF7p_k3XMXOh-hsL_D8FDQ"
}
Note: The keys are truncated for brevity.
Save the generated credentials to update the keys in your Org2Org and service apps.
Register the new Org2Org app key with the corresponding service app
On your hub org, register the key generated from the previous POST request to your corresponding service app by using the Dynamic Client Registration API (opens new window).
Request example
curl -X PUT \
-H 'Accept: application/json' \
-H "Authorization: SSWS {hubApiToken}" \
-H 'Content-Type: application/json' \
-d ' {
"client_id": "'{yourServiceAppId}'",
"client_name": "'{hubServiceClientLabel}'",
"response_types": [
"token"
],
"grant_types": [
"client_credentials"
],
"token_endpoint_auth_method": "private_key_jwt",
"application_type": "service",
"jwks": {
"keys": [
{
"kty": "RSA",
"kid": "sf-jWwRKMUU5588aokhj-xu_mGucHLxIh_-fYLAofB8",
"use": "sig",
"x5c": [
"MIIDqDCCApCgAwIBAgIGAX55C ... Iuo9j3wpemDSgGapXQ=="
],
"x5t#S256": "v-v2V8soFmXuhCAhta8wycA9lXKnrJ4ho-N3P8aASFc",
"e": "AQAB",
"n": "gIxwqCNkdAb1ioyNBY2boqUCrMj_NS ... FiAYF7p_k3XMXOh-hsL_D8FDQ"
}
]
}
}' "https://{yourHubOrgDomain}/oauth2/v1/clients/{yourServiceAppId}"
Note: Specify all the required parameters when you update a client app. Partial updates aren't supported. If any mandatory parameters are missing when you update a client app, the update fails. When you update the keys in the service app
jwks
parameter, all the old keys are overwritten. To add a key and keep the old key, you need to specify both old and new keys in thejwks.keys
array.
Update the current credentials for the Org2Org app
From the response of the previous generate key POST request, copy the kid
property and activate the new key by updating the Org2Org app (opens new window).
Request example
curl -X PUT \
-H 'Accept: application/json' \
-H "Authorization: SSWS {spokeApiToken}" \
-H 'Content-Type: application/json' \
-d ' {
"name": "okta_org2org",
"label": "'{spokeOrg2OrgClientLabel}'",
"credentials": {
"signing": {
"kid": "sf-jWwRKMUU5588aokhj-xu_mGucHLxIh_-fYLAofB8"
}
}
}' "https://{yourSpokeOrgDomain}/api/v1/apps/{yourOrg2OrgAppId}"
Note: Specify
name
andlabel
parameters when you update an Org2Org app.