Git Product home page Git Product logo

okta-sdk-java's Introduction

Maven Central License Support API Reference

Okta Java Management SDK

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:

We also publish these libraries for Java:

You can learn more on the Okta + Java page in our documentation.

Release status

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 ⚠️ Retired
9.x.x-beta, 10.x.x-beta ⚠️ Beta release (discontinued)
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.

Need help?

If you run into problems using the SDK, you can:

Getting started

Prerequisites

  • 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.

SNAPSHOT Dependencies

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:

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.

OAuth 2.0

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();

Usage guide

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

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:

Authenticate a User

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.

Get a User

UserApi userApi = new UserApi(client);
User user = userApi.getUser("userId");

List all Users

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.

Filter or search for Users

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);

Create a User

UserApi userApi = new UserApi(client);
User user = UserBuilder.instance()
    .setEmail("[email protected]")
    .setFirstName("Joe")
    .setLastName("Code")
    .buildAndCreate(userApi);

Create a User with Group(s)

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);

Update a User

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);

Get and set custom attributes

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);

Remove a User

UserApi userApi = new UserApi(client);

// deactivate first
userApi.deactivateUser(user.getId(), false);

// then delete
userApi.deleteUser(user.getId(), false);

List Groups

GroupApi groupApi = new GroupApi(client);
List<Group> groups = groupApi.listGroups(null, null, null, 10, null, null, null, null);

Create a Group

GroupApi groupApi = new GroupApi(client);
Group group = GroupBuilder.instance()
    .setName("a-group-name")
    .setDescription("Example Group")
    .buildAndCreate(groupApi);

Add a User to a Group

// 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());

List a User's enrolled Factors

UserFactorApi userFactorApi = new UserFactorApi(client);
List<UserFactor> userFactors = userFactorApi.listFactors("userId");

Get a User Factor

UserFactorApi userFactorApi = new UserFactorApi(client);
UserFactor userFactor = userFactorApi.getFactor("userId", "factorId");

Enroll a User in a new Factor

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);

Activate a Factor

UserFactorApi userFactorApi = new UserFactorApi(client);
CallUserFactor userFactor = (CallUserFactor) userFactorApi.getFactor("userId", "factorId");
ActivateFactorRequest activateFactorRequest = new ActivateFactorRequest();
activateFactorRequest.setPassCode("123456");
userFactorApi.activateFactor("userId", "factorId", activateFactorRequest);

Verify a Factor

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);

Create a SWA Application

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);

Get an Application

ApplicationApi applicationApi = new ApplicationApi(client);
BookmarkApplication bookmarkApp = (BookmarkApplication) applicationApi.getApplication("bookmark-app-id", null);

List all Applications

ApplicationApi applicationApi = new ApplicationApi(client);
List<Application> applications = applicationApi.listApplications(null, null, null, null, null, true);

Get a Policy

PolicyApi policyApi = new PolicyApi(client);
MultifactorEnrollmentPolicy mfaPolicy =
    (MultifactorEnrollmentPolicy) policyApi.getPolicy("mfa-policy-id", null);

List Policies

PolicyApi policyApi = new PolicyApi(client);
List<Policy> policies = policyApi.listPolicies(PolicyType.PASSWORD.name(), LifecycleStatus.ACTIVE.name(), null);

List System Logs

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);

Call other API endpoints

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

Collections can be fetched with manually controlled Pagination.

UserApi userApi = new UserApi(client);
List<User> users = new ArrayList<>();
String after = null;
do {
    users.addAll(userApi.listUsers(null, after, 200, null, null, null, null));
    after = PaginationUtil.getAfter(userApi.getApiClient());
} while (StringUtils.isNotBlank(after));

Thread Safety

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.

Inject the Okta Java SDK in Spring

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!

Configuration reference

This library looks for configuration in the following sources:

  1. An okta.yaml at the root of the applications classpath
  2. An okta.yaml file in a .okta folder in the current user's home directory (~/.okta/okta.yaml or %userprofile%\.okta\okta.yaml)
  3. Environment variables
  4. Java System Properties
  5. 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.

YAML configuration

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

Environment variables

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

System properties

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

Connection Retry

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

Caching

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.

Caching for applications deployed on a single JVM

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();

Disable Caching

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();

Building the SDK

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).

Contributing

We are happy to accept contributions and PRs! Please see the contribution guide to understand how to structure a contribution.

okta-sdk-java's People

Contributors

alainodea avatar andyxtran avatar arvindkrishnakumar-okta avatar ashishgupta-okta avatar askouras avatar bdemers avatar cotnic avatar dependabot[bot] avatar dogeared avatar gabrielsroka avatar guyzehavi-okta avatar hrishikeshsathe-okta avatar ivanezeigbo-okta avatar jdeva avatar jmaldonado-okta avatar jmelberg-okta avatar joelfranusic-okta avatar karthikaselvaraj-okta avatar lboyette-okta avatar mfulgo avatar mpfz0r avatar nbarbettini avatar orsenthil avatar rlepage-okta avatar senthilkumaran-okta avatar sergiishamrai-okta avatar snyk-bot avatar tthompson-okta avatar vijetmahabaleshwar-okta avatar vitaliitytarenko-okta avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

okta-sdk-java's Issues

User can be retrieved using reset password token

I am migrating over from Stormpath and I was looking into the forgot password API. I think it would be helpful to add an API endpoint to allow an admin to get a user using a password reset token.

Currently (with Stormpath's Password Reset Flow), I can generate a token and send a custom email with a password reset link that contains the token. The user can enter a new password on a custom form and then I can make another API request using just the token and the new password to reset the user's password.

Okta, however, requires that users have a recovery question and answer set in order to reset the password in their Forgot Password Flow. Apparently, the Stormpath migration generates a random question and random answer as users are being imported. I can do the same as well for my new users, but that would require storing the random answer in my database which I do not want to do.

A quick workaround would be to add an API endpoint that would allow admins to get the corresponding user for a reset password token. Then, I can use the update user API to set the user's password.

Does this proposed API endpoint make sense? Or does anyone have a better suggestion (hack) to reset a user's password without requiring a recovery question and answer?

Update profile always clears out custom profile properties

To reproduce:

  1. Create a user and add some custom profile attribute, in my case the attribute is a string named "uuid"
  2. POST to /api/v1/users/:id following the example given here https://developer.okta.com/docs/api/resources/users.html#update-user
  3. See that the custom profile attribute has been cleared out/set to null

I created an issue in the stormpath-sdk-java repository thinking that the implementation of that SDK had a bug, but after circumventing that SDK entirely to make Okta API calls, I am still getting this behavior.

Our user profiles include a uuid that is important for our current authorization system. Currently all attempts to reset a password clears out all custom properties from our user profiles. POST vs PUT does not make a difference, the uuid is always cleared out of the profile.

This is how I'm performing the update: okta_user_update.txt

Retry operation if response is invalid.

We often times see stuff like the snippet below in our logs. Would it be possible if the Okta servers don't respond with JSON that the operation be retried? I believe this is occurring when something really bad happens on the server side and it returns an html error message.

``
com.fasterxml.jackson.core.JsonParseException: Unexpected character ('<' (code 60)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source: org.apache.http.conn.EofSensorInputStream@22504d29; line: 1, column: 2]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1702)
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:558)
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(ParserMinimalBase.java:456)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._handleUnexpectedValue(UTF8StreamJsonParser.java:2689)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._nextTokenNotInObject(UTF8StreamJsonParser.java:878)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.nextToken(UTF8StreamJsonParser.java:772)
at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:3834)
at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3756)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2123)
at com.fasterxml.jackson.core.JsonParser.readValueAs(JsonParser.java:1650)
at com.okta.sdk.framework.JsonApiClient.unmarshall(JsonApiClient.java:69)
at com.okta.sdk.framework.ApiClient.extractError(ApiClient.java:322)
at com.okta.sdk.framework.ApiClient.checkResponse(ApiClient.java:313)
at com.okta.sdk.framework.ApiClient.unmarshallResponse(ApiClient.java:286)
at com.okta.sdk.framework.ApiClient.get(ApiClient.java:156)
at com.okta.sdk.clients.UserApiClient.getUser(UserApiClient.java:146)

[new-deal] Add IT for cache verification

This is probably a combo of an IT and some UTs from any related changes needed.

This caching logic was lifted from stormpath/stormpath-sdk-java and depends on object HREF properties which Okta objects do NOT have. Also, cacheable objects via the REST API all have caching headers set, so we need UTs to validate those work correctly (and set the appropriate TTL)

static AuthResult Status

Hello,

It will be easier to check for Auth status result if the either the status is enum or static
In other words, convert AuthResult.Status to enum or have the fields of AuthResult.Status be final static

So we can write 'AuthResult.Status.SUCCESS' instead of 'new AuthResult.Status().SUCCESS'

Question on Pagination.

@bdemers In the new version of the sdk the pagination is mentioned to be automatic.Please let me know if pagination can still be used like the previous okta sdk.

The previous version of okta sdk allowed client executing the sdk a greater flexibility to restart a batch execution from the failure point in case of network failures.

Cannot get resetPasswordUrl from user.forgotPassword

The ForgotPasswordResponse object returned by forgotPassword() and forgotPassword(sendEmail, userCredentials) method of the user class never has the resetPasswordUrl property initialized.

This appears to be because the backend api only return that field if sendEmail is false, but no user credentials are included in the request (as described here). This is a call that is presently impossible to make from this sdk. @bdemers Would it be possible to get a forgotPassword(sendEmail) method, or be able to include null credentials?

Missing Cancel Transaction Call (Auth Api)

The Auth Api is missing the Cancel Transaction Call:
i.e.

public AuthResult cancelTransaction(String stateToken, String relayState) throws IOException {
    Map<String, Object> params = new HashMap<String, Object>();
    params.put(STATE_TOKEN, stateToken);
    params.put(RELAY_STATE, relayState);
    return post(getEncodedPath("/cancel"), params, new TypeReference<AuthResult>() { });
}

forgetPassword method does not send recovery emails

The forgetPassword method in AuthApiClient does not send recovery emails, like it should.
Because of this, we cannot get the recovery token necessary for the user to perform the rest of the password reset.

The root cause is that the method is not setting the factorType, like the API call does:
"factorType": "EMAIL"

Allow customer auth header in Okta Java SDK

Okta SDK should make the Authorization header more flexible if user provide then allow them user their or use the default(what Okta needs) so user don’t provide our own. The reason behind that is some users environment has additional API gateway they need to authentication against, in this case, the auth header looks like below:

Accept: application/json
Content-Type: application/json
OKTAAuthorization: SSWS {{apikey}}
Authorization: Basic {{SuperPowerBasicAuthKey}}

I have reported this enhancement to our engineer team internal

Update UserIT spec tests

There are a couple of TODOs in the spec tests after the latest updates to the source 'har' files that need to be resolved.

Maven pom issue when building okta-sdk-impl 0.7.0

Getting an ArrayIndexOutOfBoundsException when building okta-sdk-impl-0.7.0.pom

Downloading: https://repo.maven.apache.org/maven2/com/okta/sdk/okta-sdk-impl/0.7.0/okta-sdk-impl-0.7.0.pom
Downloaded: https://repo.maven.apache.org/maven2/com/okta/sdk/okta-sdk-impl/0.7.0/okta-sdk-impl-0.7.0.pom (5.4 kB at 451 kB/s)
[DEBUG] Writing tracking file /Users/jonathanp/.m2/repository/com/okta/sdk/okta-sdk-impl/0.7.0/_remote.repositories
[DEBUG] Writing tracking file /Users/jonathanp/.m2/repository/com/okta/sdk/okta-sdk-impl/0.7.0/okta-sdk-impl-0.7.0.pom.lastUpdated
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.670 s
[INFO] Finished at: 2017-10-26T17:18:36-07:00
[INFO] Final Memory: 16M/302M
[INFO] ------------------------------------------------------------------------
[ERROR] 17969
java.lang.ArrayIndexOutOfBoundsException: 17969
at org.codehaus.plexus.util.xml.pull.MXParser.parsePI(MXParser.java:2502)
at org.codehaus.plexus.util.xml.pull.MXParser.parseEpilog(MXParser.java:1604)
at org.codehaus.plexus.util.xml.pull.MXParser.nextImpl(MXParser.java:1434)
at org.codehaus.plexus.util.xml.pull.MXParser.next(MXParser.java:1131)
at org.apache.maven.model.io.xpp3.MavenXpp3Reader.read(MavenXpp3Reader.java:3856)
at org.apache.maven.model.io.xpp3.MavenXpp3Reader.read(MavenXpp3Reader.java:595)
at org.apache.maven.model.io.DefaultModelReader.read(DefaultModelReader.java:109)
at org.apache.maven.model.io.DefaultModelReader.read(DefaultModelReader.java:82)
at org.apache.maven.model.building.DefaultModelProcessor.read(DefaultModelProcessor.java:81)
at org.apache.maven.model.building.DefaultModelBuilder.readModel(DefaultModelBuilder.java:535)
at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:275)
at org.apache.maven.repository.internal.DefaultArtifactDescriptorReader.loadPom(DefaultArtifactDescriptorReader.java:321)
at org.apache.maven.repository.internal.DefaultArtifactDescriptorReader.readArtifactDescriptor(DefaultArtifactDescriptorReader.java:199)
at org.eclipse.aether.internal.impl.DefaultDependencyCollector.resolveCachedArtifactDescriptor(DefaultDependencyCollector.java:544)
at org.eclipse.aether.internal.impl.DefaultDependencyCollector.getArtifactDescriptorResult(DefaultDependencyCollector.java:528)
at org.eclipse.aether.internal.impl.DefaultDependencyCollector.processDependency(DefaultDependencyCollector.java:418)
at org.eclipse.aether.internal.impl.DefaultDependencyCollector.processDependency(DefaultDependencyCollector.java:372)
at org.eclipse.aether.internal.impl.DefaultDependencyCollector.process(DefaultDependencyCollector.java:360)
at org.eclipse.aether.internal.impl.DefaultDependencyCollector.doRecurse(DefaultDependencyCollector.java:513)
at org.eclipse.aether.internal.impl.DefaultDependencyCollector.processDependency(DefaultDependencyCollector.java:467)
at org.eclipse.aether.internal.impl.DefaultDependencyCollector.processDependency(DefaultDependencyCollector.java:372)
at org.eclipse.aether.internal.impl.DefaultDependencyCollector.process(DefaultDependencyCollector.java:360)
at org.eclipse.aether.internal.impl.DefaultDependencyCollector.collectDependencies(DefaultDependencyCollector.java:263)
at org.eclipse.aether.internal.impl.DefaultRepositorySystem.collectDependencies(DefaultRepositorySystem.java:325)
at org.apache.maven.project.DefaultProjectDependenciesResolver.resolve(DefaultProjectDependenciesResolver.java:169)
at org.apache.maven.lifecycle.internal.LifecycleDependencyResolver.getDependencies(LifecycleDependencyResolver.java:195)
at org.apache.maven.lifecycle.internal.LifecycleDependencyResolver.resolveProjectDependencies(LifecycleDependencyResolver.java:127)
at org.apache.maven.lifecycle.internal.MojoExecutor.ensureDependenciesAreResolved(MojoExecutor.java:246)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:200)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:154)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:146)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:309)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:194)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:107)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:993)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:345)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:191)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)

Assign app to user

Do you have any example showing how to assign applications to a user?

Version number in POM never updated?

I noticed that releases are provided through GitHub but they all have the same 0.0.4-SNAPSHOT version number in the pom.xml.

Would it be possible to set a release number in the pom and update it whenever there is an official release?

Thanks,

Sam

Documentation how to deal with `ResourceException`

Many or all of the methods on Client throw exceptions under various circumstances, but this fact is not documented at all. There needs to be documentation for all exceptions thrown so that consumers of this API can know what to handle under which circumstances and know what to do to have a stable application.

getUser
@Generated(value="com.okta.swagger.codegen.OktaJavaClientApiCodegen",
           date="2017-09-05T20:10:20.883-04:00",
           comments="GET - /api/v1/users/{userId}")
User getUser(String userId)
Parameters:
userId - (required)
Returns:
User

ResourceException Property Status Should Have Javadoc

The status property of ResourceException does not have any documentation saying what it is. I assume this is the HTTP status code, but I prefer not to assume anything about undocumented APIs.

Meanwhile, until this documentation is added, can I get confirmation that this is indeed the HTTP status code?

Support for Identity Providers API

We use the Identity Providers API in production to:

  1. Allow customers to create and update their IdPs with necessary security restrictions from our application for SSO self-enrolment.
  2. Allow customers to see logs specific to their IdP for SSO self-troubleshooting (used to get target ID for use with Events API)

Simplify ITs

  • Remove on remote har file repo
  • Remove ITs usage of collection size (not stable, instead of initial size +1 just call a get on the expected resource)
  • Group Rule ITs should not query the group membership, this is an async task, and is already covered by tests on for the backend

HttpResponse not closed on Exception.

In the following code, JsonApiClient.java:

  @Override
    protected  <T> T unmarshall(HttpResponse response, TypeReference<T> clazz) throws IOException {
        if (response.getEntity() == null || clazz.getType().equals(Void.class)) {
            EntityUtils.consume(response.getEntity());
            return null;
        }
        InputStream inputStream = response.getEntity().getContent();
        JsonParser parser = objectMapper.getFactory().createParser(inputStream);
        T toReturn = parser.readValueAs(clazz);
        EntityUtils.consume(response.getEntity());
        return toReturn;
    }

The response is not properly consumed if "parser.readValueAs(clazz)" throws an exception.

The EntityUtils.consume call should be placed in a finally block.

Default dependencies cause SLF4J warnings

When using the basic SDK config:

<dependency>
	<groupId>com.okta.sdk</groupId>
	<artifactId>okta-sdk-api</artifactId>
	<version>0.10.0</version>
</dependency>
<dependency>
	<groupId>com.okta.sdk</groupId>
	<artifactId>okta-sdk-impl</artifactId>
	<version>0.10.0</version>
</dependency>
<dependency>
	<groupId>com.okta.sdk</groupId>
	<artifactId>okta-sdk-httpclient</artifactId>
	<version>0.10.0</version>
	<scope>runtime</scope>
</dependency>

you get a SLF4J warning:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

A simple workaround (as suggested by the documentation) is to add the slf4j-nop.jar to your classpath. Once I added

<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-nop</artifactId>
	<version>1.7.25</version>
</dependency>

to my pom.xml, I didn't see the warning message anymore.

My suggestion: Update the logging configuration, or add the above dependency to the project so this warning does not show by default.

Feature Request: Support for User Authentication

Is there a plan to add User Authentication support (via: username and password, and multi-factor validation) in the client?
https://developer.okta.com/docs/api/resources/authn.html#authentication-operations

The Javascript SDK supports this already:
https://developer.okta.com/code/javascript/okta_auth_sdk

It seems like it would be as simple as adding another method or two (e.g. authenticateUser, validateFactor) to the Client class.
https://developer.okta.com/okta-sdk-java/apidocs/com/okta/sdk/client/Client.html

Factor object instantiation

Hello,

We were having some issues creating SMS factors because there doesn't seem to be a builder or direct way to instantiate the SMSFactor object. We found the Client.instantiate method and that seems to work, but this is not mentioned anywhere in documentation or examples, so just wanted to confirm that this is the correct way to do this for SMSFactor and I am guessing other objects as well.

Thank you

AuthResult.Status fields are inaccessible

The various status strings in AuthResult.Status cannot be referenced as they are non-static and cannot be referenced from a static context (e.g. directly as AuthResult.Status.SUCCESS). In order to be used, a new instance of AuthResult.Status would have to be instantiated.

Adding user to application.

Is it possible to add an existing user to an existing application? I can get the user and application from the SDK, but I don't see a way to link them.

Provide Link to JavaDocs in README

The README gives example code snippets, but there is no link to an online copy of the JavaDocs.

This site: https://developer.okta.com/code/java/

Has a link to JavaDocs but it is the old "okta-sdk" project that does not exist any more. The link directly below it "Get it on Github" links to the new current project, which a different set of classes from "okta-sdk".

Password should not be stored in `String`

As discussed many places (here's a great StackOverflow question about it: http://stackoverflow.com/questions/8881291/why-is-char-preferred-over-string-for-passwords/8889285#8889285), password fields in Java should not be stored in String, as they are immutable.

The Password object should also have a clear method to destroy the string or the object after use, or should be built into the SDK to be destroyed as soon as possible.

I may also suggest to call the object something like PlaintextPassword, so developers feel sufficiently paranoid about using it. 😃

"No route to host" when performing operation createUser behind proxy

Hello,

When i try to connect to OKTA, i get this error :

[main] INFO com.okta.sdk.framework.ApiClient - POST https://subdomain.okta.com/api/v1/users?activate=true
May 3, 2016 4:40:04 PM org.apache.http.impl.execchain.RetryExec execute
INFO: I/O exception (java.net.NoRouteToHostException) caught when processing request to {s}->https://subdomain.okta.com:443: No route to host
May 3, 2016 4:40:04 PM org.apache.http.impl.execchain.RetryExec execute
INFO: Retrying request to {s}->https://subdomain.okta.com:443

indeed, i try to communicate whith OKTA behind a proxy server

i used JVM properties to set my proxy adresses

            System.setProperty("http.proxyHost", "ipadress");
            System.setProperty("http.proxyPort", "8080");
            System.setProperty("https.proxyHost", "ipadress");
            System.setProperty("https.proxyPort", "8080");

Apparently these lines of code are not working !

is there any way to set proxy properties using the java API ?

NB : Downloading the file from the same subdomain https://subdomain.okta.com using Java is working with this code :

    public static void download(String address, String localFileName) {
        OutputStream out = null;
        URLConnection conn = null;
        InputStream in = null;

    System.setProperty("http.proxyHost", "ipadress");
    System.setProperty("http.proxyPort", "8080");
    System.setProperty("https.proxyHost", "ipadress");
    System.setProperty("https.proxyPort", "8080");

        try {
            URL url = new URL(address);
            out = new BufferedOutputStream(new FileOutputStream(localFileName));
            conn = url.openConnection();
            in = conn.getInputStream();
            byte[] buffer = new byte[1024];

            int numRead;
            long numWritten = 0;

            while ((numRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, numRead);
                numWritten += numRead;
            }

            System.out.println(localFileName + "\t" + numWritten);
        } 
        catch (Exception exception) { 
            exception.printStackTrace();
        } 
        finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            } 
            catch (IOException ioe) {
            }
        }
    }

You will find below the code returning "No route to host" error:

package okta;

import java.io.IOException;

import com.okta.sdk.clients.UserApiClient;
import com.okta.sdk.framework.ApiClientConfiguration;
import com.okta.sdk.models.users.User;

public class Main {

        public static void main(String[] args) {
        System.out.println( "Hello World!" );

        System.out.println( "Setting proxy..." );
        System.setProperty("http.proxyHost", "ipadress");
        System.setProperty("http.proxyPort", "8080");
        System.setProperty("https.proxyHost", "ipadress");
        System.setProperty("https.proxyPort", "8080");

        System.out.println( "Setting config Okta..." );
        ApiClientConfiguration oktaSettings = new ApiClientConfiguration(
                "https://subdomain.okta.com",
                "test");

        UserApiClient userApiClient = new UserApiClient(oktaSettings);

     try {
            System.out.println( "Calling Okta..." );
            User newUser = userApiClient.createUser(
                             "First",
                             "Last",
                             "[email protected]",
                             "[email protected]",
                             true);
            System.out.println( "End calling" );
        } catch (IOException e) {
            System.out.println( "KO" );
            e.printStackTrace();
        }

        System.out.println( "End" );
    }

}

Thank you in advance.

Intermittently thrown NoHttpResponseException

Calling the authenticate API (AuthApiClient.authenticate(String,String,String)), occasionally org.apache.http.NoHttpResponseException is thrown.

It seems one possible cause is that the persistent HTTP connections are timing out on the server side but Apache HttpClient is attempting to reuse them. Although there may be several ways to fix that, if it is in fact the underlying cause, we have not been able to consistently reproduce the issue, so we cannot evaluate the effectiveness of any attempted fix.

Does anyone know a consistent way to reproduce this issue?

As a temporary workaround, we retry the call up to 3 times when a NoHttpResponseException occurs and this seems to have suppressed the problem well enough for now that it does not affect our users at least.

Looks like someone may be trying to fix it here: #23
I am interested in understanding the root cause and a way to reproduce it so we can see if any proposed fixes are working.

Add better argument checking for null/empty strings when constructing URLs

Seeing the following when calling client.getUser("string value").

It doesn't appear that this is something I can control.

I'm assuming the client is thread safe, is it not maybe?

java.lang.IllegalArgumentException: It is expected that only ArrayProperty or SetProperty properties represent collection items.
at com.okta.sdk.lang.Assert.isTrue(Assert.java:33)
at com.okta.sdk.impl.ds.cache.WriteCacheFilter.cache(WriteCacheFilter.java:154)
at com.okta.sdk.impl.ds.cache.WriteCacheFilter.filter(WriteCacheFilter.java:73)
at com.okta.sdk.impl.ds.DefaultFilterChain.filter(DefaultFilterChain.java:52)
at com.okta.sdk.impl.ds.cache.ReadCacheFilter.filter(ReadCacheFilter.java:53)
at com.okta.sdk.impl.ds.DefaultFilterChain.filter(DefaultFilterChain.java:52)
at com.okta.sdk.impl.ds.DefaultDataStore.getResourceData(DefaultDataStore.java:309)
at com.okta.sdk.impl.ds.DefaultDataStore.getResource(DefaultDataStore.java:225)
at com.okta.sdk.impl.ds.DefaultDataStore.getResource(DefaultDataStore.java:213)
at com.okta.sdk.impl.client.DefaultClient.getUser(DefaultClient.java:236)

Project is set to use Java 1.5

Changing okta-aws-cli.iml to have LANGUAGE_LEVEL="JDK_1_8" and compiler.xml as follows will allow it to build and run against aws-java-sdk-1.11.128.jar. I've not tested it thoroughly, yet.

    <bytecodeTargetLevel>
      <module name="okta-aws-cli" source="1.8" />
      <module name="okta-aws-cli" target="1.8" />
    </bytecodeTargetLevel>

Artifact can't be generated

Building with maven throw an error, this dependency can't be resolved:

        <dependency>
            <groupId>com.okta.libraries.framework</groupId>
            <artifactId>aperture-reporter</artifactId>
            <version>${aperture-reporter.version}</version>
        </dependency>

Setting Attribute Values?

Hi,

I need to create some users and I need to populate profile attributes other than the standard (firstname,lastname,email,login). Is it possible to do this withe API?

For example I need to populate the employeeNumber attribute.

thanks,
Freeman

Getting a new UserBuilder instance?

The README shows the following code snippet for creating a new user:

return UserBuilder.INSTANCE
.setEmail(email)
.setLogin(userName)
......

However, the UserBuilder.INSTANCE is simply a single global instance:
UserBuilder INSTANCE = Classes.newInstance("com.okta.sdk.impl.resource.DefaultUserBuilder");

This can lead to all sorts of unexpected behavior. For example, if multiple threads want to create users in parallel, they would overwrite one another if they all use UserBuilder.INSTANCE at the same time.

Another problem: suppose we first use UserBuilder.INSTANCE to create an account with name=Joe. Then, we want to create a new account for a different user, but this time, the name is not specified. If we simply call UserBuilder.INSTANCE and omit calling setName, the previous value of Joe will still stick around for the second account.

It would be much preferable if I could construct a new instance of UserBuilder for each time I want to create a user, but I don't see any way to create a new instance currently.

[new-deal] ClientCredential encrypted at rest

Some of the lifted code from stormpath/stormpath-sdk-java encrypted the client secret, and only decrypted when it was accessed during a request. We need to evaluate this to see if that is still a valid strategy.

I think it would be, but I think it entails additional work as the client token is potentially available from multiple locations (ENV vars, Sys props, ClientConfiguration instance, etc). Encrypting the secret in one place doesn't seem like enough. We could null out value when the secrets are passed into the ClientCredentialResolver. (even for ENV vars, but that would require some dirty reflection)

README.md wrong

When adding the Advanced Search methods to the UserApiClient, the methods documented are wrong
it mentions client.getUsersWithSearch, when what was written is client.getUsersWithAdvancedSearch

I also feel this feature is significant enough that the pom.xml should be reved to reflect the difference on compile.

GetFactorType() Not Working

I am iterating through a list of Factors for a user and attempting to use .getFactorType() but am always getting the following error:

18895a8a-48bd-45f7-b983-2fe53dac88b1

Here are more screenshots to show this Factor should not be throwing the error.

4b613339-14ad-402b-8e1a-c38f627e644f
849aa97f-c68c-4d75-9254-0aea2abc599a
095d083f-49b8-49c1-8609-7413a90b8a6b

There seems to be an error in the SDK :)
Thank you so much!

Nathan

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.