On this page
Migrate to the third generation (Gen3) of the Sign-In Widget
This guide explains how to migrate from the second (Gen2) to the third generation (Gen3) of the Okta Sign-In Widget.
Note: This document is only for Okta Identity Engine. See Identify your Okta solution (opens new window) to determine your Okta version.
Learning outcomes
- Upgrade from Gen2 to Gen3 of the Sign-In Widget.
- Use design tokens to adapt your existing customizations to Gen3.
- Use the recommended functions for Gen3.
- Roll back to the second generation, if necessary, without affecting other code in your Sign-In Widget template.
What you need
Sample code
Gen3 design tokens - customization examples
About customizations of Gen3
Gen3 of the Sign-In Widget offers the same user experience as the second generation.
However, it adds accessibility improvements in color contrast, focus management, and screen reader behavior. Gen3 also sets a foundation for improvements in customization and globalization.
Still, there are a few key differences between Gen2 and Gen3. Consider the following:
See Sign-In Widget, third generation (opens new window) to verify if the third generation is right for your org.
CSS and design tokens
Unlike Gen2, Gen3 of the Sign-In Widget doesn’t use fixed CSS class names (className). See Custom buttons for an example.
You can still use CSS to style Gen3 of the Sign-In Widget. However, without fixed className
selectors, you can’t reliably style widget elements with className
-based overrides.
Instead, Gen3 uses design tokens.
Design tokens make the Sign-In Widget's visual style consistent and easier to update. Tokens replace static values to customize the following:
- Color
- Border
- Font family
- Font size
- Font weight
- Line height
- Spacing
See Design token customization examples.
Note: Gen3 also offers autogenerated palettes that preserve accessible color contrast ratios. See the Odyssey by Okta design system (opens new window) and Palette generation and accessibility.
jQuery and Preact
Gen3 is built on Preact (opens new window), a lightweight React Native (opens new window) alternative. The architecture changes from jQuery-based (Gen2) to Preact-based (Gen3). As a result, each generation of the Sign-In Widget handles DOM manipulations in a different way.
For example, the afterRender
function doesn’t work in Gen3 as it does in Gen2. See About afterTransform and afterRender for more information.
Upgrade to Gen3
You can upgrade to Gen3 in a few different ways:
Note: If you have a new Identity Engine org, Gen3 isn't enabled by default.
Use the Admin Console
To upgrade to Gen3:
- In the Admin Console, go to Customizations > Brands.
- Select a brand.
- Go to the Pages tab, and then click Edit or Configure for the Sign-in page.
- Click the Settings tab.
- In the Sign-In Widget version section, click Edit.
- Turn on the Use the third generation toggle.
- Click Save to draft, and then click Publish.
See Brands for more details about brands in Okta.
Use the API
Send a PUT request to the https://{yourOktaDomain}/api/v1/brands/{brandId}/pages/sign-in/customized
endpoint. Include the following:
- Provide a value for the domain:
- If you want to upgrade the
widgetGeneration
of your default brand, use the value of your org domain. See Find your Okta domain. - If you want to upgrade another brand in your org, use the custom domain associated with the brand. See About Okta domain customization.
- If you want to upgrade the
- Provide a value for
brandId
. Ensure that it’s the ID associated with the domain or custom domain that you want to upgrade. - In the
widgetCustomizations
object, setwidgetGeneration
toG3
. See widgetGeneration (opens new window).
See Replace the customized sign-in page (opens new window) for more details.
Add design tokens to your code
Use the code editor to add design tokens to your code. See Use the code editor (Gen3).
Design token examples
Pass the design token values into the OktaSignIn
constructor. For example:
new OktaSignIn({
theme: {
tokens: {
PalettePrimaryMain: '#D11DCA',
TypographyColorBody: '#00297A',
TypographyColorHeading: '#00297A',
TypographyFamilyHeading: 'Helvetica',
TypographyFamilyBody: 'Helvetica',
TypographyWeightHeading: 600,
BorderRadiusMain: '24px',
Spacing5: '2.85714286rem',
}
}
});
See Customization examples for more information.
Use the useSiwGen3 variable
The useSiwGen3
variable is a conditional that allows you to set behaviors for a migration to Gen3.
Use useSiwGen3
to add specific code that works when you enable Gen3, but doesn't affect other code in the template if you need to roll back to Gen2.
Note: If your org uses Classic Engine, the
useSiwGen3
variable appears in the code editor, but you can't use it.
The following example shows how to style the okta-login-container
for a migration to Gen3:
{{#useSiwGen3}}
<style nonce="{{nonceValue}}">
#okta-login-container {
background-color: red !important;
}
</style>
{{/useSiwGen3}}
About afterTransform and afterRender
The afterRender (opens new window) function doesn’t work with Gen3 as it does with Gen2. In Gen3, if you use afterRender
for DOM manipulations, the Okta Sign-In Widget reverts any customizations to default settings. See jQuery and Preact.
The afterTransform
function is the recommended way to apply DOM customizations in Gen3.
To use the afterTransform
function, see Use the afterTransform function (recommended).
To keep using the afterRender
function, see Use the afterRender function (not recommended).
Use the afterTransform function (recommended)
Gen3 of the Sign-In Widget introduces a new function: afterTransform()
. See jQuery and Preact.
The function takes two arguments:
- The name of the form to customize
- A function that receives a context argument with the changes
signIn.afterTransform('form_name', function (context) { }
The afterTransform
function doesn’t update the DOM after components are already rendered. Instead, it allows you to modify the formBag
object sent to the components. The formBag
controls what the components render.
Consider the following examples:
Note: Supplying a wildcard (
*
) for theform_name
matches all forms. You can't use a wildcard for partial name matches.
Change button text examples
The following example shows how to change the text of the Submit button to Log in on the Identify page:
oktaSignIn.afterTransform('identify', ({ formBag }) => {
const submitIndex = formBag.uischema.elements.findIndex(ele => ele.type === 'Button' && ele.options.type === 'submit');
if (submitIndex != -1) {
const submit = formBag.uischema.elements[submitIndex];
submit.label = 'Login';
}
});
The following example shows how to change the text of the Submit button to Register on the Enroll profile page:
oktaSignIn.afterTransform('enroll-profile', ({ formBag }) => {
const submitIndex = formBag.uischema.elements.findIndex(ele => ele.type === 'Button' && ele.options.type === 'submit');
if (submitIndex != -1) {
const submit = formBag.uischema.elements[submitIndex];
submit.label = 'Register';
}
});
Remove an unused link example
The following example shows how to remove the Help, Unlock account?, and Forgot password? links from the Identify page:
oktaSignIn.afterTransform('identify', ({ formBag }) => {
const help = formBag.uischema.elements.find(ele => ele.type === 'Link' && ele.options.dataSe === 'help');
const unlock = formBag.uischema.elements.find(ele => ele.type === 'Link' && ele.options.dataSe === 'unlock');
const forgot = formBag.uischema.elements.find(ele => ele.type === 'Link' && ele.options.dataSe === 'forgot-password');
formBag.uischema.elements = formBag.uischema.elements.filter(ele => ![help, unlock, forgot].includes(ele));
});
Add an instructional paragraph example
The following example shows how to add a custom description to the Password recovery page:
oktaSignIn.afterTransform?.('identify-recovery', ({ formBag }) => {
const titleIndex = formBag.uischema.elements.findIndex(ele => ele.type === 'Title');
// Add custom description after title
const descr = {
type: 'Description',
contentType: 'subtitle',
options: {
variant: 'body1',
content: '<div class=\'my-reset-description\'>Description<br />about<br />recovery</div>'
},
};
if (titleIndex != -1) {
formBag.uischema.elements.splice(titleIndex + 1, 0, descr);
}
});
Use the afterRender function (not recommended)
For Gen3, the afterRender
function doesn't work when used to make DOM manipulations and other render-related side effects. If you use afterRender
for DOM manipulations, Gen3 reverts any customizations to default settings. See jQuery and Preact and Components and Hooks must be pure (opens new window).
If you still want to use afterRender
, consider the following:
- To use
afterRender
for DOM manipulations, consider using theMutationObserver
function. See Use MutationObserver for DOM manipulations. - To use
afterRender
for non-DOM manipulations, you don't need theMutationObserver
function. See Use afterRender for non-DOM manipulations.
Use MutationObserver for DOM manipulations
To prevent Gen3 of the Sign-In Widget from reverting your afterRender
customizations, use the DOM MutationObserver
function. See MutationObserver (opens new window).
To update UI elements, consider the following example:
<script type="text/javascript" nonce="{{nonceValue}}">
var config = OktaUtil.getSignInWidgetConfig();
var oktaSiwRoot = document.querySelector('#okta-login-container');
// The following allows you to reference the context from each render
var contextObj = {};
function cb(mutations, observer) {
// For the primary auth form, updates the button label
if (contextObj.formName === 'identify') {
var el = document.querySelector('[data-type="save"]');
if (el) { el.textContent = 'Some new label'; }
}
// For the reset-authenticator view, updates the button label
if (contextObj.formName === 'reset-authenticator') {
var el = document.querySelector('[data-type="save"]');
if (el) { el.textContent = 'A different label'; }
}
}
// Initializes the mutation observer object
var observer = new MutationObserver(cb);
// Renders the Okta Sign-In Widget
var oktaSignIn = new OktaSignIn(config);
// The following varies based on your configuration
oktaSignIn.renderEl({ el: '#okta-login-container' }, OktaUtil.completeLogin, function (error) {
console.log(error.message, error);
});
oktaSignIn.on('afterRender', function (ctx) { // ← Restores the context
// Resets the global context object for reference using the callback function
contextObj = ctx;
// The following condition only executes the observer for specific views/forms
if (ctx.formName === 'identify' || ctx.formName === 'reset-authenticator') {
// Pauses
observer.disconnect();
// Calls once after initial render
cb();
// Observes for re-renders
observer.observe(oktaSiwRoot, {
subtree: true,
childList: true,
attributes: true,
characterData: true,
});
}
});
</script>
Use afterRender for non-DOM manipulations
The third generation can use the afterRender
function for non-DOM manipulations without extra logic. The following example doesn't update the UI, so it doesn't need the MutationObserver
. Instead, it sends a log to your external logging service:
<script type="text/javascript" nonce="{{nonceValue}}">
var config = OktaUtil.getSignInWidgetConfig();
// Renders the Okta Sign-In Widget
var oktaSignIn = new OktaSignIn(config);
// The following varies based on your own configuration
oktaSignIn.renderEl({ el: '#okta-login-container' }, OktaUtil.completeLogin, function (error) {
console.log(error.message, error);
});
oktaSignIn.on('afterRender', function (context) {
if (context.formName === 'identify') {
// Sends a log to your external logging service indicating a customer landed on this view
someExternalLoggingService.log('Rendered Primary auth form');
}
});
</script>