> For the complete documentation index, see [llms.txt](https://docs.akinon.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.akinon.com/tutorials/commerce/how-to-configure-step-up-verification.md).

# How to Configure Step-Up Verification

This guide explains how to set up **Step-Up Verification** in Commerce. Step-Up Verification adds an extra identity check before a customer can carry out a sensitive change on their account: on top of their password, the customer must enter a short one-time code to prove they are the real account owner before the change is accepted.

The same identity check protects more than one change. Today it is used when a customer changes the **email address** or the **phone number** on their account. You can turn the check on or off — and adjust how it behaves — for each of these separately.

## <mark style="color:red;">1. Overview</mark> <a href="#id-1-overview" id="id-1-overview"></a>

An attacker who gets into a customer's session does not usually stop there. The first thing worth changing is the email address or phone number on the account, because those are the channels password resets and one-time codes are sent to. Step-Up Verification makes that change cost more than a borrowed session, and it makes sure the real owner hears about it.

Each protected change has its own name in the settings, so you can configure them independently:

* **`email-change`** — the identity check before the account's primary email address is changed.
* **`phone-change`** — the identity check before the phone number on the profile is changed.

What this feature does:

* **Asks for two things, not one.** The customer's password *and* a one-time code. A live session on its own is not enough.
* **Sends the code to the customer's own contact.** The code always goes to the phone number or email address already saved on the account — never to the new address or number the customer is trying to set.
* **Will not reuse the channel they signed in with.** A customer who signed in with a text-message code cannot receive the identity code by text message as well; the second check has to be genuinely separate.
* **Ties the code to the exact new value.** A code approved for one email address cannot be used to set a different one.
* **Tells the storefront what is missing.** The storefront does not need to know in advance whether the check is on; it asks, and the response lists which of the two factors is still needed.
* **Announces the change and lets the customer undo it.** Once the change completes, the **previous** email address or phone number receives a message with a one-click undo link. Completing a change the customer asked for costs them nothing else — their password and their signed-in sessions keep working. Using the undo link is what disables the password.

**Settings used in this guide**

| What it controls                                                               | Setting                                                                            | Where                                                                                  | Section                  |
| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------ |
| Which changes are protected, delivery channels, how long the check stays valid | `VERIFICATION_TOKEN_CONF`                                                          | Dynamic Settings, section 142                                                          | [2.1](#id-2-setup)       |
| Code lifetime, wrong-guess limit, resend waiting time                          | `OTP_PURPOSE_SETTINGS`                                                             | Dynamic Settings, section 141                                                          | [2.2](#id-2-setup)       |
| Routing the email change endpoints to their protected variants                 | `EMAIL_CHANGE_VIEW`, `EMAIL_SET_PRIMARY_VIEW`                                      | Environment Variables, entries 94 and 95                                               | [2.3](#id-2-setup)       |
| How long a completed change can be undone                                      | `SECURE_EMAIL_CHANGE_REVOKE_WINDOW_DAYS`, `SECURE_PHONE_CHANGE_REVOKE_WINDOW_DAYS` | Dynamic Settings, sections 143 and 144                                                 | [2.4](#id-2-setup)       |
| Wording of the notice messages                                                 | Mailing templates                                                                  | [E-mail Templates](https://docs.akinon.com/technical-guides/commerce/e-mail-templates) | [4](#id-4-the-undo-link) |

The dynamic settings are documented in [Dynamic Settings](https://docs.akinon.com/technical-guides/commerce/dynamic-settings), and the environment variables in [Environment Variables](https://docs.akinon.com/technical-guides/commerce/environment-variables).

{% hint style="info" %}
The SMS gateway must be configured and working before the check can be used on the text-message channel: see the `SMS_GATEWAY` and `SMS_GATEWAYS` entries in [Dynamic Settings](https://docs.akinon.com/technical-guides/commerce/dynamic-settings).
{% endhint %}

## <mark style="color:red;">2. Setup</mark> <a href="#id-2-setup" id="id-2-setup"></a>

### <mark style="color:red;">2.1. Turning the check on with VERIFICATION\_TOKEN\_CONF</mark>

Nothing is protected until you switch it on for a specific change.

1. Log in to **Omnitron** using your own credentials.

   <figure><img src="/files/i8QWGPZddVK4HOYZ6sAp" alt=""><figcaption></figcaption></figure>
2. Go to **Sales Channels → Sales Channel Settings → Dynamic Settings**.

   <figure><img src="/files/SAObd0WUbnLJRQp0Qe0n" alt=""><figcaption></figcaption></figure>
3. Type **VERIFICATION\_TOKEN\_CONF** in the search box and click it in the list.
4. Enter the configuration on the edit screen, then save. The configuration has one shared section named `default` whose values apply everywhere, plus one section per protected change. A value set inside a change's own section overrides the shared value.

**Example configuration:**

```json
{
  "email-change": {"enabled": true, "allowed_channels": ["email"], "token_ttl_seconds": 900},
  "phone-change": {"enabled": true}
}
```

In this example the email change check is on and sends the code to the customer's current email address, and they have 15 minutes to finish the change afterwards. The phone number change check is on as well, with both channels available and the default 10-minute window.

The keys you can set in each section:

| Key                 | What it means                                                                                                                              |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `enabled`           | Whether the identity check is required for this change.                                                                                    |
| `allowed_channels`  | Which delivery methods the customer may choose from — `sms`, `email`, or both.                                                             |
| `token_ttl_seconds` | How long the completed check stays valid, in seconds. In practice, how long the customer has to finish the change after entering the code. |

For the accepted values and defaults, see section 142 of [Dynamic Settings](https://docs.akinon.com/technical-guides/commerce/dynamic-settings).

{% hint style="warning" %}
Turning the check on inside the `default` section does **not** turn it on for any change. To require it, set `enabled` inside that change's own section — for example inside `email-change`.
{% endhint %}

{% hint style="danger" %}
Think twice before restricting a change to a single channel. The check refuses the channel the customer's current session signed in with, so if `email-change` allows only `sms`, every customer who signed in with a text-message code is locked out of changing their email address. Leaving both channels available avoids this.
{% endhint %}

### <mark style="color:red;">2.2. Adjusting the code itself</mark>

How long a code is valid, how many wrong guesses are allowed, and how soon a new code can be requested come from a separate setting, `OTP_PURPOSE_SETTINGS`, using the same names for the protected changes. On the same **Dynamic Settings** screen, search for it and save a value such as:

```json
{
  "default": {"max_attempts": 5},
  "email-change": {"max_attempts": 3, "expire_time": 300, "resent_time_gap": 60}
}
```

Here an email change allows three wrong guesses instead of five, the code expires after 5 minutes, and a new code cannot be requested more often than once a minute. See section 141 of [Dynamic Settings](https://docs.akinon.com/technical-guides/commerce/dynamic-settings) for every field and its fallback.

### <mark style="color:red;">2.3. Enabling the protected email change flow</mark>

For an email change, turning the purpose on is only half of the setup — the endpoints also have to be routed to their protected variants. Add these two environment variables to the Commerce service in ACC:

```
Name: EMAIL_CHANGE_VIEW

Value: omnishop.users.views.SecureEmailChangeView
```

```
Name: EMAIL_SET_PRIMARY_VIEW

Value: omnishop.users.views.SecureEmailChangeSetPrimaryView
```

The first applies the identity check to the change request. The second sends the notice with the undo link described in [section 4](#id-4-the-undo-link) once the change completes.

A phone number change needs neither variable: enabling `phone-change` is enough, because the profile endpoint carries the check itself.

{% hint style="warning" %}
A partial setup fails quietly rather than loudly, so check all three switches together:

* `EMAIL_CHANGE_VIEW` set but the `email-change` purpose disabled — the change still succeeds on the password alone, with no code ever requested.
* The purpose enabled but `EMAIL_SET_PRIMARY_VIEW` unset — the change is protected, but no notice or undo link is ever sent, so a customer whose account was taken over has nothing to click.
  {% endhint %}

See entries 94 and 95 of [Environment Variables](https://docs.akinon.com/technical-guides/commerce/environment-variables) for the full description of both.

### <mark style="color:red;">2.4. Setting how long a change can be undone</mark>

Two settings decide how long the undo link keeps working. Both default to **7 days**.

1. On the same **Dynamic Settings** screen, type **SECURE\_EMAIL\_CHANGE\_REVOKE\_WINDOW\_DAYS** in the search box and click it in the list.

   <figure><img src="/files/CStHOI1LkuVdUs3C4FqN" alt=""><figcaption></figcaption></figure>
2. Enter the number of days the undo link should keep working, then save.

   <figure><img src="/files/wDUfvoM8QOlaWtjqKG07" alt=""><figcaption></figcaption></figure>
3. Repeat for **SECURE\_PHONE\_CHANGE\_REVOKE\_WINDOW\_DAYS**, which does the same for phone number changes.

Choose a period long enough for a customer to notice an unexpected message in their old inbox — or on their old number — and act on it. The link works only once in any case.

## <mark style="color:red;">3. How the identity check works</mark> <a href="#id-3-how-the-identity-check-works" id="id-3-how-the-identity-check-works"></a>

When a customer starts a change that is protected:

1. The storefront sends the change as usual. Because a factor is missing, the request is refused and the response lists what is still needed — the password, the one-time code, or both — along with the delivery methods the customer may choose from.
2. The customer picks a delivery method. Only the methods you allowed are offered, and the one their session signed in with is already removed from the list.
3. The code is sent to the phone number or email address already on the account.
4. The customer enters the code and their password, and the change is accepted.

If the customer needs a new code they can ask for one, but only after the waiting time you set has passed. If they take too long, the code or the completed check expires and they simply start again.

{% hint style="info" %}
A wrong password does not waste the code. The customer can correct the password and continue with the same code, as long as it has not expired.
{% endhint %}

{% hint style="info" %}
The code is tied to the exact value the customer asked for. If they change the new email address or phone number after requesting the code, the code no longer applies and they have to request another one.
{% endhint %}

### <mark style="color:red;">3.1. Email change, end to end</mark>

An email change has an extra step, because the new address has to be confirmed as well:

1. **The customer asks to change their email address.** They enter the new address and their password, and complete the identity check.
2. **A confirmation link is sent to the new address.** The change has not taken effect yet.
3. **The change completes when that link is opened.** The new address becomes the account's email address, and the previous address receives a message with an undo link.
4. **The customer can undo it.** If they did not ask for this change, they open the undo link in the message sent to their old address.

### <mark style="color:red;">3.2. Phone number change</mark>

There is no confirmation step. Once the identity check passes, the new number is saved immediately and the previous number receives a text message with an undo link.

## <mark style="color:red;">4. The notice and the undo link</mark> <a href="#id-4-the-undo-link" id="id-4-the-undo-link"></a>

The notice sent to the previous email address or phone number is what turns this from a lock into a recovery path. It goes to the channel the change took away, which is the one place an attacker no longer controls.

**When a change completes** the customer is not disturbed further: the previous address or number receives the notice with a one-click undo link, the change is recorded in the audit log, and that is all. Their password and their signed-in sessions keep working, because this is a change they asked for.

**When the undo link is used** the change is assumed to have been someone else's, so the password it was made with is no longer trusted:

* The previous email address or phone number is restored.
* The account's current password is made **unusable**, so it stops working immediately.
* The customer is then sent a password reset link on the restored channel — by email for an email change, by text message for a phone number change — using the same reset flow as the storefront's own "forgot my password". They pick a new password and are back in control.
* The notice sent on that channel tells them both things: that the password was disabled, and that a reset link is on its way.
* **Signed-in sessions are left alone.** The unusable password is what stops the attacker from signing in again; anyone already holding a session keeps it until it expires on its own.

{% hint style="warning" %}
For the phone case, the reset link is delivered only if your project ships an `sms/account/reset-password.html` template. No SMS bodies are provided by the platform, and a missing template is skipped without an error — so the password would be disabled with no way for the customer to set a new one from that message. Confirm that template exists before enabling the phone number check.
{% endhint %}

{% hint style="info" %}
The undo link can be used only once, and only within the number of days you set. A phone number change can also no longer be undone if the number has changed again since — a later, legitimate change is never overwritten by an older link.
{% endhint %}

**Wording of the messages**

The notices are ordinary templates, editable in **Omnitron → Sales Channels → Content Management → Mailing Templates**:

| Message                               | Template                                         |
| ------------------------------------- | ------------------------------------------------ |
| Email change, to the previous address | `account/email/email_change_old_notice.html`     |
| Email change undone                   | `account/email/email_change_revoked_notice.html` |
| Phone change, to the previous number  | `sms/account/phone-change-old-notice.html`       |
| Phone change undone                   | `sms/account/phone-change-revoked-notice.html`   |
| Password reset link after an undo     | Your existing password reset templates           |

Each template's available values — including the undo link itself — are listed in [E-mail Templates](https://docs.akinon.com/technical-guides/commerce/e-mail-templates).

{% hint style="warning" %}
Make the undo link easy to find, and keep the text message short enough to arrive in one piece. This link is the customer's only self-service way back after a change they did not make; a message that buries it, or one that gets cut off before it, removes the recovery path even though the feature is working correctly.

The two "undone" messages should also keep their wording about the password being disabled. A customer who reads only that the change was reversed, and then finds their password rejected, has no idea why.
{% endhint %}

## <mark style="color:red;">5. Checking your setup</mark> <a href="#id-5-checking-your-setup" id="id-5-checking-your-setup"></a>

Go through the flow once as a customer would, and confirm each of the following:

1. Requesting the change without a code is refused, and the response lists the factors that are missing.
2. The delivery methods offered exclude the channel the test account signed in with.
3. The code arrives at the account's existing email address or phone number — not at the new value being set.
4. Changing the new value after requesting the code causes the change to be refused.
5. The previous address or number receives the notice, and its undo link restores the old value.
6. After using the undo link, the old password no longer works and a password reset link arrives on the restored channel. For a phone number change, confirm the reset text message actually arrives — that depends on your own `sms/account/reset-password.html` template.
7. Completing a change *without* using the undo link leaves the customer signed in and their password working.
8. With the check turned off again, the same change succeeds without a code — so customers are never blocked if you switch it off.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.akinon.com/tutorials/commerce/how-to-configure-step-up-verification.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
