SMS
SMS sends text messages through a provider and logs every one. Its main use is phone sign-in: a code by SMS the first time, then a 6-digit PIN.
What it is
| Part | Where | What it does |
|---|---|---|
| SMS settings | Admin, Settings, SMS (/admin/settings/sms) | The provider and its keys, the countries people can sign in from and the default one, and a test send. It also says whether phone sign-in is on, with a link to its switch in User Settings. |
| SMS Logs | Admin, Email + SMS, SMS Logs (/admin/sms-logs) | Every message, sent or only recorded, with its status |
| Phone sign-in | The sign-in page, and the profile's sign-in methods | Sign up and sign in with a mobile number, a one-time code and a PIN |
Two providers exist:
| Provider | Sends | Needs |
|---|---|---|
| Test (log only) | Nothing. The full message, code included, goes to SMS Logs. | Nothing. It is the provider when no settings are saved. |
| MSG91 | One-time codes, through MSG91's OTP API | An MSG91 auth key and the id of a DLT-approved OTP template that contains ##OTP## |
MSG91 sends only registered templates (Indian regulation), so it sends codes and nothing else; there is no free-text SMS.
Turn it on or off
SMS is a switchable feature, id sms, on by default and needing no other feature. To turn it off, list it in src/custom/features.ts:
export const CUSTOM_FEATURES: FeatureChoice = {
off: ['sms'],
};
With it off:
- the SMS settings tab and the SMS Logs page go, and their URLs show the not-found page; the menu's Email + SMS group is called Email;
- phone sign-in is off whatever User Settings says, and its switch is hidden;
- the functions
sendTestSms,checkPhoneSignInand the phone sign-in functions are not built, so the next full functions deploy deletes them.
Settings and logs stay in Firestore. How the switch works for every feature: Feature ids.
Set it up
Open Admin, Settings, SMS.
Under Countries, list the countries people can sign in from: type a name, an ISO id or a code (
44or+44) in the search box and pick one. Each shows with its flag, name and code. Only numbers from these countries can sign in by phone, and the list is never empty: the last country has no remove button. A new site has India only.With more than one country, choose the Default country. The sign-in page's country chip starts on it, and numbers typed without a code belong to it. Removing the default makes the first country left the default. With one country there is nothing to choose.
Saving stores the countries and their calling codes in
Settings/sms; the server reads the codes. It also copies the countries toSettings/users(phoneCountry,phoneCountryCode,phoneCountries), which the sign-in page can read before anyone signs in, whileSettings/smsstays admin only. Opening this page puts the copy right if it is missing or out of step. A site saved before countries were stored reads each saved code as its main country (91India,44the United Kingdom,1the United States) until it is saved again.To send real messages, choose MSG91 and enter the auth key and the OTP template id. The key is never shown again: leave the field empty to keep the saved one.
Click Save SMS settings. Then, under Send a test SMS, pick your country beside the box, enter your own number and click Send test. With MSG91 the test is your OTP template with a made-up code; with the test provider it is only recorded in SMS Logs.
Then turn phone sign-in on under Admin, Settings, User Settings (the SMS page links there). The switch first checks the one Google Cloud setting phone sign-in needs, and shows how to fix it if it is missing (see Google Cloud setup for phone sign-in). With phone sign-in on and the test provider, User Settings also warns that codes are only logged.
With the test provider, no SMS is sent and the sign-in page shows the sign-up code on screen, so a sign-up does not prove the person owns the number. PIN reset codes are only in SMS Logs, unless you turn on Show PIN reset codes on screen (it appears with the test provider, off by default). Turn it on only while you test: anyone who knows a number could then reset that PIN and take over the account. Choosing MSG91 turns it off. Link codes are never shown on screen. Switch to MSG91 before real people use phone sign-in.
Use it
Phone sign-in
- The person types their mobile number. Numbers in any common shape work (
+91 98765-43210,098765 43210); each is stored as E.164, such as+919876543210. - A new number gets a 6-digit code by SMS. After the code, the person enters their name and chooses a 6-digit PIN, and the account is created with the default role from User Settings. New numbers are refused while sign-ups are closed.
- A registered number signs in with its PIN. After 5 wrong PINs, the PIN is locked and the person resets it with a new code.
- Forgot PIN? sends a code; after it, the person chooses a new PIN, and every other session of that account is signed out within the hour.
Codes last 10 minutes and allow 5 tries. A number can get a code for the same purpose once a minute and 5 codes an hour; a request inside the minute sends nothing and the page shows the code already sent, and neither it nor an SMS the provider refuses counts towards the 5. A code for another purpose, such as a PIN reset right after a sign-up code, goes at once. Asking again while the code still works sends the same code, with another 10 minutes, so two messages never carry different codes. PINs such as repeated digits or 123456 are refused. A signed-in person can also add a number to their account, or change their PIN, from their profile. Adding a number sends a code; a number verified this way moves from any other account that had it (see Sign-in methods).
SMS Logs
Each message shows the time, the number (To), the message, its type (Sign-in code or Test), its status and the provider. The status is Logged (test provider, stored as logged), Sent (sent) or Failed (failed), with the provider's error after the message. For a real provider the code is replaced by dots in the stored text, so the log never holds a live code. Filter by status to find failures.
Extend it
To send a text from your own Cloud Functions, call sendSms() from functions/src/sms/sendSms.ts. It sends through the configured provider and writes the log:
import { sendSms } from '../sms/sendSms.js';
const code = '482913';
const result = await sendSms({ to: '+919876543210', purpose: 'otp', code, text: `${code} is your code.` });
// result.status: 'logged' | 'sent' | 'failed'
With MSG91 only messages with a code can be sent, and the text is ignored: the OTP template is what arrives. Check isFeatureOn('sms') (from functions/src/feature-flags.ts) first if your app may turn SMS off.
A new provider is a change to core (functions/src/sms/providers.ts), not to an app. Raise it as a feature request.
What it stores
| Where | Holds |
|---|---|
Settings/sms | provider, defaultCountry and allowedCountries (ISO ids such as IN), defaultCountryCode and allowedCountryCodes (their calling codes, which the server reads), msg91AuthKey, msg91OtpTemplateId |
SmsLogs | One record per message |
phone_otps | The current code for each number, hashed, with its purpose, expiry and tries |
phone_index | Which user record owns each number (keyed by a hash of the number) |
auth_pins | Each account's PIN, hashed, and its failed tries |
_rate_limits | Counters for the per-number and per-caller limits |
The user record keeps phone and phoneVerified. Nothing is kept in Storage.
| Function | Does |
|---|---|
sendTestSms | The test send (admin) |
checkPhoneSignIn | Whether phone sign-in can sign people in on this project, and the fix when it cannot (admin; see Google Cloud setup) |
checkPhoneAccount | Whether a number has an account and a PIN |
requestPhoneOtp, verifyPhoneOtp | Send and check a code |
completePhoneSignup | Create the account after a sign-up code |
signInWithPin, resetPin, setPin | Sign in, reset the PIN after a code, change the PIN when signed in |
All are callables, deployed as arccms-<name>. See Cloud Functions and Data model.
Who can do what
| Who | Can |
|---|---|
| Admin | Read and write Settings/sms; read SmsLogs; send a test SMS |
| Anyone, signed in or not | Call the phone sign-up and sign-in functions while phone sign-in is on; code requests, account checks and PIN tries are rate limited |
| Signed-in person | Change their own PIN (setPin) |
| Nobody from a browser | Read or write phone_otps, phone_index, auth_pins, _rate_limits, or write SmsLogs. Only Cloud Functions do. |
Troubleshooting
| You see | Why, and what to do |
|---|---|
| "Phone sign-in is not turned on for this site." | Turn it on in Admin, Settings, User Settings. |
| "This site takes mobile numbers from India only." on the sign-in page, or "Only numbers starting +91 can be used here." from the server | Add the person's country under Countries. |
| "We couldn't send the SMS." | The provider refused it. SMS Logs has the failed record and the provider's error. For MSG91, check the auth key and the template id. |
| "MSG91 is not set up" | The auth key or the template id is missing in Settings, SMS. |
| "We sent a code a moment ago." with a countdown, or "Too many codes. Try again after 5:42 PM." | The once-a-minute or 5-an-hour limit per number. Use the code already sent, or wait until the time shown. |
| No code arrives and the status in SMS Logs is Logged | The test provider is active: nothing is sent. |