On this page

The user enters the site

When the user goes to the home page and the application loads, call idx.NewClient to create an SDK client object.

idx, err := idx.NewClient(
      c.Okta.IDX.ClientID,
      c.Okta.IDX.ClientSecret,
      c.Okta.IDX.Issuer,
      c.Okta.IDX.Scopes,
      c.Okta.IDX.RedirectURI)
if err != nil {
    log.Fatalf("new client error: %+v", err)
}

Add a Sign up link to your app's sign-in page. The self-registration flow begins when the user clicks the Sign up link and the browser takes them to the Create Account page.

A sign-in form with fields for username and password, a next button, and links to the sign-up and forgot your password forms

Note: The account's username is also its email address.

Create a page for the user to enter their basic profile information: their email, first name, and family name.

A sign-up form with fields for first name, family name, and email address, and a create account button

The user submits their profile data

When the user clicks Sign Up, create a UserProfile object and set its FirstName, LastName, and Email to the values entered by the user. Pass this object as a parameter to idxClient.InitProfileEnroll.

profile := &idx.UserProfile{
    FirstName: r.FormValue("firstName"),
    LastName: r.FormValue("lastName"),
    Email: r.FormValue("email"),
}

enrollResponse, err := s.idxClient.InitProfileEnroll(
    context.TODO(), profile)
if err != nil {
    //Error handling
}

s.cache.Set("enrollResponse", enrollResponse, time.Minute*5)
if enrollResponse.HasStep(idx.EnrollmentStepPasswordSetup) {
    http.Redirect(w, r, "/enrollPassword", http.StatusFound)
    return
}

InitProfileEnroll returns an EnrollmentResponse object. Call its HasStep method passing in the EnrollmentStepPasswordSetup constant for the status of the registration process. HasStep returns true indicating that the user should enroll their password.

The user enrolls their password

Create a page that allows the user to supply a new password for verification. For example:

A set password form with two fields to enter and to confirm a password and a submit button

When the user submits their password, call enrollResponse.SetNewPassword, passing in the new password as a parameter.

cer, _ := s.cache.Get("enrollResponse")
enrollResponse := cer.(*idx.EnrollmentResponse)

newPassword := r.FormValue("newPassword")
confirmPassword := r.FormValue("confirmPassword")

enrollResponse, err = enrollResponse.SetNewPassword(context.TODO(), r.FormValue("newPassword"))
if err != nil {
 //Error Handling
}

The app displays a list of authenticators to enroll

Call enrollmentResponse.HasStep passing in the EnrollmentStepSuccess constant for the status of the registration process. HasStep returns true indicating that the user should enroll another factor.

if !enrollResponse.HasStep(idx.EnrollmentStepSuccess) {
  http.Redirect(w, r, "/enrollFactor", http.StatusFound)
  return
}

Create a page that displays a list of required authentication factors the user can enroll to verify their identity. They must choose a factor from the list and click Next. Use EnrollmentResponse.HasStep to identify which factors to display and whether the user can skip the remaining factors. The constants used are:

  • EnrollmentStepSkip
  • EnrollmentStepPhoneVerification
  • EnrollmentStepEmailVerification

The following code shows the logic used to build the list.

cer, _ := s.cache.Get("enrollResponse")
enrollResponse := cer.(*idx.EnrollmentResponse)

phoneFactor := false
emailFactor := false
skipFactor := false

if enrollResponse.HasStep(idx.EnrollmentStepSkip) {
  skipFactor = true
}

if enrollResponse.HasStep(idx.EnrollmentStepPhoneVerification) {
  phoneFactor = true
}

if enrollResponse.HasStep(idx.EnrollmentStepEmailVerification) {
  emailFactor = true
}

if !phoneFactor && !emailFactor {
  s.transitionToProfile(enrollResponse, w, r)
  return
}

The flags set in the previous code are used in the following code to toggle the factor display and skip option.

{{ if not .FactorSkip }}
  <p class="text-sm text-gray-500">We require you to enroll in the following factors:</p>
{{end}}
<div class="mt-4 space-y-4">
  {{if .FactorEmail}}
    <div class="flex items-center">
      <input id="push_email" name="push_factor" value="push_email" type="radio" checked>
      <label for="push_email">
        Email
      </label>
    </div>
  {{end}}
  {{if .FactorPhone}}
    <div class="flex items-center">
      <input id="push_phone" name="push_factor" value="push_phone" type="radio" {if not .FactorEmail}} checked{{end}}>
      <label for="push_phone">
        Phone
      </label>
    </div>
  {{end}}

In this scenario, you configure the app's app sign-in policy to require a password and another factor. Therefore, the user must enroll at least one of either the email or phone factors.

A choose your authenticator form with email and phone authenticator options and a next button

The user submits the email authenticator

If the user chooses and submits the email authenticator, call EnrollmentResponse.VerifyEmail.

cer, _ := s.cache.Get("enrollResponse")
enrollResponse := cer.(*idx.EnrollmentResponse)

enrollResponse, err := enrollResponse.VerifyEmail(r.Context())

if err != nil {
  http.Redirect(w, r, "/enrollFactor", http.StatusFound)
  return
}

  s.cache.Set("enrollResponse", enrollResponse, time.Minute*5)
}
  s.render("enrollEmail.gohtml", w, r)

The app displays an OTP input page

If the call is successful, a one-time passcode (OTP) is sent to the user's email. Build a form that allows the user to enter that OTP.

A form with a field for a verification code and a submit button

The user submits the OTP

The user opens the email and copies the OTP into the form. When the user submits the OTP, call EnrollmentResponse.ConfirmEmail, passing in the OTP as a parameter.

cer, _ := s.cache.Get("enrollResponse")
enrollResponse := cer.(*idx.EnrollmentResponse)

if enrollResponse.Token() != nil {
 //Since the phone factor is available in this use case, tokens won't exist until the
 //user skips the phone factor in the next step
}

enrollResponse, err = enrollResponse.ConfirmEmail(r.Context(), r.FormValue("code"))

The app displays a second list of optional authenticators to enroll

Assuming that the verification was successful, call WhereAmI on the returned EnrollmentResponse object. WhereAmI returns an EnrollmentResponse object with information about how to proceed. In this scenario, the user still has authentication factors to enroll before registration is complete.

 //Identify what is next to proceed with the register
enrollResponse, err = enrollResponse.WhereAmI(r.Context())

s.cache.Set("enrollResponse", enrollResponse, time.Minute*5)
http.Redirect(w, r, "/enrollFactor", http.StatusFound)

Redirect the user to the list page you created earlier to choose another authentication factor. The code is the same. The page should show only the phone factor. However, since this factor is optional and the user has now enrolled two factors, enrollResponse.HasStep(idx.EnrollmentStepSkip) returns true meaning that the list page should now also display a Skip button.

if enrollResponse.HasStep(idx.EnrollmentStepSkip) {
 //This method should return true
}

if enrollResponse.HasStep(idx.EnrollmentStepPhoneVerification) {
 //This call should return true
}

if enrollResponse.HasStep(idx.EnrollmentStepEmailVerification) {
 //This call should return false because the email was already enrolled
}

A choose your authenticator form with only a phone authenticator option, and next and skip buttons

The user skips the phone authenticator

If the user skips phone enrollment, call EnrollmentResponse.Skip. This skips the authenticator enrollment and eliminates the need to verify the factor:

func (s *Server) transitionToProfile(er *idx.EnrollmentResponse, w http.ResponseWriter, r *http.Request) {
  session, err := sessionStore.Get(r, "direct-auth")

  enrollResponse, err := er.Skip(r.Context())

  ...

For more details about enrolling the phone factor, see the sample application. For details on how to verify a sign-in flow with the phone factor, see Sign in with password and phone factors.

Complete registration

The user is now registered with no more factors to be verified. The EnrollmentResponse object returned from the Skip method returns tokens indicating that the registration and sign-in flows were successful. Redirect the user to the app's default signed-in page.

if enrollResponse.Token() != nil {
  session.Values["access_token"] = enrollResponse.Token().AccessToken
  session.Values["id_token"] = enrollResponse.Token().IDToken
  err = session.Save(r, w)
  if err != nil {
    log.Fatalf("could not save access token: %s", err)
  }
}