We've now upgraded to Okta Identity Engine. This unlocks a number of new features which previously weren't feasible. One of these is "passwordless", or the idea of eliminating, or reducing the need for passwords.
We also currently have an issue within our OAuth flows where users don't always end up signed in the same browser context that they registered from. This can occur if a user clicks a reset password/registration verification link in their on another browser or device, or if deeplinking/https interception isn't working properly on the user's device.
One way of fixing both these issues it to use One Time Passcodes (OTPs), where instead of a user clicking a link in an email, they have to type a 6 digit number into an input field which performs the same action of verifying the user. This means that the user always stays within the same browser device/context, while also securely verifying the user.
To begin with we're going to only implement OTPs for registration only, with the user still having to set a password. After this we will being rolling out full passwordless.
Flowchart
what have i done...
flowchart TD;
start[\User comes to Gateway\] -- Register Page --> register;
register[User enters email address] --> register-interact;
register-interact([POST /oauth2/auth_server/v1/interact]) -- Get interction_handle --> register-introspect;
register-introspect([POST /idp/idx/introspect\ninteractionHandle in body]) -- Get and store stateHandle\nin encryptedState\nunless specified, all calls\nuse stateHandle in body --> register-enroll;
register-enroll([POST /idp/idx/enroll]) -- Error --> register-enroll-error;
register-enroll([POST /idp/idx/enroll]) -- Success --> register-enroll-email;
register-enroll-error{{What error was it?}};
register-enroll-error -- unknown error --> register-enroll-error-unknown;
register-enroll-error -- stateHandle\nexpired--> register-enroll-error-expiry;
register-enroll-error-unknown{{An error case which doesn't match any of the others}} --> okta-classic-registration-fallback;
register-enroll-error-expiry{{User took too long.\nDifficult to get here as\nexpiry of stateHandle is\n2 hours here}} -- restart with email --> register-interact;
register-enroll-email([POST /idp/idx/enroll/new\nuserProfile: email, isGuardianUser,\nregistrationPlatform, registrationLocation]);
register-enroll-email -- Error --> register-enroll-email-error;
register-enroll-email --> register-email-sent;
register-enroll-email-error{{What error was it?}};
register-enroll-email-error -- unknown error --> register-enroll-email-error-unknown;
register-enroll-email-error -- Form\nvalidation error --> register-enroll-email-error-validation;
register-enroll-email-error -- User already exist --> register-enroll-email-error-user-exists;
register-enroll-email-error-unknown{{An error case which doesn't match any of the others}} --> okta-classic-registration-fallback;
register-enroll-email-error-validation{{e.g. missing field, email wrong/invalid}} -- show page with error --> register;
register-enroll-email-error-user-exists{{A user already exists in Okta\nfor the given email.}} -- Send existing\nUser Already Exists\nemail --> okta-classic-registration-fallback;
register-email-sent[Show email sent page\nwith set passcode box];
register-email-sent -- User clicks change email\nclear encryptedState --> register;
register-email-sent -- User clicks resend email --> register-email-sent-resend;
register-email-sent -- User types passcode from email,\nand clicks submit --> register-passwordless;
register-email-sent-resend([POST /idp/idx/challenge/resend]) --> register-email-sent;
register-passwordless([POST /idp/idx/challenge/answer\ncredentials: passcode]);
register-passwordless -- Error --> register-passwordless-error;
register-passwordless -- Success --> register-passwordless-complete-password;
register-passwordless-error{{What error was it?}};
register-passwordless-error -- unkown error --> register-passwordless-error-unknown;
register-passwordless-error -- stateHandle/token\nexpired --> register-passwordless-error-expiry;
register-passwordless-error -- incorrect passcode --> register-passwordless-error-incorrect-passcode;
register-passwordless-error-unknown{{An error case which doesn't\nmatch the others}} -- send user security email\nto set password --> okta-classic-registration-fallback
register-passwordless-error-expiry{{Expired\nafter 30 mins}} -- send user security email\nto set password --> okta-classic-registration-fallback
register-passwordless-error-incorrect-passcode{{Wrong passcode error}}
register-passwordless-error-incorrect-passcode -- display error --> register-email-sent
register-passwordless-complete-password([POST /idp/idx/credential/enroll\nauthenticator: id, factorType=password]);
register-passwordless-complete-password -- Error --> register-passwordless-complete-password-error;
register-passwordless-complete-password -- Show set password page --> register-passwordless-set-password;
register-passwordless-complete-password-error{{What error is it?}}
register-passwordless-complete-password-error -- unknown --> register-passwordless-complete-password-error-unknown
register-passwordless-complete-password-error -- validation --> register-passwordless-complete-password-error-validation
register-passwordless-complete-password-error-unknown -- send user security email\nto set password --> okta-classic-registration-fallback
register-passwordless-complete-password-error-validation{{e.g. weak password, not long, too long etc}} --> register-passwordless-complete-password
register-passwordless-set-password[User enters password];
register-passwordless-set-password --> register-password-set-password-answer;
register-password-set-password-answer([POST /idp/idx/challenge/answer\ncredentials: passcode=password]);
register-password-set-password-answer -- Error --> register-password-set-password-answer-error;
register-password-set-password-answer -- Get and set\nidx cookie\nclear encryptedState --> login-token-redirect;
register-password-set-password-answer-error{{What error was it?}}
register-password-set-password-answer-error -- Unknown error --> register-password-set-password-answer-error-unknown
register-password-set-password-answer-error -- User took too long --> register-password-set-password-answer-error-timeout
register-password-set-password-answer-error -- Validation Error --> register-password-set-password-answer-error-invalid
register-password-set-password-answer-error-unknown -- send user security email\nto set password --> okta-classic-registration-fallback
register-password-set-password-answer-error-unknown{{An error which doesn't match\nthe others}}
register-password-set-password-answer-error-timeout{{stateHandle expired\n30 mins}} -- send user security email\nto set password --> okta-classic-registration-fallback
register-password-set-password-answer-error-invalid{{e.g. password too short, too long, weak etc}}
register-password-set-password-answer-error-invalid --> register-passwordless-set-password
login-token-redirect([303 Redirect to\n/login/token/redirect?stateToken=stateHandle.splitByTilde0]) -- This sets Global SSO session --> complete-oauth-flow;
complete-oauth-flow[/Complete Authorization Code Flow\nThis will redirect user back to app/];
okta-classic-registration-fallback[/Use Okta Classic registration fallback/]
Why?
We've now upgraded to Okta Identity Engine. This unlocks a number of new features which previously weren't feasible. One of these is "passwordless", or the idea of eliminating, or reducing the need for passwords.
We also currently have an issue within our OAuth flows where users don't always end up signed in the same browser context that they registered from. This can occur if a user clicks a reset password/registration verification link in their on another browser or device, or if deeplinking/https interception isn't working properly on the user's device.
One way of fixing both these issues it to use One Time Passcodes (OTPs), where instead of a user clicking a link in an email, they have to type a 6 digit number into an input field which performs the same action of verifying the user. This means that the user always stays within the same browser device/context, while also securely verifying the user.
To begin with we're going to only implement OTPs for registration only, with the user still having to set a password. After this we will being rolling out full passwordless.
Flowchart
what have i done...