okta / okta-signin-widget

HTML/CSS/JS widget that provides out-of-the-box authentication UX for your organization's apps
Other
375 stars 317 forks source link

Support Build Status npm version

Okta Sign-In Widget

The Okta Sign-In Widget is a Javascript widget that provides a fully featured and customizable login experience which can be used to authenticate and register users in web and mobile applications.

The widget is used on Okta's default signin page to start an Okta SSO session and set the Okta session cookie in the web browser. It can also perform an OIDC flow to easily integrate your web or mobile applications into the Okta platform.

A custom Okta-hosted signin page can be configured to use your organization's domain name and branding.

The widget can also be embedded directly into your organization's web or mobile applications for a seamless user experience.

See the Usage Guide for more information on how to get started using the Sign-in Widget.

Okta Identity Engine

The Okta Identity Engine (OIE) is a platform service that allows enterprises to build more flexible access experiences that are tailored to their organizational needs. The Okta Sign-in Widget supports OIE in all usage scenarios.

Note: Unless otherwise noted, this README assumes you are using Identity Engine. Information on using the widget with the Classic Engine can be found in this document

Related SDKs

The Sign-in Widget is self-contained and requires no other frameworks at runtime. However, there may be certain features your app needs such as token storage, renewal, or validation, which the widget does not provide.

These SDKs are fully compatible with the Okta Sign-in Widget and provide utilities to help integrate Okta authentication end-to-end in your own application.

Javascript

Java

.Net

Sample applications

Complete sample applications demonstrate usage of the Okta Sign-In Widget in both Okta-hosted and embedded scenarios.

Usage Guide

There are several ways to use the Okta Sign-in Widget:

Okta-hosted sign-in page (default)

Okta provides a sign-in page, available at your organization's URL, which allows the user to complete the entire authorization flow, start an SSO (Single Sign-On) session, and set the Okta session cookie in the web browser. You can customize this page with a background image and logo. By default, signing in on this page redirects the user to the Okta user dashboard.

The default Okta-hosted sign-in page can also authenticate a user in an OIDC application. Your app can redirect to a sign-in page to perform the authentication flow, after which Okta redirects the user back to the app callback. Okta provides SDKs in many languages to help construct the redirect URL and handle the login callback as part of the hosted flow.

Okta provides several complete sample applications which demonstrate how to use the Okta hosted flow.

Okta-hosted sign-in page (customizable)

Okta also provides a hosted sign-in page that can be customized so that it is available under a custom domain which is a subdomain of your company's top-level domain. Although the page is hosted by Okta, you can customize the template of this page in many powerful ways.

As far as your app is concerned, the customized widget behaves the same as the default Okta-hosted widget and you can use the same hosted flow.

Note: There will be a configuration object on the page which contains all required values and enabled features. You will most likely not need to modify this object. If you find that you do need to modify this configuration, take care not to overwrite or remove any required values.

Embedded (self-hosted)

For a completely seamless experience that allows for the highest level of customization, you can embed the Sign-In Widget directly into your application. This allows full use of the widget's configuration and API.

Using an embedded widget, client-side web and native apps can avoid the round-trip redirect of the hosted flow in many cases. See showSignIn.

Server-side web applications will receive OAuth tokens server-side, so they must handle a redirect callback. These apps should use showSignInAndRedirect.

You can embed the Sign-In Widget in your app by either including a script tag that pulls the widget from the Okta CDN or bundling the NPM module into your app.

Using the Okta CDN

Loading our assets directly from the CDN is a good choice if you want an easy way to get started with the Widget, don't already have an existing build process that leverages npm or yarn for external dependencies, or any other reason where you don't want to bundle the Sign-in Widget into your application.

The standard bundle (okta-sign-in.min.js) includes support for both Classic Engine and the Identity Engine. It also includes a polyfill to ensure compatibility with older browsers such as IE11. If your application doesn't need to support IE11, you can include the no-polyfill bundle instead to decrease the loading time for first-time users. The standalone polyfill bundle can be conditionally included on pages to add support for older browsers only when necessary.

If your organization has upgraded to Identity Engine, the smaller oie bundle can be used.

Bundle File Name Approx. Size Classic Engine Identity Engine Polyfill Notes
standard okta-sign-in.min.js 1.7 MB :white_check_mark: :white_check_mark: :white_check_mark: Standard bundle which includes everything
no-polyfill okta-sign-in.no-polyfill.min.js 1.7 MB :white_check_mark: :white_check_mark: Standard bundle without polyfill
oie okta-sign-in.oie.min.js 1.3 MB :white_check_mark: Smaller bundle for OIE enabled orgs
classic okta-sign-in.classic.min.js 1.1 MB :white_check_mark: Smaller bundle for Classic Engine only
polyfill okta-sign-in.polyfill.min.js 108KB :white_check_mark: Standalone polyfill bundle. Can be used along with a widget bundle that does not include the polyfill.

To embed the Sign-in Widget via CDN, include links to the JS and CSS files in your HTML:

<!-- Latest CDN production Javascript and CSS -->
<script src="https://global.oktacdn.com/okta-signin-widget/7.25.1/js/okta-sign-in.min.js" type="text/javascript" integrity="sha384-8QHSy1n8imbyR7imair5z4njOEYiZZk5gqBOJYbbUN3W6HQwW3PZ9lYQiybespeW" crossorigin="anonymous"></script>

<link href="https://global.oktacdn.com/okta-signin-widget/7.25.1/css/okta-sign-in.min.css" type="text/css" rel="stylesheet" integrity="sha384-63aTBe2wMqzMRsDHNmlF/FreSWmf3p08BhUDoPlzVf3d+stbkfWtqmdyJ4He5m3m" crossorigin="anonymous" />

NOTE: The CDN URLs contain a version number. This number should be the same for both the Javascript and the CSS file and match a version on the releases page. We recommend using the latest widget version.

When using one of the bundles without the polyfill included, you may want to conditionally load the standalone polyfill bundle. The polyfill should be loaded before the widget bundle:

<!-- Polyfill for older browsers -->
<script src="https://global.oktacdn.com/okta-signin-widget/7.25.1/js/okta-sign-in.polyfill.min.js" type="text/javascript" integrity="sha384-QzQIGwIndxyBdHRQOwgjmQJLod6LRMchZyYg7RUq8FUECvPvreqauQhkU2FF9EGD" crossorigin="anonymous"></script>

<!-- Widget bundle for Okta Identity Engine -->
<script src="https://global.oktacdn.com/okta-signin-widget/7.25.1/js/okta-sign-in.oie.min.js" type="text/javascript" integrity="sha384-T4d68QBaFQ/b3kDy8qubuXDALwWgBRfP0JsfZsYRzZNlIXflVE2svwIHrPaivLyd" crossorigin="anonymous"></script>

<!-- CSS for widget -->
<link href="https://global.oktacdn.com/okta-signin-widget/7.25.1/css/okta-sign-in.min.css" type="text/css" rel="stylesheet" integrity="sha384-63aTBe2wMqzMRsDHNmlF/FreSWmf3p08BhUDoPlzVf3d+stbkfWtqmdyJ4He5m3m" crossorigin="anonymous" />

Using the npm module

Using our npm module is a good choice if:

To install @okta/okta-signin-widget:

# Run this command in your project root folder

# yarn
yarn add @okta/okta-signin-widget

# npm
npm install @okta/okta-signin-widget --save

This installs the latest version of the Sign-in Widget to your project's node_modules directory.

NOTE: If you're using TypeScript, you'll need to enable synthetic imports in your tsconfig.json.

{
  ...
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    ...
  }
}

Angular (TypeScript) projects require a simliar configuration, also in your tsconfig.json

{
  ...
  "angularCompilerOptions": {
    "allowSyntheticDefaultImports": true,
    ...
  }
}

The widget source files and assets are installed to node_modules/@okta/okta-signin-widget/dist, and have this directory structure:

node_modules/@okta/okta-signin-widget/dist/
├── css/
│   │   # Main CSS file for widget styles
│   └── okta-sign-in.min.css
│
│   # Base font and image files that are used in rendering the widget
├── font/
│
├── img/
│
├── js/
│   │   # CDN JS file that exports the OktaSignIn object in UMD format. This is
│   │   # packaged with everything needed to run the widget, including 3rd party
│   │   # vendor files and polyfills.
│   ├── okta-sign-in.min.js
|   |
│   │   # CDN JS file bundled without polyfills.
│   ├── okta-sign-in.no-polyfill.min.js
│   │
│   │   # Development version of okta-sign-in.min.js. Equipped with helpful
│   │   # console warning messages for common configuration errors.
│   └── okta-sign-in.js
│
│    # Localized strings that are used to display all text and labels in the
│    # widget. Three output formats are included - json and properties
├── labels/
│
│   # Sass files that are used to generate the widget css. If you are already
│   # using Sass in your project, you can include these helper files to make
│   # generating your custom theme easier
└── sass/

After installing:

  1. Copy the assets to a folder that will be distributed to your publicly hosted site. The folders you'll need to copy are css, font, img, js and labels.

  2. Instead of copying the js directory and including it in your page as a global, you can require the Sign-In Widget in your build if you are using Webpack, Browserify, or another module bundling system that understands the node_modules format.

    // Load the Sign-In Widget module
    var OktaSignIn = require('@okta/okta-signin-widget');
    
    // Use OktaSignIn
    var signIn = new OktaSignIn(/* configOptions */);

    Source maps are provided as an external .map file. If you are using Webpack, these can be loaded using the source-map-loader plugin.

    If you want to include widget styles in bundle using style-loader or mini-css-extract-plugin, use the following import:

    import '@okta/okta-signin-widget/css/okta-sign-in.min.css';

    Note: If you use Browserify to bundle your app, you'll need to use the --noparse option:

    browserify main.js \
    --noparse=$PWD/node_modules/@okta/okta-signin-widget/dist/js-okta-sign-in.entry.js \
    --outfile=bundle.js
  3. Make sure you include ES6 polyfills with your bundler if you need to support IE11. The widget provides all needed polyfills through an export:

const polyfill = require('@okta/okta-signin-widget/polyfill');

or

import polyfill from '@okta/okta-signin-widget/polyfill';

Examples

These simple examples should help you get started with using the Sign-in Widget. For complete end-to-end solutions, check out our sample applications.

SPA Application

A Single Page Application (SPA) runs completely in the browser. SPA applications authenticate using client-side flows and store OAuth tokens in browser-based storage.

Note: See configuration for more information on these configuration values

var signIn = new OktaSignIn(
  {
    issuer: 'https://{yourOktaDomain}/oauth2/default',
    clientId: '{{clientId of your OIDC app}}',
    redirectUri: '{{redirectUri configured in OIDC app}}',
  }
);

signIn.showSignIn({
  // Assumes there is an empty element on the page with an id of 'osw-container'
  el: '#osw-container'
}).then(function(res) {
  // Most flows will not require any redirection. In these cases, tokens will be returned directly.
  // res.tokens is an object
  oktaSignIn.authClient.handleLoginRedirect(res.tokens);
}).catch(function(error) {
  // This function is invoked with errors the widget cannot recover from:
  // Known errors: CONFIG_ERROR, UNSUPPORTED_BROWSER_ERROR
});
Web Application

A web application runs primarily on the server. The widget, which executes client-side, will be embedded into an HTML page that includes a script block that configures and renders the widget. OAuth tokens will be received server-side on the application's login redirect callback.

Note: See configuration for more information on these configuration values

var signIn = new OktaSignIn(
  {
    issuer: 'https://{yourOktaDomain}/oauth2/default',
    clientId: '{{clientId of your OIDC app}}',
    redirectUri: '{{redirectUri configured in OIDC app}}',
    state: '{{state passed from backend}}', // state can be any string, it will be passed on redirect callback
    codeChallenge: '{{PKCE code challenge from backend}}', // PKCE is required for interaction code flow
  }
);

// When the authorization flow is complete there will be a redirect to Okta.
// Okta's servers will process the information and then redirect back to your application's `redirectUri`
// If successful, an authorization code will exist in the URL as the "code" query parameter
// If unsuccesful, there will be an "error" query parameter in the URL
signIn.showSignInAndRedirect({
  // Assumes there is an empty element on the page with an id of 'osw-container'
  el: '#osw-container'
}).catch(function(error) {
  // This function is invoked with errors the widget cannot recover from:
  // Known errors: CONFIG_ERROR, UNSUPPORTED_BROWSER_ERROR
});

Flow

In addition to the default authentication flow, the widget supports several pre-defined flows, which allow you to provide single-purpose HTML pages for several common use-cases.

By default, the Okta Sign-In Widget will either proceed with a current flow or start a new authenticate flow. The flow option allows bootstrapping the widget into a specific view such as register, unlock, or reset password. Supported flows:

Note: A particular flow can only work if the admin has configured the org to allow the required operations (example: if Profile Enrollment (User sign-up) in the admin console is not enabled, bootstrapping the widget with flow: 'signup' will result in an error)

// login.html
new OktaSignIn({
  flow: 'login'
});

// signup.html
new OktaSignIn({
  flow: 'signup'
});

// reset_password.html
new OktaSignIn({
  flow: 'resetPassword'
});

// unlock_account.html
new OktaSignIn({
  flow: 'unlockAccount'
});

Redirect Callbacks

A redirect callback occurs 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 authentication or account recovery using email verification.

OAuth callback

The OAuth callback is the last step of the interaction code flow. On successful authentication, the browser is redirected to Okta with information to begin a new session. Okta's servers process the information and then redirect back to your application's redirectUri. If successful, an interaction code is present in the URL as the interaction_code query parameter. If unsuccessful, there is an error and error_description query parameters in the URL. Whether successful or not, the state parameter, which was originally passed to the widget by your application, will also be returned on the redirect. This can be used by server-side web applications to match the callback with the correct user session.

All web applications will handle an OAuth callback. For SPA applications, in many cases the sign-on policy will not require a redirect and these applications can receive tokens directly from showSignIn. However, if the sign-on policy requires redirection for any reason (such as integration with a Social / IDP provider) SPA apps will need to handle an Oauth callback. For this reason we recommend that all SPA apps should be prepared to handle an OAuth callback.

Note: The widget does not handle an OAuth callback directly. Server-side web applications can use one of our SDKs to help with handling the callback. SPA applications can use the okta-auth-js SDK, which is included with the Sign-in Widget as the authClient property.

A SPA application can handle the OAuth callback client-side using the built-in authClient:

// https://myapp.mycompany.com/login/callback?interaction_code=ABC&state=XYZ
if (signIn.authClient.isLoginRedirect()) {
  await signIn.authClient.handleLoginRedirect();
}
Social/IDP callback

After signing in with a 3rd party IDP, the user is redirected back to the application's redirectUri. 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. In this case, the Sign-in Widget should be loaded again so that the flow can continue.

Both server-side web and SPA applications should look for the error query parameter and, if the value is interaction_required, they should render the widget again using the same configuration as the first render. The state parameter will also be passed on the callback which can be used to match the request with the user's application session. The widget will automatically proceed with the transaction.

Email verify callback

Your application will need to implement an email verify callback if your sign-on policy uses Email Magic Link/OTP. After the user clicks the link in an email, they are redirected back to the application's email verify callback URI. The query parameters passed to the application include state and otp. As with the Social/IDP callback, the widget should be rendered again using the same configuration. Additionally, the otp should be passed to the widget's constructor.

Note: See configuration for more information on these configuration values

var signIn = new OktaSignIn(
  {
    issuer: 'https://{yourOktaDomain}/oauth2/default',
    clientId: '{{clientId of your OIDC app}}',
    redirectUri: '{{redirectUri configured in OIDC app}}',
    state: '{{state from URL}}',
    otp: '{{otp from URL}}'
  }
);

API Reference

OktaSignIn

Creates a new instance of the Sign-In Widget with the provided options.

For applications using a customized Okta-hosted widget, there will be a configuration object on the page which contains all required values. You will most likely not need to modify this object.

For applications using an embedded widget, you will need to provide an OIDC configuration:

Note: See configuration for more information on these configuration values

var signIn = new OktaSignIn(
  {
    issuer: 'https://{yourOktaDomain}/oauth2/default',
    clientId: '{{clientId of your OIDC app}}',
    redirectUri: '{{redirectUri configured in OIDC app}}',
  }
);

showSignIn

Renders the widget to the DOM. On success, the promise resolves. On error, the promise rejects. If the sign-on policy requires a redirect to Okta or another identity provider (IdP), the browser will redirect and the promise will not resolve. The responses and errors are the same as those for renderEl.

Note: This is the recommended way to render the widget for SPA applications. Server-side web apps should use the showSignInAndRedirect method instead.

showSignIn accepts the same options as the widget constructor. Options passed to the method will override options from the constructor.

Note: See configuration for more information on these configuration values

var signIn = new OktaSignIn({
  issuer: 'https://{yourOktaDomain}/oauth2/default'
  clientId: '{{clientId of your OIDC app}}',
  redirectUri: '{{redirectUri configured in OIDC app}}',
});

oktaSignIn.showSignIn({
  // Assumes there is an empty element on the page with an id of ‘osw-container’
  el: ‘#osw-container’,
}).then(response => {
  oktaSignIn.authClient.handleLoginRedirect(res.tokens);
}).catch(function(error) {
  // This function is invoked with errors the widget cannot recover from:
  // Known errors: CONFIG_ERROR, UNSUPPORTED_BROWSER_ERROR
  console.log('login error', error);
});

showSignInAndRedirect

Renders the widget to the DOM. On successful authentication, the browser will be redirected to Okta with information to begin a new session. Okta's servers will process the information and then redirect back to your application's redirectUri. If successful, an interaction code will exist in the URL as the interaction_code query parameter. If unsuccessful, there will be error and error_description query parameters in the URL. Whether successful or not, the state parameter which was passed to the widget will also be returned on redirect. This can be used by your server-side web application to match the callback with the correct user session.

showSignInAndRedirect accepts the same options as the widget constructor. Options passed to the method will override options from the constructor.

Note: See configuration for more information on these configuration values

var signIn = new OktaSignIn({
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  clientId: '{{clientId of your OIDC app}}',
  redirectUri: '{{redirectUri configured in OIDC app}}',
  state: '{{state passed from backend}}', // state can be any string, it will be passed on redirect callback
  codeChallenge: '{{PKCE code challenge from backend}}', // PKCE is required for interaction code flow
});

signIn.showSignInAndRedirect({
  // Assumes there is an empty element on the page with an id of 'osw-container'
  el: '#osw-container'
}).catch(function(error) {
  // This function is invoked with errors the widget cannot recover from:
  // Known errors: CONFIG_ERROR, UNSUPPORTED_BROWSER_ERROR
});

hide

Hide the widget, but keep the widget in the DOM.

signIn.hide();

show

Show the widget if hidden.

signIn.show();

remove

Remove the widget from the DOM entirely.

signIn.remove();

on

Subscribe to an event published by the widget.

// Handle a 'ready' event using an onReady callback
signIn.on('ready', onReady);

off

Unsubscribe from widget events. If no callback is provided, unsubscribes all listeners from the event.

// Unsubscribe all listeners from all events
signIn.off();

// Unsubscribe all listeners that have been registered to the 'ready' event
signIn.off('ready');

// Unsubscribe the onReady listener from the 'ready' event
signIn.off('ready', onReady);

authClient

Provides access to the underlying [@okta/okta-auth-js][] object used by the Sign-in Widget. All methods are documented in the API reference.

The authClient is configured using values passed to the widget, such as clientId, issuer, redirectUri, state, and scopes. Options which are not directly supported by the widget can be passed to AuthJS using the authParams object.

The authClient can also be created and configured outside the widget and passed to the widget as the authClient option. If an authClient option is passed, authParams will be ignored.

Note: See configuration for more information on these configuration values

var authClient = new OktaAuth({
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  clientId: '{yourClientId}',
  redirectUri: '{{redirectUri configured in OIDC app}}',
});
var config = {
  baseUrl: 'https://{yourOktaDomain}',
  authClient: authClient,
};

var signIn = new OktaSignIn(config);
// signIn.authClient === authClient

If no authClient option is set, an instance will be created using the options passed to the widget and authParams:

Note: When using the authClient configuration option, make sure to install and use the same version of @okta/okta-auth-js as that used by the installed widget. This version can be found in the package.json file of the installed widget.

var config = {
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  clientId: '{yourClientId}',
  redirectUri: '{{redirectUri configured in OIDC app}}',
  authParams: {
    ignoreSignature: true
  }
};

var signIn = new OktaSignIn(config);
// signIn.authClient.options.clientId === '{yourClientId}'
// signIn.authClient.options.ignoreSignature === true'

before

Adds an asynchronous hook function which will execute before a view is rendered.

Note: See configuration for more information on these configuration values

var config = {
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  clientId: '{yourClientId}',
  redirectUri: '{{redirectUri configured in OIDC app}}',
};
var signIn = new OktaSignIn(config);
signIn.before('success-redirect', async () => {
  // custom logic can go here. when the function resolves, execution will continue.
});

after

Note: This function is only supported when using the Okta Identity Engine

Adds an asynchronous hook function which will execute after a view is rendered.

Note: See configuration for more information on these configuration values

var config = {
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  clientId: '{yourClientId}',
  redirectUri: '{{redirectUri configured in OIDC app}}',
};
var signIn = new OktaSignIn(config);
signIn.after('identify', async () => {
  // custom logic can go here. when the function resolves, execution will continue.
});

Configuration

If you are using the default Okta-hosted signin page, all configuration is handled via the Customization section of the Admin UI.

If you are using the custom Okta-hosted signin page, a configuration object is included on the page which contains all necessary values. You will probably not need to modify this object, but you may use this object as a starting point and add additional customizations.

For embedded widgets, you should set the issuer, clientId, and redirectUri. By default, the widget will run on the Identity Engine using the interaction code flow. The widget can also run against the Classic Engine by setting the useClassicEngine option to true. (See this document for more details on running in Classic Engine.

Basic config options

All embedded widgets should set these basic options: issuer, clientId, and redirectUri.

Note: Okta-hosted widgets should not set these values.

issuer

The URL of the Authorization Server which will issue OAuth tokens to your application.

Note: https://{yourOktaDomain} can be any Okta organization. See our developer guide for help with finding your Okta domain.

Basic configuration using the "default" Custom Authorization Server:

var config = {
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  clientId: '{{clientId of your OIDC app}}',
  redirectUri: '{{redirectUri configured in OIDC app}}',
}

A different Custom Authorization Server can be specified:

var config = {
  issuer: 'https://{yourOktaDomain}/oauth2/custom',
  clientId: '{{clientId of your OIDC app}}',
  redirectUri: '{{redirectUri configured in OIDC app}}',
}

Some applications, such as those that require access to the Okta User API, will want to use the Okta Organization Authorization Server as the issuer. In this case the issuer should match your Okta domain:

var config = {
  issuer: 'https://{yourOktaDomain}',
  clientId: '{{clientId of your OIDC app}}',
  redirectUri: '{{redirectUri configured in OIDC app}}',
}

Note: The Okta Organization Authorization Server is only meant for access to the Okta User API and does not support all of the features of the standard Custom Authorization Server, such as custom scopes on access tokens. It is generally recommended to use a Custom Authorization Server to secure access to your organization's resources.

clientId

Note: This configuration value can be found in the Okta Admin UI. See our developer guide for help with finding your application's clientId

Client Id of the application.

redirectUri

Note: This configuration value can be found in the Okta Admin UI under the application's "General Settings"

The URI to use for the OAuth callback.

useClassicEngine

Defaults to false. By default, the widget will use the interaction code flow on the Identity Engine. Setting the useClassicEngine option to true will cause the widget to run against the Classic Engine instead. (See this document for more details on configuring a widget running in Classic Engine).

Note: This option, along with support for the Classic Engine, will be removed in a future widget version. All customers are encouraged to migrate from the Classic Engine to the Identity Engine. Visit Migrating to OIE for more details on migrating to Identity Engine.

codeChallenge

The PKCE code challenge. SPA applications will not need this option since the widget will manage the entire transaction. Web applications should generate their own code challenge and code secret. The code challenge is passed to the widget, and the code secret is held server-side to obtain tokens on the redirect login callback.

Note: Check out our sample applications for complete working examples of interaction code flow using PKCE

state

An application-provided value which will be returned as a query parameter during on the redirect login callback or email verify callback. If no value is set, then a random value will be created. When handling an email verify callback, the value of state from the query parameter should be passed to the widget as a configuration option (along with otp). This will ensure that the widget can load and resume the current transaction.

otp

When handling an email verify callback, the value of otp from the query parameter should be passed to the widget as a configuration option (along with state). This will ensure that the widget can load and resume the current transaction.

scopes

Defaults to ['openid', 'email']. Specify what information to make available in the returned id_token or access_token. For OIDC, you must include openid as one of the scopes. For a list of available scopes, see Scopes and Claims.

idpDisplay

Display order for external identity providers relative to the Okta login form. Defaults to SECONDARY.

Brand

logo

Local path or URL to a logo image that is displayed at the top of the Sign-In Widget

// Hosted on the same origin
logo: '/img/logo.png'

// Can also be a full url
logo: 'https://acme.com/img/logo.png'

logoText

Text for alt attribute of the logo image, logo text will only show up when logo image is not available

// Text to describe the logo
logoText: 'logo text'

brandName

The brand or company name that is displayed in messages rendered by the Sign-in Widget (for example, "Reset your {brandName} password"). If no brandName is provided, a generic message is rendered instead (for example, "Reset your password"). You can further customize the text that is displayed with language and text settings.

brandName: 'Spaghetti Inc.'

colors

These options let you customize the appearance of the Sign-in Widget.

If you want even more customization, you can modify the Sass source files and build the Widget.

colors.brand

Sets the brand color as the background color of the primary CTA button. Colors must be in hex format, like #008000.

colors: {
  brand: '#008000'
}

Localization

Supported languages

Support for additional languages can be added with the assets.languages option.

language

Set the language of the widget. If no language is specified, the widget will choose a language based on the user's browser preferences if it is supported, or defaults to en.

// You can simply pass the languageCode as a string:
language: 'ja'

// Or, if you need to determine it dynamically, you can pass a
// callback function:
language: (supportedLanguages, userLanguages) => {
  // supportedLanguages is an array of languageCodes, i.e.:
  // ['cs', 'da', ...]
  //
  // userLanguages is an array of languageCodes that come from the user's
  // browser preferences
  return supportedLanguages[0];
}

defaultCountryCode

Set the default countryCode of the widget. If no defaultCountryCode is provided, defaults to US. It sets the country calling code for phone number accordingly in the widget.

i18n

Override the text in the widget. The full list of properties can be found in the login.properties and country.properties files.

// The i18n object maps language codes to a hash of property keys ->
// property values.
i18n: {
  // Overriding English properties
  'en': {
    'primaryauth.title': 'Sign in to Acme',
    'primaryauth.username.placeholder': 'Your Acme Username'
  },
  // Overriding Japanese properties
  'ja': {
    'primaryauth.title': 'ACMEにサインイン',
    'primaryauth.username.placeholder': 'ACMEのユーザー名'
  }
}

// If you want to override any properties in the country.properties file,
// you will need to prefix the name with "country.":
i18n: {
  'en': {
    // login.properties keys do not have a special prefix
    'primaryAuth.title': 'Sign in to Acme',

    // country.properties keys are prefixed with 'country.'
    'country.AF': 'Afghanistan, edited',
    'country.AL': 'Albania, edited'
  }
}

assets

assets.baseUrl

Override the base url the widget pulls its language files from. The widget is only packaged with english text by default, and loads other languages on demand from the Okta CDN. If you want to serve the language files from your own servers, update this setting.

// Loading the assets from a path on the current domain
assets: {
  baseUrl: '/path/to/dist'
},

// Full urls work as well
assets: {
  baseUrl: 'https://acme.com/assets/dist'
}

Note: The json files can be accessed from the dist/labels/json folder that is published in the npm module.

assets.languages

Specify the list of supported languages which are hosted and accessible under the path {assets.baseUrl}/labels/json/. This option supersedes the default list of supported languages. If an unsupported language is requested (explicitly using the language option or automatically by browser detection), the default language (en) will be used.

assets.rewrite

You can use this function to rewrite the asset path and filename. Use this function if you will host the asset files on your own host, and plan to change the path or filename of the assets. This is useful, for example, if you want to cachebust the files.

assets: {
  // Note: baseUrl is still needed to set the base path
  baseUrl: '/path/to/dist',

  rewrite: (assetPath) => {
    // assetPath is relative to baseUrl
    // Example assetPath to load login for 'ja': "/labels/json/login_ja.json"
    return someCacheBust(assetPath);
  }
}

Links

Back to sign in link

Set the following config option to override the back to sign in link URL. If not provided, the widget will navigate to Primary Auth.

backToSignInLink: 'https://www.backtosignin.com'

Note: For compatibility with previous widget versions, signOutLink is accepted as an alias for backToSignInLink

Sign up link

You can add a registration link to the primary auth page by setting the following config options.

registration.click

Function that is called when the registration link is clicked.

// An example that adds a registration link underneath the login form on the primary auth page
registration: {
  click: () => {
    window.location.href = 'https://acme.com/sign-up';
  }
}

Help Links

Set the following config options to override the help link URLs on the Primary Auth page.

// An example that overrides all help links, and sets two custom links
helpLinks: {
  help: 'https://acme.com/help',
  forgotPassword: 'https://acme.com/forgot-password',
  unlock: 'https://acme.com/unlock-account',
  custom: [
    {
      text: 'What is Okta?',
      href: 'https://acme.com/what-is-okta'
    },
    {
      text: 'Acme Portal',
      href: 'https://acme.com',
      target: '_blank'
    }
  ]
}
helpLinks.help

Custom link href for the "Help" link

helpLinks.forgotPassword

Custom link href for the "Forgot Password" link

helpLinks.unlock

Custom link href for the "Unlock Account" link. For this link to display, features.selfServiceUnlock must be set to true, and the self service unlock feature must be enabled in your admin settings.

helpLinks.custom

Array of custom link objects {text, href, target} that will be added after the "Help" link. The target of the link is optional.

hCaptcha options

Set the following config options to customize hCaptcha script URI:

// An example that uses cn1 host
hcaptcha: {
  scriptSource: 'https://cn1.hcaptcha.com/1/api.js',
  scriptParams: {
    apihost: 'https://cn1.hcaptcha.com',
    endpoint: 'https://cn1.hcaptcha.com',
    assethost: 'https://assets-cn1.hcaptcha.com',
    imghost: 'https://imgs-cn1.hcaptcha.com',
    reportapi: 'https://reportapi-cn1.hcaptcha.com',
  }
},

reCAPTCHA options

Set the following config options to customize reCAPTCHA script URI:

// An example that uses recaptcha.net
recaptcha: {
  scriptSource: 'https://recaptcha.net/recaptcha/api.js'
},

Hooks

Asynchronous callbacks can be invoked before or after a specific view is rendered. Hooks can be used to add custom logic such as instrumentation, logging, or additional user input. Normal execution is blocked while the hook function is executing and will resume after the Promise returned from the hook function resolves. Hooks can be added via config, as shown below, or at runtime using the before or after methods. The full list of views can be found in RemediationConstants.js.

// Hooks can be set in config
hooks: {
  'identify': {
    after: [
      async function afterIdentify() {
        // custom logic goes here
      }
    ]
  },
  'success-redirect': {
    before: [
      async function beforeSuccessRedirect() {
        // custom logic goes here
      }
    ]
  }
}

// Hooks can also be added at runtime
signIn.before('success-redirect', async () => {
  // custom logic goes here
});

signIn.after('identify', async () => {
  // custom logic goes here
});

Username and password

transformUsername

Transforms the username before sending requests with the username to Okta. This is useful when you have an internal mapping between what the user enters and their Okta username.

// The callback function is passed two arguments:
// 1) username: The name entered by the user
// 2) operation: The type of operation the user is trying to perform:
//      - PRIMARY_AUTH
//      - FORGOT_PASSWORD
//      - UNLOCK_ACCOUNT
transformUsername: (username, operation) => {
  // This example will append the '@acme.com' domain if the user has
  // not entered it
  return username.includes('@acme.com')
    ? username
    : username + '@acme.com';
}

Registration

Callback functions can be provided which will be called at specific moments in the registration process.

registration: {
  parseSchema: (schema, onSuccess, onFailure) => {
      // handle parseSchema callback
      onSuccess(schema);
  },
  preSubmit: (postData, onSuccess, onFailure) => {
      // handle preSubmit callback
      onSuccess(postData);
  },
  postSubmit: (response, onSuccess, onFailure) => {
      // handle postsubmit callback
      onSuccess(response);
  }
},

parseSchema

Callback used to change the JSON schema that comes back from the Okta API.

parseSchema: (schema, onSuccess) => {
  // This example will add an additional field to the registration form.
  schema.push(
    {
      'name': 'userProfile.address',
      'type': 'text',
      'placeholder': 'Enter your street address',
      'maxLength': 255,
      'label-top': true,
      'label': 'Street Address',
      'required': true,
    }
  );
  onSuccess(schema);
}

preSubmit

Callback used primarily to modify the request parameters sent to the Okta API.

preSubmit: (postData, onSuccess) => {
  // This example will append the domain name to the email address if the user forgets to add it during registration.
  if (!postData.userProfile.email.includes('@acme.com')) {
    postData.userProfile.email += '@acme.com';
  }
  }
  onSuccess(postData);
}

postSubmit

Callback used to primarily get control and to modify the behavior post submission to registration API.

postSubmit: (response, onSuccess) => {
  // This example will log the API request body to the browser console before completing registration.
  console.log(response);
  onSuccess(response);
}

Handling registration callback errors

Use the default error
preSubmit: (postData, onSuccess, onFailure) => {
  // A generic form level error is shown if no error object is provided
  onFailure();
}
Display a form error
preSubmit: (postData, onSuccess, onFailure) => {
  const error = {
    "errorSummary": "Custom form level error"
  };
  onFailure(error);
}
Display a form field error
  preSubmit: (postData, onSuccess, onFailure) => {
    const error = {
        "errorSummary": "API Error",
        "errorCauses": [
            {
              "errorSummary": "Custom field level error",
              "property": "userProfile.email",
            }
        ]
    };
    onFailure(error);
  }

Custom Buttons

You can add custom buttons underneath the login form on the primary auth page by setting the following config options. If you'd like to change the divider text, use the i18n config option.

// An example that adds a custom button below the login form on the Sign in form
customButtons: [{
  title: 'Click Me',
  className: 'btn-customAuth',
  click: () => {
    // clicking on the button navigates to another page
    window.location.href = 'https://www.example.com';
  }
}]

// An example that adds a custom button with a localized title below the Sign in form
i18n: {
  en: {
    'customButton.title': 'Custom Button Title',
  },
},
customButtons: [{
  i18nKey: 'customButton.title',
  className: 'btn-customAuth',
  click: () => {
    // clicking on the button navigates to another page
    window.location.href = 'https://www.example.com';
  }
}]

customButtons.title

String that is set as the button text (set only one of title OR i18nKey)

customButtons.i18nKey

Custom translation key for button text specified in i18n config option (set only one of title OR i18nKey)

customButtons.className

Optional class that can be added to the button

customButtons.click

Function that is called when the button is clicked

Feature flags

Enable or disable widget functionality with the following options.

features: {
  showPasswordToggleOnSignInPage: true,
  hideSignOutLinkInMFA: false,
  rememberMe: true
}

features.showPasswordToggleOnSignInPage

Defaults to true. Shows eye icon to toggle visibility of the user entered password on the Okta Sign-In page. Password is hidden by default, even when this flag is enabled. Passwords are visible for 30 seconds and then hidden automatically.

features.showIdentifier

Defaults to true. Shows the user's identifier on any view with user context.

features.hideSignOutLinkInMFA

Defaults to false. Hides the "Back to sign in" link for authenticator enrollment and challenge flows.

features.rememberMe

Defaults to true. Pre-fills the identifier field with the previously used username.

features.autoFocus

Defaults to true. Automatically focuses the first input field of any form when displayed.

features.disableAutocomplete

Defaults to false. Sets the autocomplete attribute on input fields to off.

cspNonce

The widget injects secure inline script/style blocks at runtime for customization purpose, but those blocks may violate CSP rules that set in the hosted web page.

cspNonce allows set nonce value from Content-Security-Policy header to the injected blocks, so script/style from those blocks can still be executable.

Note: nonce directive was added to CSP level2, you may still see CSP errors in browser console if it's used in unsupported browsers.

Events

Events published by the widget. Subscribe to these events using on.

ready

Triggered when the widget is ready to accept user input for the first time. Returns a context object containing the following properties:

signIn.on('ready', function (context) {
  // The Widget is ready for user input
});

afterError

The widget will handle most types of errors - for example, if the user enters an invalid password or there are issues authenticating. To capture an authentication state change error after it is handled and rendered by the Widget, listen to the afterError event. You can also capture OAuth and registration errors. For other error types, it is encouraged to handle them using the renderEl error handler.

Returns context and error objects containing the following properties:

signIn.on('afterError', function (context, error) {
    console.log(context.controller);
    // reset-password

    console.log(error.name);
    // AuthApiError

    console.log(error.message);
    // The password does not meet the complexity requirements
    // of the current password policy.

    console.log(error.statusCode);
    // 403
});

afterRender

Triggered when the widget transitions to a new page and animations have finished. Returns a context object containing the following properties:

// Overriding the "Back to sign in" click action on the Forgot Password page
signIn.on('afterRender', function (context) {
  if (context.controller !== 'forgot-password') {
    return;
  }
  var backLink = document.getElementsByClassName('js-back')[0];
  backLink.addEventListener('click', function (e) {
    e.preventDefault();
    e.stopPropagation();
    // Custom link behavior
  });
});

Building the Widget

We use Yarn as our node package manager. To install Yarn, check out their install documentation.

  1. Clone this repo and navigate to the new okta-signin-widget folder.

    git clone https://github.com/okta/okta-signin-widget.git
    cd okta-signin-widget
  2. Install our Node dependencies.

    yarn install
  3. Create a .widgetrc.js file in the okta-signin-widget directory with your desired configuration:

    module.exports = {
      issuer: 'https://{yourOktaDomain}/oauth2/default',
      clientId: '{{clientId of your OIDC app}}',
      redirectUri: '{{redirectUri configured in OIDC app}}',
      logoText: 'Windico',
      features: {
        rememberMe: true,
      },
    }
  4. Build the widget, start a local connect server that hosts it, and launch a browser window with the widget running.

    yarn start

    or start local connect server in watch mode, changes in src/ and assets/sass/ folders will trigger browser auto reload.

    yarn start --watch
  5. Finally, enable CORS support for our new server by following these instructions. You can now authenticate to Okta using your very own, customizable widget!

Build and test commands

Command Description
yarn start Build the widget, start the server, and open a browser window with the widget loaded
yarn start --watch Build the widget, start the server, and open a browser window with the widget loaded and watch on widget js and sass changes
yarn build:dev Build an unminified version of the widget
yarn build:release Build a minified, uglified version of the widget (okta-sign-in.min.js) and a non-minified development version of the widget (okta-sign-in.js).
yarn test -t jest Run unit tests using Jest
yarn test -t jest --suiteHelp Display optional test suite options
yarn test -t testcafe <browser> Run testcafe tests on selected browser (example: yarn test -t testcafe chrome)
yarn lint Run eslint and scss linting tests

Local development workflow using yarn link

When developing locally, you may want to test local changes to the widget in another project, which is also local. To use yarn link locally, follow these steps:

In okta-signin-widget directory:

yarn build:release
cd dist
yarn link
yarn build:webpack-dev --output-path ./dist/js --output-filename okta-sign-in.entry.js --watch

This will watch for changes in signin widget source code and automatically rebuild to the dist directory.

In your other local project directory:

yarn link @okta/okta-signin-widget

Utilizing Pseudo-loc

:warning: This tool requires access to Okta's internal registry via the VPN.

A pseudo-localized language is a test language created to identify issues with the internationalization process. Generated from login.properties English resources, the pseudo-loc properties file can be used to test UI's for English leaks and CSS layout issues caused due to localization.

To generate pseudo-loc, run the following command:

# Navigate into the pseudo-loc package
[okta-signin-widget]$ cd packages/@okta/pseudo-loc/

# Install all required dependencies and generate login_ok_PL.properties
# NOTE: This requires VPN access
[pseudo-loc]$ yarn install
[pseudo-loc]$ yarn pseudo-loc

Finally, update the .widgetrc.js file to use the ok_PL language, and start the widget playground.

module.exports = {
  // ...other widget config
  // ...
  language: 'ok-PL',
  ...
}

Browser support

Need to know if the Sign-In Widget supports your browser requirements? Please see Platforms, Browser, and OS Support.

Contributing

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