SMS OTP
The SMS One Time Password (OTP) Authenticator adds a factor of SMS to an authentication chain. It can be used standalone but in most cases it makes more sense to use it as a second or third factor in an authentication flow, or as an additional factor in a registration flow. The SMS OTP Authenticator can run in two modes: non-smartphone capable OTP mode or Smartphone enabled Hyperlink mode.
The SMS OTP Authenticator supports the following capabilities:
- SMS One Time Password sent as Text
- SMS One Time Password sent as Hyperlink
- Registration and update of a user’s phone number
Base Configuration#
The Authenticator base configuration is found in the general section authenticator configuration.
Using as standalone factor (Single factor)#
In some cases it’s necessary to use SMS as a single factor. In such cases the SMS OTP Authenticator will start by querying the user for their username.

SMS Authenticator - Enter username
If the username doesn’t exist, or the user doesn’t have a registered phone number the Authenticator will show the same error error.no-user-or-phonenumber, unless prevent-account-enumeration is enabled (see Preventing Account Enumeration).

SMS Authenticator - Invalid phone number or username
Using as second or N-th factor#
The most common case is to use the SMS OTP Authenticator as a factor in a multifactor authentication setup. Commonly after a first factor which collects a username and a password. The SMS OTP Authenticator will then bypass the step of collecting the username again, and will use the username it gets from the SSO Session already established by the previous factor.
See Multi-factor configuration for Authentication for how to configure a Multifactor authentication chain.
Using an Intermediate Attribute#
You can configure the authenticator to pick up the value for the phone number from an attribute, by configuring intermediate-attribute-name, instead of using the account manager to fetch it from a database. It is mandatory that the referenced attribute exists in the intermediate attributes, so it has to be set by a previous authenticator or an authentication-action.
It is not possible to enable registration while using this option.
This option can only be used when the authenticator is not the first factor, it must either have a pre-requisite authenticator configured, or prompted from an authentication-action. Trying to use the authenticator as a single factor (or first factor) with an intermediate-attribute-name configured will result in an error.
SMS OTP in OTP mode#
When using the Authenticator in OTP mode, the flow requires the user to manually read and enter a One Time Password (OTP) that the user has received on their phone. The default strength of the OTP is 6 digits and timeout 1 minute.

SMS Authenticator - Enter OTP
For testing purposes, the following system property may be used to tell C to use a hardcoded OTP value: se.curity:identity-server:unsafe:hardcoded-otp. This must not be used in production environments.
Localization#
The SMS message sent to the users can be customized and localized. The full message key is authenticator.sms.enter-otp.sms.sent.message with default value %s.
If you modify the message, don’t forget to add %s wherever you want the OTP to be in the message.
SMS OTP in Hyperlink mode#
A more convenient setup is to send a hyperlink to the user. When the user clicks the hyperlink the authentication flow resumes automatically. The user is sent to a waiting page while the SMS is being sent to the phone, and remains there until the user clicks the link. The script will timeout after 1 minute by default, the user may choose to restart the flow to try again.

SMS Authenticator - Hyperlink
Localization#
The SMS message sent to the users can be customized and localized. The full message key is authenticator.sms.link-wait.sms.sent.message with default value Click link to authenticate: \n%s.
If you modify the message, don’t forget to add %s wherever you want the link to be in the message.
Registration#
The SMS OTP Authenticator supports phone number registration. This requires the Authenticator to be configured with an account manager that can update the phone number of the underlying account. Both the Standard SQL Account Manager and the SCIM Account Manager support this capability. The SMS OTP Authenticator restricts the user to have a single primary phone number registered with its account. It is not possible to have multiple numbers.
It is required to protect the registration by another factor. I.e. to not let the user change the phone number without being authenticated by some means. This is done by configuring the required-authenticator-for-registration setting in the general authenticator setting.
Typically best practice is to require another N-factor before letting a user change the phone number. This is to avoid potential attempts for an attacker to take over a user’s account by stealing less factors than the end-application requires. So if the SMS OTP is used in a 2-factor flow, the registration section should also require 2 factors in order for it to not allow a user to go from 1 factor to 2 factors.
If no other means of second factors are available, like BankID, then it is at least recommended to use the Email Authenticator as the second registration factor.
Registration during authentication#
The Authenticator can be configured to allow the user to change or register their phone number under an ongoing authentication flow. This is done by configuring the allow-registration-during-login property of the authenticator.
By default this is enabled, which results in a button shown in each step, allowing the user to start the registration flow.
Show info before registration#
When the user is about to register their phone number, we require the user first to authenticate. This can be confusing for the user since it may not be aware of what’s going on. For this reason it can be useful to show an informational page before the user starts the registration flow. By enabling show-info-before-registration the authenticator will redirect to the info page rather than the registration page, when the user clicks the registration button.

SMS Authenticator - Information screen
Automatic Login#
When enabled, automatic login is available after successfully registering a new phone number.
Mark Phone Number as Verified#
When store-phone-number-as-verified is enabled, the authenticator will mark the user’s primary phone number as verified in the account store after a successful authentication, regardless of whether OTP mode or hyperlink mode is used.
This option requires the authenticator to be configured with an account-manager. It cannot be used with intermediate-attribute-name.
Preventing Account Enumeration#
When the authenticator looks up a username, a failed lookup normally produces a distinct error page, thus allowing account-enumeration: an attacker can submit usernames and read off which ones exist.
Enable prevent-account-enumeration to remove that signal. A username that matches no account, or matches an account with no phone number, still follows the same path as a successful one, with the same HTTP status, the same page, the same HAAPI representation and the same response time.
With the option enabled:
- The confirmation page and the HAAPI representation show no phone number, whether the account exists or not.
- In OTP mode the user reaches the code form, and any code submitted is rejected with
enter-otp.error.invalid.otp, exactly as an incorrect code for a real account is. Themax-allowed-attemptsceiling is reached at the same point either way. - In hyperlink mode the user reaches the waiting page, which polls until the hyperlink expires. This is indistinguishable from a real user who never opens the link.
- A failed lookup is counted against
max-challenges-sent, the same limit a sent SMS is counted against. That limit is per session, so it slows enumeration rather than stopping it; an attacker that discards the session cookie starts from zero. Front the endpoint with a rate limit if that matters to you. - Responses are padded to
min-response-time(default 1000 milliseconds), so the two outcomes cannot be told apart by timing. This applies to successful attempts too, and is therefore a deliberate cost on every login through the authenticator. Set it above the slowest send your SMS provider produces, otherwise a slow send still reveals that an SMS was sent.
The option is off by default; enabling it requires an account-manager to be configured. It does not apply to backchannel authentication, which reports an unknown user to the client.
As a Second Factor#
When the authenticator runs after a previous factor, a user whose account has no phone number is not told so while
the option is enabled. They reach the code form or the waiting page like any other user, and no SMS arrives.
When allow-registration-during-login is enabled, those pages still offer to register a phone number, which is how such
a user can complete the login.
Configuration#
The general authenticator config is described under authenticator configuration. This section describes the additional parameters that are available for the SMS plugin.
The SMS Authenticator is located in the configuration at **/profiles/profile{id type}/settings/authentication-service/authenticators/authenticator{id}/sms**
The available parameters for the SMS OTP Authenticator are:
- account-manager-id (ref, mandatory): A reference to an account manager in the profile. This account manager must have a data-store configured for Registration to be possible. Otherwise the authenticator will operate in authentication mode only.
- send-otp-as-code (boolean, default false): This option sets the authenticator to use a One Time Password (OTP) instead of sending a Hyperlink when set to true.
- otp-length (integer between 4 and 10, default 6): This option sets the length of the OTP sent to the user. Only possible to set if
send-otp-as-codeis set to true. - allow-registration-during-login (boolean, default true): Enables the registration button on the authentication screens, to allow the user to change or register a phone number during authentication.
- show-info-before-registration (boolean, default true): Only possible to set if the
allow-registration-during-loginis set to true. This will show the info page before redirecting to the registration page. - max-allowed-attempts (integer between 0 and 100, default 3): This setting restricts how many attempts a user can do to verify the OTP within a session. If the limit is reached, an error
enter-otp.error.too-many-attemptswill be shown. Setting this to 0 allows for unlimited attempts. - max-challenges-sent (integer between 0 and 100, default 3): This setting restricts how many OTPs will be generated within a session. If the limit is reached, an error
enter-username.error.too-many-attemptswill be shown. Setting this to 0 allows for unlimited OTP generations. Whenever a new OTP is generated, the attempts counter is reset to 0. - store-phone-number-as-verified (boolean, optional): When set to true, the authenticator will mark the user’s primary phone number as verified in the account store after a successful authentication. Requires
account-managerto be configured; cannot be used withintermediate-attribute-name. - prevent-account-enumeration (optional): When present, an attempt for a username with no account or no phone number is made indistinguishable from a successful one, in status, page, HAAPI representation and timing. Its
min-response-time(integer, milliseconds, default 1000) is the duration every attempt is padded to. See Preventing Account Enumeration. Requiresaccount-managerto be configured.
The SMS Authenticator requires an SMS provider to be configured on the profile. (see Creating an Authentication Profile)
Example#
<authenticator xmlns="https://curity.se/ns/conf/profile/authentication">
<id>sms1</id>
<previous-authenticator>usernamePassword1</previous-authenticator>
<required-authenticator-for-registration>username1</required-authenticator-for-registration>
<sms>
<account-manager-id>SqlDSAccountManager</account-manager-id>
</sms>
</authenticator>
A SMS authenticator can also be used as a back-channel authenticator.
<authenticators>
<backchannel-authenticator>
<id>bankid-backchannel-test-authenticator</id>
<sms-backchannel xmlns="https://curity.se/ns/ext-conf/sms-backchannel">
<frontchannel-authenticator>sms1</frontchannel-authenticator>
</sms-backchannel>
</backchannel-authenticator>
</authenticators>