❕ The use of this module requires usage of the Okta Identity Engine. This functionality is in general availability but is being gradually rolled out to customers. If you want to request to gain access to the Okta Identity Engine, please reach out to your account manager. If you do not have an account manager, please reach out to oie@okta.com for more information.
This module is built to communicate with Okta as an OAuth 2.0 + OpenID Connect provider. It works with Okta's Identity Engine to authenticate and register users.
To see this library working in a sample, check out our Embedded auth with SDKs sample.
The IDX API is built on the [Okta Identity Engine][]. The API allows applications to implement features which were not possible with the older authn API, such as multi-factor authentication without redirection to Okta. We recommend that all new application deployments use the IDX
API. Existing applications can migrate from authn
to IDX
by following this guide.
See detail in Using the npm module.
This module provides convenience methods to support popular scenarios to communicate with Okta's Identity Engine.
In addition to the default authentication flow, this SDK supports several pre-defined flows, such as register, recoverPassword and unlockAccount. A flow can be started by calling one of the available flow entrypoints or by passing a valid flow identifier string to startTransaction
. The flow
is saved with the transaction which enables the proceed method to corrrectly handle remediations without additional context. Starting a new flow discards any existing in-progress transaction of a different type. For example, if an authentication flow is in-progress, a call to authenticate or proceed will continue using the current transaction but a call to a flow entrypoint will start a new transaction.
// a recover password flow can be started by calling the `recoverPassword` entrypoint
await authClient.idx.recoverPassword();
const flow = authClient.idx.getFlow(); // "recoverPassword"
// or by passing the flow identifier to `startTransaction`
await authClient.idx.startTransaction({ flow: 'recoverPassword' })
The flow is set automatically when calling one of these methods:
The flow
will be set to default
unless otherwise specified in idx.startTransaction
A redirect callback occurrs when your app is reloaded in the browser as part of a flow. During a redirect callback, the app is loaded at a specific URL path that you have defined in your Okta App configuration. Most callbacks can only be handled once and will produce an error if there is an attempt to handle it twice. Typically, the app will redirect itself to a well known or previously saved URL path after the callback logic has been handled to avoid errors on page reload.
Note: Most apps should be prepared to handle one or more redirect callbacks. Depending on how the App sign-on policy is configured, some SPA applications may be able to receive tokens without any redirect. However, logic will need to be added if the policy includes signing in with a Social / IDP provider)) or allows account recovery using email.
All web applications will handle an OAuth callback which includes an interaction_code
query parameter. SPA applications can receive an interactionCode
directly from an IDX Response. However, they may still need to implement OAuth callback logic if the sign-on policy includes signing in with a Social / IDP provider.
// https://myapp.mycompany.com/login/callback?interaction_code=ABC&state=XYZ
if (authClient.isLoginRedirect()) {
await authClient.handleLoginRedirect();
}
After signing in with a 3rd party IDP, the user is redirected back to the application. If no further input is needed from the user, then this will be an OAuth callback containing an interaction_code
parameter. If further input is required, then the callback will contain an error
parameter with the value interaction_required
const search = window.location.search;
if (authClient.idx.isInteractionRequired(search)) {
await authClient.idx.proceed();
}
After the user clicks the link in an email, they are redirected back to the application. The query parameters include state
and otp
const { search } = window.location;
if (authClient.idx.isEmailVerifyCallback(search)) {
try {
// Proceed, if possible. Throws an `EmailVerifyCallbackError` if proceed is not possible.
const { status, nextStep, tokens } = await authClient.idx.handleEmailVerifyCallback(search);
if (status === IdxStatus.SUCCESS) {
// user has successfully authenticated
authClient.tokenManager.setTokens(tokens);
} else {
// follow nextStep to see what is needed to proceed
}
} catch (e) {
if (authClient.idx.isEmailVerifyCallbackError(e)) {
const { otp, state } = e;
// you can add special handling for the email verify callback error
// by default it will have a message that says
// `Enter the OTP code in the originating client: ${otp}`
}
console.log(e.message);
}
}
You can work with these methods with Up-Front
and On-Demand
approaches, normally a mix of both approaches will be needed when user inputs are required in the middle of the flow (e.g. multiple factors auth). Below are the general explanation of these two approaches, more code examples will be provided with the specific methods.
You can provide parameters based on your app's policy configuration and user inputs to drive the methods to communicate with Okta's Identity Engine.
You can provide minimum or even no parameters to call the methods, and the methods can provide direction for the flow by indicating the nextStep
and required inputs
.
Most methods in this module resolve IdxTransaction as the response. With Okta's Identity Engine's dynamic nature, it's very important to understand how to follow the response to proceed to the next step.
This field indicates the status of the current flow.
IdxStatus.SUCCESS
: This status indicates the flow has ended successfully with tokens.IdxStatus.PENDING
: This status indicates the flow is still in progress, checknextStep
andmessages
(intermediate form errors) fields in the response to proceed.IdxStatus.FAILURE
: This status indicates error has happened in SDK level, checkerror
field in the response for error handling.IdxStatus.TERMINAL
: This status indicates the flow has run into aterminal
state, checkmessages
field to handle it.IdxStatus.CANCELED
: This status indicates the flow has been canceled. It's normally the response status ofidx.cancel
.
This field contains information to proceed with the next step. It's avaiable when in IdxStatus.PENDING
status.
-
name
: The identifier of the next step. -
type?
: The type of the authenticator that the step belongs to. -
authenticator?
: The authenticator that the step belongs to. -
canSkip?
: This field indicates if the step is skippable or not. -
inputs?
: parameters for the next step.// get "inputs" from the response // inputs: [{ name: 'username', label: 'Username' }] const { nextStep: { inputs } } = await authClient.idx.authenticate(); // gather user inputs (this call should happen in a separated request) const transaction = await authClient.idx.authenticate({ username: 'from user input' });
-
options?
: This field is available in response when the input is a selection. It can also provide information for how to build UI for the next step.
It's available with IdxStatus.SUCCESS
status. Provides tokens set based on scopes configuration.
It passes back messages from Okta's Identity Engine. Form message
and Terminal message
both come to this field.
It's avaialbe with IdxStatus.FAILURE
status when the SDK run into unhandlable state.
It provides transaction meta (pkce meta, interactionHandle, etc.).
It indicates what features are available based on the app / org policy configuration.
It provides information for avaiable next steps.
The convenience method for starting an authentication
flow.
Example (Two factors auth with email authenticator):
Up-Front:
const {
status, // IdxStatus.PENDING
nextStep: {
inputs // [{ name: 'verificationCode', ... }]
}
} = await authClient.idx.authenticate({
username: 'xxx',
password: 'xxx',
authenticators: [AuthenticatorKey.OKTA_EMAIL /* 'okta_email' */]
});
// submit verification code from email
const {
status, // IdxStatus.SUCCESS
tokens
} = await authClient.idx.proceed({ verificationCode: 'xxx' });
On-Demand:
// start the flow with no param
const {
status, // IdxStatus.PENDING
nextStep: {
inputs // [{ name: 'username', ... }, { name: 'password', ... }]
}
} = await authClient.idx.authenticate();
// gather user inputs
const {
status, // IdxStatus.PENDING
nextStep: {
inputs, // [{ name: 'authenticator', ... }]
options // [{ name: 'email', ... }, ...]
}
} = await authClient.idx.proceed({ username: 'xxx', password: 'xxx' });
// a list of authenticators is shown and the user selects "email"
const {
status, // IdxStatus.PENDING
nextStep: {
inputs // [{ name: 'verificationCode', ... }]
}
} = await authClient.idx.proceed({ authenticator: AuthenticatorKey.OKTA_EMAIL /* 'okta_email' */ });
// gather verification code from email (this call should happen in a separated request)
const {
status, // IdxStatus.SUCCESS
tokens
} = await authClient.idx.proceed({ verificationCode: 'xxx' });
The convenience method for starting a self service registration
flow.
Example (Registration with password authenticator enrollment)
Up-Front:
const {
status, // IdxStatus.PENDING
nextStep: {
inputs // [{ name: 'password', ... }]
}
} = await authClient.idx.register({
firstName: 'xxx',
lastName: 'xxx',
email: 'xxx',
authenticators: [AuthenticatorKey.OKTA_PASSWORD /* 'okta_password' */]
});
// submit password
const {
status, // IdxStatus.SUCCESS
tokens
} = await authClient.idx.proceed({ password: 'xxx' });
On-Demand:
const {
status, // IdxStatus.PENDING
nextStep: {
inputs // [{ name: 'firstName', ... }, { name: 'lastName', ... }, { name: 'email', ... }]
}
} = await authClient.idx.register();
// submit user inputs and select password authenticator
const {
status, // IdxStatus.PENDING
nextStep: {
inputs, // [{ name: 'authenticator', ... }]
options // [{ name: 'password', ... }, ...]
}
} = await authClient.idx.proceed({
firstName: 'xxx',
lastName: 'xxx',
email: 'xxx',
authenticator: 'password'
});
// select authenticator (this call should happen in a separated request)
const {
status, // IdxStatus.PENDING
nextStep: {
inputs // [{ name: 'password', ... }]
}
} = await authClient.idx.proceed({ authenticator: AuthenticatorKey.OKTA_PASSWORD /* 'okta_password' */ });
// gather password from user input (this call should happen in a separated request)
const {
status, // IdxStatus.SUCCESS
tokens
} = await authClient.idx.proceed({ password: 'xxx' });
Account activation:
const {
status, // IdxStatus.SUCCESS
tokens
} = await authClient.idx.register({
activationToken: 'xxxxx',
authenticator: 'okta_password',
password: 'xxx'
});
The convenience method for starting a self service password recovery
flow. See Email Verify Callback for more detailed information on this flow.
Example (Password recovery with email authenticator verification)
Up-Front:
const {
status, // IdxStatus.PENDING
nextStep: {
inputs // [{ name: 'verificationCode', ... }]
}
} = await authClient.idx.recoverPassword({
username: 'xxx',
authenticators: [AuthenticatorKey.OKTA_EMAIL /* 'okta_email' */]
});
// submit verification code
const {
status, // IdxStatus.PENDING
nextStep: {
inputs // [{ name: 'password', ... }]
}
} = await authClient.idx.proceed({ verificationCode: 'xxx' });
// submit new password
const {
status, // IdxStatus.SUCCESS
tokens
} = await authClient.idx.proceed({ password: 'xxx' });
On-Demand:
const {
status, // IdxStatus.PENDING
nextStep: {
inputs // [{ name: 'username', ... }]
}
} = await authClient.idx.recoverPassword();
// gather username from user input
const {
status, // IdxStatus.PENDING
nextStep: {
inputs, // [{ name: 'authenticator', ... }]
options // [{ name: 'email', ... }, ...]
}
} = await authClient.idx.proceed({ username });
// user sees a list of authenticators and selects "email"
const {
status, // IdxStatus.PENDING
nextStep: {
inputs // [{ name: 'verificationCode', ... }]
}
} = await authClient.idx.proceed({ authenticator: AuthenticatorKey.OKTA_EMAIL /* 'okta_email' */ });
// gather verification code from email (this call should happen in a separated request)
const {
status,
nextStep: {
inputs // [{ name: 'password', ...}]
}
} = await authClient.idx.proceed({ verificationCode: 'xxx' });
// submit new password
const {
status, // IdxStatus.SUCCESS
tokens
} = await authClient.idx.proceed({ password: 'xxx' });
The convenience method for starting a self service account recovery` flow.
Example (Account unlock with email authenticator verification)
Up-Front:
const {
status, // IdxStatus.PENDING
nextStep: {
inputs // [{ name: 'verificationCode', ... }]
}
} = await authClient.idx.unlockAccount({
username: 'xxx',
authenticators: [AuthenticatorKey.OKTA_EMAIL /* 'okta_email' */]
});
// submit verification code
const {
status, // IdxStatus.TERMINAL
messages // 'Your Account is now unlocked!'
} = await authClient.idx.proceed({ verificationCode: 'xxx' });
On-Demand:
const {
status, // IdxStatus.PENDING
nextStep: {
inputs // [{ name: 'username', ... }]
}
} = await authClient.idx.unlockAccount();
// gather username from user input and
// user sees a list of authenticators and selects "email"
const {
status, // IdxStatus.PENDING
nextStep: {
inputs, // [{ name: 'authenticator', ... }]
options // [{ name: 'email', ... }, ...]
}
} = await authClient.idx.proceed({ username, authenticator: AuthenticatorKey.OKTA_EMAIL });
// gather verification code from email (this call should happen in a separated request)
const {
status, // IdxStatus.TERMINAL
messages // 'Your Account is now unlocked!'
} = await authClient.idx.proceed({ verificationCode: 'xxx' });
Alias for idx.startTransaction
Resolves Start Transaction related fields.
Popular use cases:
- Single entry point when the [flow][] is dynamic
- Provides
meta
for Okta Sign-In Widget integration. - Provides
enabledFeatures
andavailableSteps
for dynamic UI rendering.
// start a recoverPassword transaction
await authClient.idx.startTransaction({ flow: 'recoverPassword' })
Cancels the in progress idx interaction transaction.
await authClient.idx.cancel();
Continues an in-progress idx transaction. If there is no saved transaction, this method will throw. To check for the existence of an in-progress idx transaction, use idx.canProceed
. When provided with {step: stepName}
, continues transaction at specified remediation (step).
if (authClient.idx.canProceed()) {
await authClient.idx.proceed();
}
Returns true if there is a saved in-progress idx transaction. To test against shared browser storage, pass state
.
// checks against sessionStorage, can only proceed in same tab
authClient.idx.canProceed();
// will check against shared localStorage, allows proceeding in another tab if the state matches
authClient.idx.canProceed({ state });
Resumes saved transaction and fires poll request to Okta API. Performs single poll request by default. Front-end clients can pass { refresh: millisecondsInt
} parameter, which enables polling to continue until it is considered complete by server or session times out. refresh
value is available through the nextStep
property of in-progress transaction.
let idxTransaction = await authClient.idx.register();
// ...
// render view that matches current IDX stage
// ...
const pollOptions = idxTransaction?.nextStep?.poll;
if (pollOptions.required) {
authClient.idx.poll(pollOptions.refresh).then(pollResult => {
// render view matching poll result
});
}
Returns the identifier for the currently configured flow.
const flow: FlowIdentifier = authClient.idx.getFlow();
The flow
is usually set automatically when calling one of the flow entrypoints or startTransaction. This method can be used to manually set the flow
:
authClient.idx.setFlow('register');
Handles interaction_code
from Okta's Identity Engine's callback url. It exchanges the code for tokens and saves them in storage.
try {
// handle interactionCode and save tokens
await authClient.idx.handleInteractionCodeRedirect(callbackUrl);
} catch (err) {
if (authClient.idx.isInteractionRequiredError(err)) {
// need to proceed with required authenticator/s
}
throw err;
}