Limited registrations open — claim your spot
minimoth

Firebase

Use MiniMoth for OTP delivery and verification, then sign the user into Firebase Auth with a custom token minted by your own backend.

How it fits together

MiniMoth verifies the phone number. Your backend — which already holds your Firebase service account — mints a Firebase custom token for that user and hands it to the client, which exchanges it for a Firebase session:

  1. Client calls your backend, which calls otp/verify via MiniMoth
  2. Your backend calls getAuth().createCustomToken(uid)
  3. Client calls signInWithCustomToken() with that token
MiniMoth never touches the Firebase Admin SDK and never stores your Firebase service account credentials — the custom token is minted entirely on your own backend, from your own credentials. This is a Custom Auth Token integration, not a Firebase Extension.

Your backend (Express)

Verify the OTP, then mint the custom token in the same handler. result from otp.verify() carries no phone field — use the phone number you already have (the one the client sent to be verified) to build the Firebase uid.

import express from 'express'
import { MiniMoth } from '@minimoth/sdk-node'
import { getAuth } from 'firebase-admin/auth'

const mm = new MiniMoth({ apiKey: process.env.MINIMOTH_API_KEY })
const app = express()
app.use(express.json())

app.post('/auth/send', async (req, res) => {
  const { phone } = req.body
  const { otpId } = await mm.otp.send({ phone })
  res.json({ otpId })
})

app.post('/auth/verify', async (req, res) => {
  const { phone, otp } = req.body
  const result = await mm.otp.verify({ phone, otp })

  if (!result.valid) return res.status(400).json({ error: result.code })

  // result has no phone field — use the phone you already have (the one
  // you called otp.verify with) to build a stable, tenant-defined uid.
  const uid = `phone:${phone}` // e.g. "phone:+919876543210"
  const customToken = await getAuth().createCustomToken(uid, { phone_number: phone })

  res.json({ customToken })
})

Your client (web)

import { getAuth, signInWithCustomToken } from 'firebase/auth'

const { customToken } = await fetch('/auth/verify', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ phone, otp }),
}).then(r => r.json())

await signInWithCustomToken(getAuth(), customToken)

The uid convention is entirely yours

MiniMoth has no opinion on how you derive a Firebase uid from a phone number. MiniMoth's phone field is always +91XXXXXXXXXX — build your uid from that consistently, so the same user doesn't fork into two Firebase records (e.g. +919876543210 vs 9876543210). Changing the convention later orphans existing Firebase user records — there's no MiniMoth-side migration for this.

Try the full OTP flow interactively in the sandbox — no SMS sent, no credits consumed. The same Playground also has a live test to send a real OTP to your phone (uses your credits).

Test in Playground →