How to Enable Passwordless Registration and OTP Login
By default, Commerce requires every customer to choose a password when registering. This document explains how to make password optional at sign-up, so that customers can create an account and sign back in using only a one-time code sent by SMS — no password ever needed.
Commerce already supports OTP login out of the box: a registered customer can sign in by entering their phone number and confirming a code sent by SMS. What this guide adds is passwordless registration — letting customers create accounts without choosing a password in the first place.
1. Overview
PasswordlessRegisterSerializer makes the password field optional at POST /users/registration/. A customer who registers without a password gets an account with an unusable password — standard password sign-in never works for that account, so OTP login becomes their only sign-in route.
What does not change
Existing customers with usable passwords are not affected — they can still sign in the way they always have.
The OTP login endpoint (
POST /users/otp-login/) is unchanged. Customers identify themselves with their phone number, exactly as before.
Configuration surface
Switch registration to the OTP-capable view
REST_REGISTER_VIEW env variable
omnishop.users.views.RegisterSMSOtpView
Environment Variables, entry 70
Make password optional at registration
REST_AUTH_REGISTER_SERIALIZERS env variable
{"REGISTER_SERIALIZER": "omnishop.users.resources.serializers.PasswordlessRegisterSerializer"}
Environment Variables, entry 71
Authentication backend for phone-based OTP login
AUTHENTICATION_BACKENDS env variable
Must include omnishop.users.backends.PhoneNumberAuthenticationBackend
Environment Variables, entry 72
Attempt budget, code lifetime, resend cooldown
OTP_PURPOSE_SETTINGS dynamic setting
See 2.3
Dynamic Settings, section 141
2. Setup
2.1. Enabling passwordless registration
Two environment variables must be set together in the Commerce service in ACC.
First, switch the registration endpoint to the OTP-capable view:
The default registration view does not support the two-step send/verify flow. RegisterSMSOtpView adds that: it sends an OTP on the first request and creates the account only after the code is verified on the second.
The full description of this variable is entry 70 of Environment Variables.
Second, make the password field optional:
PasswordlessRegisterSerializer extends the OTP registration serializer and marks password as optional. Required fields like first_name, phone, email are unchanged.
When a customer registers without providing a password (or provides an empty string), the account is created with an unusable password — only OTP login works for that account.
The full description of this variable is entry 71 of Environment Variables.
An account with an unusable password can only sign in via OTP. Make sure OTP login is working before enabling passwordless registration — otherwise those customers will have no way to access their account.
2.2. Registering the authentication backend
OTP login requires an authentication backend to resolve a phone number to an account. AUTHENTICATION_BACKENDS must include:
Whether this is the only entry or sits alongside other backends (e.g. allauth.account.auth_backends.AuthenticationBackend for social auth) depends on the project. See entry 72 of Environment Variables for the variable itself.
2.3. Tuning OTP behaviour with OTP_PURPOSE_SETTINGS
This dynamic setting controls the attempt limit, code lifetime, and resend cooldown. Registration challenges have no specific purpose tag, so they resolve against the default entry. Every field, its type, and its fallback value are described in section 141 of Dynamic Settings.
Log in to Omnitron using your own credentials.

Navigate to Sales Channels → Sales Channel Settings → Dynamic Settings.

Search for OTP_PURPOSE_SETTINGS in the search box and click on it in the list.
Enter the configuration and save it:
max_attempts
Wrong-guess budget for a single code. On reaching it the challenge is discarded and the customer must request a new one.
expire_time
How long a sent code stays valid, in seconds.
resent_time_gap
Minimum wait before a new code can be sent for the same challenge, in seconds.
Set resent_time_gap deliberately. Every resend is an SMS you pay for. A cooldown of 0 lets a script drive that cost; a value around a minute is usually enough to stop the abuse without frustrating a customer whose first message was slow to arrive.
3. The registration flow
Registration is a two-step flow on the same endpoint. The customer submits their details first (the backend sends an OTP), then submits again with the code to create the account. Request and response details for POST /users/registration/ are in the Users OpenAPI specification, under API Reference → Commerce OpenAPIs → Users.
Send the code. Post the registration fields without a
code.passwordmay be omitted or sent as an empty string for a passwordless account:The response is
202 Acceptedand echoes back the submitted fields (excludingpasswordandcode, which are write-only). An OTP is sent to the provided phone number.Verify the code. Repost the same fields with the code appended:
On success the response is
201 Createdand includes an authentication token:Resending the code. Add
"resend": trueto the step 1 request and repost it with the same cookie:The response is
202 Acceptedwith an empty body. Theresent_time_gapcooldown applies — disable the resend button for that long rather than letting the customer discover the limit through an error.
4. The sign-in flow
Sign-in is also a two-step flow on a single endpoint. The same endpoint sends the code and verifies it — the backend infers the step from the session. Request and response details for POST /users/otp-login/ are in the Users OpenAPI specification.
Send the code. Post the customer's phone number with no
code:The response is
200 OKwith an empty body. An OTP is sent to that phone number.Verify the code. Repost the same phone number together with the code:
On success the customer is signed in, a session cookie is set, and the response includes an authentication token and a redirect URL:
Resending. Repeat step 1 exactly as it was. There is no separate resend endpoint. The
resent_time_gapcooldown fromOTP_PURPOSE_SETTINGSapplies — disable the resend button for that long.
Handling failures
Branch on error_code, not on the message text — messages are translated and will change per language.
error_code
What happened
What to show
sms_verification_100_2
Wrong code
"The code is incorrect." The challenge is still alive — keep the customer on the same screen.
sms_verification_100_4
Code expired
"The code has expired." Offer a resend.
sms_verification_100_3
Resend too soon
"Please wait before requesting a new code."
sms_verification_100_6
Attempt budget exhausted
The challenge is discarded. Send the customer back to step 1 rather than letting them keep trying.
HTTP 429 (throttled) has no numeric retry field. The number of seconds to wait is embedded in the detail message ("Expected available in N seconds."). Parse it to show a countdown; otherwise show a generic "please try again shortly".
5. Verifying the setup
Walk through both flows once as a customer would and confirm each of the following:
Registration
Submitting the registration form without a
passwordfield returns202 Acceptedand triggers an SMS.Submitting the form again with the correct code returns
201 Createdwith an authentication token.The created account has no usable password — attempting to sign in with any password should fail.
Submitting the form with a password works as before: the account is created and password sign-in succeeds.
Submitting a weak password (e.g.
"123") returns a400validation error. Submitting an empty string does not — it is treated as passwordless.
Login
Signing in with a phone number returns
200 OKand triggers an SMS. Entering the correct code signs the customer in.An unknown phone number produces a generic rejection with no hint that it does not exist.
Entering a wrong code the configured number of times (
max_attempts) ends withsms_verification_100_6, after which a new code is required rather than another attempt.Requesting a resend immediately returns
sms_verification_100_3, and succeeds once theresent_time_gaphas elapsed.An account registered without a password can sign in via OTP and cannot sign in with any password.
Last updated
Was this helpful?

