Une API d'authentification réutilisable qui génère, envoie et vérifie des codes à usage unique via Telegram, Signal ou WhatsApp — déployée en une commande sur votre propre serveur.
Tout ce qu'il faut pour délivrer et vérifier des codes en production — sans dépendre d'un fournisseur payant.
Codes générés par CSPRNG, stockés sous forme de digest HMAC-SHA256 salé — jamais en clair, comparaison en temps constant.
Telegram, Signal, WhatsApp derrière une interface commune. Si un canal échoue, le suivant prend le relais automatiquement.
TTL court configurable, usage unique, compteur de tentatives et verrouillage après dépassement — le tout dans Redis.
Limitation de débit par numéro (fenêtre glissante) et par IP, cooldown de renvoi — vos numéros expéditeurs et vos utilisateurs sont protégés.
Vos données restent chez vous : un seul docker compose up, configuration entièrement par variables d'environnement.
Trois endpoints REST, clés API par application, réponses JSON normalisées — intégrable en dix lignes dans n'importe quel backend.
API Bot officielle — gratuite, contractuellement licite. Le canal recommandé.
Sidecar signal-cli REST — client non officiel, réservé aux usages propres.
Bridge Baileys simulant un appareil lié — contraire aux conditions WhatsApp.
docs/RISKS.md.Votre app appelle /v1/otp/request avec le numéro E.164 et les canaux souhaités.
Code CSPRNG généré, tenté sur le canal préféré puis les replis. Seul le digest HMAC est persisté.
L'utilisateur reçoit le code dans sa messagerie et le soumet à votre application.
/v1/otp/verify compare en temps constant — usage unique, expiration et verrouillage gérés.
# Demande d'OTP curl -X POST https://otp.mondomaine.fr/v1/otp/request \ -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \ -d '{"phone":"+33612345678","channel":"telegram","fallback_channels":["signal"]}' # → {"request_id":"9f2c…","status":"sent","channel_used":"telegram","expires_in":300} curl -X POST https://otp.mondomaine.fr/v1/otp/verify \ -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \ -d '{"request_id":"9f2c…","code":"483920"}' # → {"status":"verified"}
import httpx API = "https://otp.mondomaine.fr" H = {"X-API-Key": API_KEY} r = httpx.post(f"{API}/v1/otp/request", headers=H, json={ "phone": "+33612345678", "channel": "telegram", "fallback_channels": ["signal"], }) rid = r.json()["request_id"] code = input("Code reçu : ") ok = httpx.post(f"{API}/v1/otp/verify", headers=H, json={"request_id": rid, "code": code}) assert ok.json()["status"] == "verified"
const API = "https://otp.mondomaine.fr"; const H = { "X-API-Key": API_KEY, "Content-Type": "application/json" }; const req = await fetch(`${API}/v1/otp/request`, { method: "POST", headers: H, body: JSON.stringify({ phone: "+33612345678", channel: "telegram", fallback_channels: ["signal"], }), }); const { request_id } = await req.json(); const res = await fetch(`${API}/v1/otp/verify`, { method: "POST", headers: H, body: JSON.stringify({ request_id, code: userCode }), }); // res.json().status === "verified"
Chaque couche applique le principe du moindre exposé — même une fuite de la base ne révèle ni codes ni numéros.
secrets — imprévisible et uniforme.compare_digest — aucune fuite par timing sur la comparaison.Auto-hébergé : vous payez votre serveur, pas vos messages.
Pour démarrer et tester la plateforme.
Pour les produits en production.
Pour les flottes et la conformité.
Clonez, configurez vos secrets, lancez Compose. C'est tout.
$ cp .env.example .env $ openssl rand -hex 32 # OTP_PEPPER, INTERNAL_TOKEN… $ docker compose up -d --build $ curl http://localhost:8080/health # {"status":"ok"}