- Release status
- Need help?
- Getting started
- Usage guide
- Spring Support
- Configuration reference
- Building the SDK
- Contributing
This repository contains the Okta management SDK for Java. This SDK can be used in your server-side code to interact with the Okta management API and:
- Create and update users with the Users API
- Add security factors to users with the Factors API
- Manage groups with the Groups API
- Manage applications with the Apps API
- Manage logs with the Logs API
- Manage sessions with the Sessions API
- Manage templates with the Custom Templates API
- Manage identity providers with the Identity Providers API
- Manage authorization servers with the Authorization Servers API
- Manage event hooks with the Event Hooks Management API
- Manage inline hooks with the Inline Hooks Management API.
- Manage features with the Features API.
- Manage linked objects with the Linked Objects API.
- Manage trusted origins with the Trusted Origins API.
- Manage user types with the User Types API.
- Manage custom domains with the Domains API.
- Manage network zones with the Zones API.
- Much more!
We also publish these libraries for Java:
You can learn more on the Okta + Java page in our documentation.
This library uses semantic versioning and follows Okta's library version policy.
✔️ The latest stable major version series is: 16.x.x
Version | Status |
---|---|
0.0.x, 1.x, 2.x.x, 3.x.x, 4.x.x, 5.x.x, 6.x.x, 7.x.x, 8.x.x | |
9.x.x-beta, 10.x.x-beta | |
10.x.x | ✔️ Stable (migration guide) |
11.x.x | ✔️ Stable (see changes) |
12.x.x | ✔️ Stable (see changes) |
13.x.x | ✔️ Stable (see changes) |
14.x.x | ✔️ Stable (see changes) |
15.x.x | ✔️ Stable (see changes) |
16.x.x | ✔️ Stable (see changes) |
The latest release can always be found on the releases page.
If you run into problems using the SDK, you can:
- Ask questions on the Okta Developer Forums
- Post issues here on GitHub (for code errors)
- Java 11 or later
To use this SDK, you will need to include the following dependencies:
For Apache Maven:
<dependency>
<groupId>com.okta.sdk</groupId>
<artifactId>okta-sdk-api</artifactId>
<version>${okta.sdk.version}</version>
</dependency>
<dependency>
<groupId>com.okta.sdk</groupId>
<artifactId>okta-sdk-impl</artifactId>
<version>${okta.sdk.version}</version>
<scope>runtime</scope>
</dependency>
For Gradle:
compile "com.okta.sdk:okta-sdk-api:${okta.sdk.version}"
runtime "com.okta.sdk:okta-sdk-impl:${okta.sdk.version}"
where ${okta.sdk.version} is the latest published version in Maven Central.
Snapshots are deployed off of the 'master' branch to OSSRH and can be consumed using the following repository configured for Apache Maven or Gradle:
https://oss.sonatype.org/content/repositories/snapshots/
You will also need:
- An Okta account, called an organization (sign up for a free developer organization if you need one)
- An API token
Construct a client instance by passing it your Okta domain name and API token:
ApiClient client = Clients.builder()
.setOrgUrl("https://{yourOktaDomain}") // e.g. https://dev-123456.okta.com
.setClientCredentials(new TokenClientCredentials("{apiToken}"))
.build();
Hard-coding the Okta domain and API token works for quick tests, but for real projects you should use a more secure way of storing these values (such as environment variables). This library supports a few different configuration sources, covered in the configuration reference section.
Okta allows you to interact with Okta APIs using scoped OAuth 2.0 access tokens. Each access token enables the bearer to perform specific actions on specific Okta endpoints, with that ability controlled by which scopes the access token contains.
This SDK supports this feature only for service-to-service applications. Check out our guides to learn more about how to register a new service application using a private and public key pair.
Check out our guide to learn how to generate a JWK and convert the same to PEM format which would be used as PrivateKey in Client
creation.
When using this approach, you will not need an API Token because the SDK will request an access token for you. In order to use OAuth 2.0, construct a client instance by passing the following parameters:
ApiClient client = Clients.builder()
.setOrgUrl("https://{yourOktaDomain}") // e.g. https://dev-123456.okta.com
.setAuthorizationMode(AuthorizationMode.PRIVATE_KEY)
.setClientId("{clientId}")
.setKid("{kid}") // optional
.setScopes(new HashSet<>(Arrays.asList("okta.users.manage", "okta.apps.manage", "okta.groups.manage")))
.setPrivateKey("/path/to/yourPrivateKey.pem")
// (or) .setPrivateKey("full PEM payload")
// (or) .setPrivateKey(Paths.get("/path/to/yourPrivateKey.pem"))
// (or) .setPrivateKey(inputStream)
// (or) .setPrivateKey(privateKey)
// (or) .setOAuth2AccessToken("access token string") // if set, private key (if supplied) will be ignored
.build();
These examples will help you understand how to use this library. You can also browse the full API reference documentation.
Once you initialize a ApiClient
instance, you can pass this instance to the constructor of any API area clients (such as UserApi
, GroupApi
, ApplicationApi
etc.).
You can start using these clients to call management APIs relevant to the chosen API area.
Note: For creation (HTTP POST or PUT operation) of models that follow inheritance (e.g. Application, Policy | PolicyRule, UserFactor), use the APIs found in their respective ApiHelper
class (e.g. ApplicationApiHelper
, PolicyApiHelper
, UserFactorApiHelper
)
to ensure safe type cast to their respective subclass types.
Non-admin users will require to be granted specific permissions to perform certain tasks and access resources.
Check out the following resources to learn more:
This library should be used with the Okta management API. For authentication, we recommend using an OAuth 2.0 or OpenID Connect library such as Spring Security OAuth or Okta's Spring Boot integration. For Okta Authentication API you can use Authentication SDK.
UserApi userApi = new UserApi(client);
User user = userApi.getUser("userId");
UserApi userApi = new UserApi(client);
List<User> users = userApi.listUsers(null, null, 5, null, null, null, null);
// stream
users.stream()
.forEach(user -> {
// do something
});
For more examples of handling collections see the pagination section below.
UserApi userApi = new UserApi(client);
// search by email
List<User> users = userApi.listUsers(null, null, 5, null, "profile.email eq \"[email protected]\"", null, null);
// filter parameter
userApi.listUsers(null, null, null, "status eq \"ACTIVE\"",null, null, null);
UserApi userApi = new UserApi(client);
User user = UserBuilder.instance()
.setEmail("[email protected]")
.setFirstName("Joe")
.setLastName("Code")
.buildAndCreate(userApi);
UserApi userApi = new UserApi(client);
User user = UserBuilder.instance()
.setEmail("[email protected]")
.setFirstName("Joe")
.setLastName("Code")
.setGroups(Arrays.asList("groupId-1", "groupId-2"))
.buildAndCreate(userApi);
UserApi userApi = new UserApi(client);
UpdateUserRequest updateUserRequest = new UpdateUserRequest();
UserProfile userProfile = new UserProfile();
userProfile.setNickName("Batman");
updateUserRequest.setProfile(userProfile);
userApi.updateUser(user.getId(), updateUserRequest, true);
Custom user profile attributes can be added with code like below, but the respective property must first be associated to the User schema. You can use the Profile Editor in your Org Administrator UI (Developer Console) or the Schemas API to manage schema extensions.
Once you have created the custom attributes via UI, you can use the code below to get and set values:
UserApi userApi = new UserApi(client);
UpdateUserRequest updateUserRequest = new UpdateUserRequest();
UserProfile userProfile = new UserProfile();
userProfile.getAdditionalProperties().put("foo", "bar");
updateUserRequest.setProfile(userProfile);
userApi.updateUser(user.getId(), updateUserRequest, true);
UserApi userApi = new UserApi(client);
// deactivate first
userApi.deactivateUser(user.getId(), false);
// then delete
userApi.deleteUser(user.getId(), false);
GroupApi groupApi = new GroupApi(client);
List<Group> groups = groupApi.listGroups(null, null, null, 10, null, null, null, null);
GroupApi groupApi = new GroupApi(client);
Group group = GroupBuilder.instance()
.setName("a-group-name")
.setDescription("Example Group")
.buildAndCreate(groupApi);
// create user
UserApi userApi = new UserApi(client);
User user = UserBuilder.instance()
.setEmail("[email protected]")
.setFirstName("Joe")
.setLastName("Code")
.buildAndCreate(userApi);
// create group
GroupApi groupApi = new GroupApi(client);
Group group = GroupBuilder.instance()
.setName("a-group-name")
.setDescription("Example Group")
.buildAndCreate(groupApi);
// assign user to group
groupApi.assignUserToGroup(group.getId(), user.getId());
UserFactorApi userFactorApi = new UserFactorApi(client);
List<UserFactor> userFactors = userFactorApi.listFactors("userId");
UserFactorApi userFactorApi = new UserFactorApi(client);
UserFactor userFactor = userFactorApi.getFactor("userId", "factorId");
UserFactorApi userFactorApi = new UserFactorApi(client);
SmsUserFactorProfile smsUserFactorProfile = new SmsUserFactorProfile();
smsUserFactorProfile.setPhoneNumber("555 867 5309");
SmsUserFactor smsUserFactor = new SmsUserFactor();
smsUserFactor.setProvider(FactorProvider.OKTA);
smsUserFactor.setFactorType(FactorType.SMS);
smsUserFactor.setProfile(smsUserFactorProfile);
userFactorApi.enrollFactor("userId", smsUserFactor, true, "templateId", 30, true);
UserFactorApi userFactorApi = new UserFactorApi(client);
CallUserFactor userFactor = (CallUserFactor) userFactorApi.getFactor("userId", "factorId");
ActivateFactorRequest activateFactorRequest = new ActivateFactorRequest();
activateFactorRequest.setPassCode("123456");
userFactorApi.activateFactor("userId", "factorId", activateFactorRequest);
UserFactorApi userFactorApi = new UserFactorApi(client);
UserFactor userFactor = userFactorApi.getFactor( "userId", "factorId");
VerifyFactorRequest verifyFactorRequest = new VerifyFactorRequest();
verifyFactorRequest.setPassCode("123456");
VerifyUserFactorResponse verifyUserFactorResponse =
userFactorApi.verifyFactor("userId", "factorId", "templateId", 10, "xForwardedFor", "userAgent", "acceptLanguage", verifyFactorRequest);
ApplicationApi applicationApi = new ApplicationApi(client);
SwaApplicationSettingsApplication swaApplicationSettingsApplication = new SwaApplicationSettingsApplication();
swaApplicationSettingsApplication.buttonField("btn-login")
.passwordField("txtbox-password")
.usernameField("txtbox-username")
.url("https://example.com/login.html");
SwaApplicationSettings swaApplicationSettings = new SwaApplicationSettings();
swaApplicationSettings.app(swaApplicationSettingsApplication);
BrowserPluginApplication browserPluginApplication = new BrowserPluginApplication();
browserPluginApplication.name("template_swa");
browserPluginApplication.label("Sample Plugin App");
browserPluginApplication.settings(swaApplicationSettings);
// create BrowserPluginApplication app type
BrowserPluginApplication createdApp =
(BrowserPluginApplication) applicationApi.createApplication(browserPluginApplication, true, null);
ApplicationApi applicationApi = new ApplicationApi(client);
BookmarkApplication bookmarkApp = (BookmarkApplication) applicationApi.getApplication("bookmark-app-id", null);
ApplicationApi applicationApi = new ApplicationApi(client);
List<Application> applications = applicationApi.listApplications(null, null, null, null, null, true);
PolicyApi policyApi = new PolicyApi(client);
MultifactorEnrollmentPolicy mfaPolicy =
(MultifactorEnrollmentPolicy) policyApi.getPolicy("mfa-policy-id", null);
PolicyApi policyApi = new PolicyApi(client);
List<Policy> policies = policyApi.listPolicies(PolicyType.PASSWORD.name(), LifecycleStatus.ACTIVE.name(), null);
SystemLogApi systemLogApi = new SystemLogApi(client);
// use a filter (start date, end date, filter, or query, sort order) all options are nullable
List<LogEvent> logEvents =
systemLogApi.listLogEvents(null, null, null, "interestingURI.com", 100, "ASCENDING", null);
Not every API endpoint is represented by a method in this library. You can call any Okta management API endpoint using this generic syntax:
ApiClient apiClient = buildApiClient("orgBaseUrl", "apiKey");
// Create a BookmarkApplication
BookmarkApplication bookmarkApplication = new BookmarkApplication();
bookmarkApplication.setName("bookmark");
bookmarkApplication.setLabel("Sample Bookmark App");
bookmarkApplication.setSignOnMode(ApplicationSignOnMode.BOOKMARK);
BookmarkApplicationSettings bookmarkApplicationSettings = new BookmarkApplicationSettings();
BookmarkApplicationSettingsApplication bookmarkApplicationSettingsApplication =
new BookmarkApplicationSettingsApplication();
bookmarkApplicationSettingsApplication.setUrl("https://example.com/bookmark.htm");
bookmarkApplicationSettingsApplication.setRequestIntegration(false);
bookmarkApplicationSettings.setApp(bookmarkApplicationSettingsApplication);
bookmarkApplication.setSettings(bookmarkApplicationSettings);
StringJoiner localVarQueryStringJoiner = new StringJoiner("&");
List<Pair> localVarQueryParams = new ArrayList<>();
List<Pair> localVarCollectionQueryParams = new ArrayList<>();
Map<String, String> localVarHeaderParams = new HashMap<>();
Map<String, String> localVarCookieParams = new HashMap<>();
Map<String, Object> localVarFormParams = new HashMap<>();
BookmarkApplication createdApp = apiClient.invokeAPI(
"/api/v1/apps", // path
HttpMethod.POST.name(), // http method
localVarQueryParams, // query params
localVarCollectionQueryParams, // collection query params
localVarQueryStringJoiner.toString(),
bookmarkApplication, // request body
localVarHeaderParams, // header params
localVarCookieParams, // cookie params
localVarFormParams, // form params
MediaType.APPLICATION_JSON_VALUE, // accept
MediaType.APPLICATION_JSON_VALUE, // content type
new String[]{ "apiToken", "oauth2" }, // auth names
new TypeReference<BookmarkApplication>() { } // return type
);
Pagination info would be available via PagedList
when the API response is a collection of models.
UserApi userApi = new UserApi(client);
// max number of items per page
int pageSize = 10;
PagedList<User> pagedUserList = new PagedList<>();
do {
pagedUserList = (PagedList<User>)
userApi.listUsers(null, pagedUserList.getAfter(), pageSize, null, null, null, null);
pagedUserList.forEach(usr -> log.info("User: {}", usr.getProfile().getEmail()));
} while (pagedUserList.hasMoreItems());
Every instance of the SDK Client
is thread-safe. You should use the same instance throughout the entire lifecycle of your application. Each instance has its own Connection pool and Caching resources that are automatically released when the instance is garbage collected.
To integrate the Okta Java SDK into your Spring Boot application you just need to add a dependency:
<dependency>
<groupId>com.okta.spring</groupId>
<artifactId>okta-spring-sdk</artifactId>
<version>${okta.spring.version}</version>
</dependency>
Then define the following properties:
Key | Description |
---|---|
okta.client.orgUrl |
Your Okta Url: https://{yourOktaDomain} , i.e. https://dev-123456.okta.com |
okta.client.token |
An Okta API token, see creating an API token for more info. |
NOTE: The configuration techniques described in the configuration reference section will work as well.
All that is left is to inject the client (com.okta.sdk.client.Client
)! Take a look at this post for more info on the best way to inject your beans.
For more information check out the Okta Spring Boot Starter project!
This library looks for configuration in the following sources:
- An
okta.yaml
at the root of the applications classpath - An
okta.yaml
file in a.okta
folder in the current user's home directory (~/.okta/okta.yaml
or%userprofile%\.okta\okta.yaml
) - Environment variables
- Java System Properties
- Configuration explicitly set programmatically (see the example in Getting started)
Higher numbers win. In other words, configuration passed via the constructor will override configuration found in environment variables, which will override configuration in okta.yaml
(if any), and so on.
The full YAML configuration looks like:
okta:
client:
connectionTimeout: 30 # seconds
orgUrl: "https://{yourOktaDomain}" # i.e. https://dev-123456.okta.com
proxy:
port: null
host: null
username: null
password: null
token: yourApiToken
requestTimeout: 0 # seconds
rateLimit:
maxRetries: 4
Each one of the configuration values above can be turned into an environment variable name with the _
(underscore) character:
OKTA_CLIENT_CONNECTIONTIMEOUT
OKTA_CLIENT_TOKEN
- and so on
Each one of the configuration values written in 'dot' notation to be used as a Java system property:
okta.client.connectionTimeout
okta.client.token
- and so on
By default, this SDK will retry requests that return with response code 503
, 504
, 429
(caused by rate limiting), or socket/connection exceptions.
Default configuration tells SDK to retry requests up to 4 times without time limitation:
okta.client.requestTimeout = 0 //Sets the maximum number of seconds to wait when retrying before giving up.
okta.client.rateLimit.maxRetries = 4 //Sets the maximum number of attempts to retrying before giving up.
For interactive clients (i.e. web pages) it is optimal to set requestTimeout
to be 10 sec (or less, based on your needs), and the maxRetries
attempts to be 0.
This means the requests will retry as many times as possible within 10 seconds:
okta.client.requestTimeout = 10
okta.client.rateLimit.maxRetries = 0
(or)
import com.okta.sdk.client.Client;
import com.okta.sdk.client.Clients;
Client client = Clients.builder()
.setRetryMaxElapsed(10)
.setRetryMaxAttempts(0)
.build();
For batch/non-interactive processes optimal values are opposite. It is optimal to set requestTimeout
to be 0, and the maxRetries
attempts to be 5.
The SDK will retry requests up to 5 times before failing:
okta.client.requestTimeout = 0
okta.client.rateLimit.maxRetries = 5
If you need to limit execution time and retry attempts, you can set both requestTimeout
and the maxRetries
.
For example, the following example would retry up to 15 times within 30 seconds:
okta.client.requestTimeout = 30
okta.client.rateLimit.maxRetries = 15
To disable the retry functionality you need to set maxRetries
to zero:
okta.client.rateLimit.maxRetries = 0
By default, a simple production-grade in-memory CacheManager will be enabled when the Client instance is created. This CacheManager implementation has the following characteristics:
- It assumes a default time-to-live and time-to-idle of 1 hour for all cache entries.
- It auto-sizes itself based on your application's memory usage. It will not cause OutOfMemoryExceptions.
The default cache manager is not suitable for an application deployed across multiple JVMs.
This is because the default implementation is 100% in-memory (in-process) in the current JVM. If more than one JVM is deployed with the same application codebase - for example, a web application deployed on multiple identical hosts for scaling or high availability - each JVM would have it's own in-memory cache.
As a result, if your application that uses an Okta Client instance is deployed across multiple JVMs, you SHOULD ensure that the Client is configured with a CacheManager implementation that uses coherent and clustered/distributed memory.
See the ClientBuilder
Javadoc for more details on caching.
If your application is deployed on a single JVM and you still want to use the default CacheManager implementation, but the default cache configuration does not meet your needs, you can specify a different configuration. For example:
Caches.newCacheManager()
.withDefaultTimeToLive(300, TimeUnit.SECONDS) // default
.withDefaultTimeToIdle(300, TimeUnit.SECONDS) //general default
.withCache(forResource(User.class) //User-specific cache settings
.withTimeToLive(1, TimeUnit.HOURS)
.withTimeToIdle(30, TimeUnit.MINUTES))
.withCache(forResource(Group.class) //Group-specific cache settings
.withTimeToLive(1, TimeUnit.HOURS))
//... etc ...
.build();
While production applications will usually enable a working CacheManager as described above, you might wish to disable caching entirely. You can do this by configuring a disabled CacheManager instance. For example:
ApiClient client = Clients.builder()
.setCacheManager(Caches.newDisabledCacheManager())
.build();
In most cases, you won't need to build the SDK from source. If you want to build it yourself, take a look at the build instructions wiki (though just cloning the repo and running mvn install
should get you going).
We are happy to accept contributions and PRs! Please see the contribution guide to understand how to structure a contribution.