Add SMS one-time-code login with Esemes Gateway
Follow these steps in order. Esemes Gateway sends SMS through a real Android phone in Mauritius, so a mistake sends a real text to a real person.
- Read the docs first: the MCP tool
get_docsif theesemesMCP server is connected, otherwisehttps://sms-admin.bizmakers.app/llms-full.txt(one file). API base:https://sms.bizmakers.app. - Use a test key while building. Keys starting
smt_send nothing: results are simulated,POST /v1/otpreturnstest_code, number5000 0001fails,5000 0002expires,5000 0003is never delivered. Do not ask for or use a live key (smk_) until the user says to switch. - Keep the key server-side. Put it in an environment variable (
ESEMES_KEY). Never in browser JavaScript, a mobile app bundle, a public repo or a log line. If the app has no backend, add a small server endpoint; do not call the API from the client. - Send:
POST /v1/otpwith{"to": "5123 4567", "purpose": "login"}. Store the returnedotp_idin the server session (not the code, which you never receive outside test mode). - Verify:
POST /v1/otp/verifywith{"otp_id", "code"}. Success is{"valid": true}. Treat any other result as a failed login and show a message forreason:incorrect(withattempts_left),expired_or_used(offer to resend),too_many_attempts(start over). - Sign the person in properly after
{"valid": true}. Use the app's existing auth/session if it has one. Otherwise issue a signed or encrypted session (e.g.iron-sessionor ajoseJWT with a server secret). Never store the phone number in a plain cookie and trust it later: anyone can set that cookie by hand. Session andotp_idcookies arehttpOnly,sameSite: "lax"andsecurein production. - Handle errors:
429 otp_cooldown= wait 30 s before resending (show a countdown);429 recipient_rate_limited= too many codes for that number this hour;country_not_allowed= only Mauritian mobiles (+230 5xxx xxxx) work, say so;recipient_suppressed= the person opted out of this sender's messages (only affects non-OTP sends). - Idempotency: for plain
POST /v1/messagessends that may be retried, pass anidempotency_key. - Webhooks (optional): verify
X-SMS-Signature(t=<unix>,v1=<hex>, HMAC-SHA256 of"<t>." + rawBody) and reject timestamps older than 5 minutes. - Test the whole flow with the test key, including a wrong code, the cooldown and a forged session cookie being refused, then tell the user what to change to go live (swap the key, nothing else).
The MCP server esemes-mcp has tools get_docs, send_sms, send_otp, verify_otp, get_message, list_messages, get_usage; it refuses a live key unless ESEMES_ALLOW_LIVE=1.