Skip to main content
Version: Next

SMS OTP Authenticator

sms_otp sends a short-lived numeric code to a user's phone number over SMS. Unlike TOTP, it has no separate enrollment step — at sign-in, O2ID reads the phone number straight from the user's own attributes, generates a code, stores its hash, and delivers it through a connection you configure. The user proves possession of the phone by submitting the code back.

Requirements

sms_otp needs an sms-provider-category connection to send through. Today that means twilio — see Connections for connection basics.

Create the connection:

o2idctl connections create --type twilio --name "Twilio" \
--field accountSid=<account-sid> \
--field authToken=<auth-token> \
--field fromNumber=+15550001111

fromNumber must be in E.164 format (a leading +, country code, no spaces or punctuation).

Configure SMS OTP

o2idctl authenticators create --type sms_otp --display-name "SMS OTP" \
--connection-id <connection-id>

Fields:

FieldDefaultDescription
codeLength6Number of digits in generated codes
ttlSeconds300How long an issued code stays valid
maxAttempts5Verification attempts allowed before the code is rejected outright
cooldownSeconds30Minimum time between two codes sent to the same user
attributeKeyphoneNumberUser attribute holding the destination phone number

The user's attributeKey attribute must already hold a phone number in E.164 format — a missing or malformed value fails the send instead of silently dispatching an SMS.

Using SMS OTP authenticator in a flow

{
"sms-otp": {
"type": "authenticator",
"onSuccess": "allow",
"onFailure": "sms-otp",
"properties": {
"authenticatorId": "<authenticator-id>",
"maxAttempts": 5,
"onLockout": "deny"
}
}
}

O2ID sends a code the moment a flow run reaches this node, and expects a code value on the next Advance call — the same shape as TOTP. A resend (the run returning to this node after a rejected code) only dispatches a new SMS once cooldownSeconds has elapsed since the last one; within the cooldown window the user re-enters the code from the message they already received.

See Flows for a complete login flow example.