When using Okta Identity Engine (OIE) with the hosted Sign-In Widget, users are often presented with an authenticator selection screen — even when there is only one option available. They need to manually click Email or SMS before the widget sends the code.
If you have locked your policy to a single authenticator, this extra step is pure friction, especially in CIAM contexts. Here is how to remove it with a workaround.
This customization involves manipulating the DOM and may break if Okta updates the Sign-In Widget. Use with caution and test thoroughly. What is proposed in this article is a quick workaround. Always ask to Okta’s Professional Services or an Okta’s Partner to implement the solution if you want to use it in production.
Auto-submit Email authenticator #
Add the following JavaScript to your custom Sign-In Widget page. The key requirement is to register the afterRender listener before calling renderEl — otherwise the event fires before the handler exists.
var config = OktaUtil.getSignInWidgetConfig();
// ... your other config
var setTimeoutToRenderSignInWidget = 0;
var oktaSignIn = new OktaSignIn(config);
// Clicks the primary button on the confirmation screen to trigger email send
function applyAuthenticatorVerificationDataFormCustomisation(context) {
if (context.authenticatorKey === 'okta_email' && context.methodType === 'email') {
setTimeoutToRenderSignInWidget = 1000;
document.getElementById('okta-login-container').style.display = 'none';
var btn = document.querySelector('.select-factor[aria-label*="mail"]')
|| document.getElementsByClassName('button-primary')[0];
if (btn) btn.click();
}
}
// Clicks "Enter code instead" to switch from magic link to OTP input
// Only needed if magic link is enabled in your policy
function applyMfaVerifyPasscodeControllerCustomisation(context) {
if (context.formName === 'challenge-authenticator' && context.authenticatorKey === 'okta_email') {
var otpLink = document.getElementsByClassName('enter-auth-code-instead-link')[0];
if (otpLink) otpLink.click();
}
}
// IMPORTANT: register afterRender before calling renderEl
oktaSignIn.on('afterRender', function (context) {
setTimeoutToRenderSignInWidget = 0;
if (context.formName === 'select-authenticator-authenticate') {
var emailBtn = document.querySelector('.select-factor[aria-label*="mail"]');
if (emailBtn) emailBtn.click();
}
if (context.formName === 'authenticator-verification-data') {
applyAuthenticatorVerificationDataFormCustomisation(context);
}
if (context.controller === 'mfa-verify-passcode') {
applyMfaVerifyPasscodeControllerCustomisation(context);
}
if (setTimeoutToRenderSignInWidget > 0) {
setTimeout(function () {
document.getElementById('okta-login-container').style.display = 'block';
}, setTimeoutToRenderSignInWidget);
} else {
document.getElementById('okta-login-container').style.display = 'block';
}
});
oktaSignIn.renderEl(
{ el: '#okta-login-container' },
OktaUtil.completeLogin,
function(error) { console.log(error.message, error); }
);For the email auto-submit to work reliably, make sure Email authenticator auto-enrollment is enabled in your Okta policy. Without it, users who have not yet enrolled their email address will hit an enrollment screen before reaching the flow above. Configure it under Security → Authenticators → Email → Enrollment and set the policy to Required or Optional with auto-enroll. Official docs: Configure the Okta Email authenticator.
Auto-submit SMS authenticator #
The same approach works for SMS. Just replace the applyAuthenticatorVerificationDataFormCustomisation function.
var config = OktaUtil.getSignInWidgetConfig();
// ... your other config
var setTimeoutToRenderSignInWidget = 0;
var oktaSignIn = new OktaSignIn(config);
// Clicks the primary button on the confirmation screen to trigger SMS send
function applyAuthenticatorVerificationDataFormCustomisation(context) {
if (context.authenticatorKey === 'phone_number' && context.methodType === 'sms') {
setTimeoutToRenderSignInWidget = 1000;
document.getElementById('okta-login-container').style.display = 'none';
var btn = document.querySelector('.select-factor[aria-label*="phone"]')
|| document.getElementsByClassName('button-primary')[0];
if (btn) btn.click();
}
}
// IMPORTANT: register afterRender before calling renderEl
oktaSignIn.on('afterRender', function (context) {
setTimeoutToRenderSignInWidget = 0;
if (context.formName === 'select-authenticator-authenticate') {
var smsBtn = document.querySelector('.select-factor[aria-label*="phone"]');
if (smsBtn) smsBtn.click();
}
if (context.formName === 'authenticator-verification-data') {
applyAuthenticatorVerificationDataFormCustomisation(context);
}
if (setTimeoutToRenderSignInWidget > 0) {
setTimeout(function () {
document.getElementById('okta-login-container').style.display = 'block';
}, setTimeoutToRenderSignInWidget);
} else {
document.getElementById('okta-login-container').style.display = 'block';
}
});
oktaSignIn.renderEl(
{ el: '#okta-login-container' },
OktaUtil.completeLogin,
function(error) { console.log(error.message, error); }
);What happens under the hood #
The Sign-In Widget fires an afterRender event after every screen transition. Each event carries a context object with useful properties:
context.formName— identifies the current screencontext.authenticatorKey— the authenticator in play (okta_email,phone_number)context.methodType— the delivery method (email,sms)
The flow for email or SMS authentication goes through three screens in sequence:
| # | formName |
What the user sees |
|---|---|---|
| 1 | select-authenticator-authenticate |
List of authenticators to pick from |
| 2 | authenticator-verification-data |
Confirmation before sending the code |
| 3 | challenge-authenticator |
Input field to enter the received code |
The customization auto-clicks through screens 1 and 2 so the user lands directly on screen 3.
Why hide and show the container? #
The setTimeoutToRenderSignInWidget mechanism prevents a visible flicker: when the auto-click fires, the widget briefly renders the intermediate screen before transitioning to the next one. Hiding #okta-login-container for 1 second masks that transition. The variable is reset to 0 at the top of every afterRender call so it does not affect unrelated screens.
Optional: override magic link labels #
If magic link is enabled in your Okta email authenticator policy, the challenge screen defaults to showing magic link as the primary action. The snippet above already calls enter-auth-code-instead-link to switch automatically to OTP input. You can optionally override the widget labels to make the UI clearer for the user:
config.i18n = {
en: {
'oie.email.verify.alternate.magicLinkToEmailAddress': 'We sent an email to <$1>{0}</$1>. ',
'oie.email.verify.alternate.instructions': 'Enter the verification code in the text box.',
}
// add other languages if needed
};If magic link is disabled in your policy, skip this block entirely — it has no effect.
Selector stability #
The code uses aria-label attributes as selectors (e.g. [aria-label*="mail"]) rather than positional class selectors like getElementsByClassName('button-primary')[0]. This is intentional: aria-label values are tied to the authenticator being rendered and survive widget upgrades better than CSS class names, which Okta may change between versions. If you have a multilingual widget, verify the aria-label values match your target language — they are localized.
For reference, the official Sign-In Widget customization guide is at developer.okta.com.