@1auth/messenger
Messaging framework for contact method verification (email, phone, etc.).
Install
npm i @1auth/messenger Usage
import messenger from '@1auth/messenger'
messenger({
store,
notify,
encryptedFields: ['value']
}) Configuration options
| Option | Type | Default | Description |
|---|---|---|---|
store | object | required | Storage backend module |
notify | object | required | Notification module |
table | object | — | Table schema definition |
encryptedFields | string[] | ["value"] | Fields to encrypt |
idGenerate | object | — | ID generation config |
randomId | object | — | Random ID options (prefix: messenger_) |
notifyId | string | 'messenger' | Prefix for the notify template ids: {notifyId}-{type}-exists, -verify, -create, -remove-self, -remove |
The verification flow
A messenger is only trusted once the owner proves they receive on it. That proof is a short-lived one-time token delivered through the messenger being claimed — the address verifies itself.
User Your app @1auth/messenger @1auth/notify
│ │ │ │
│ add address │ │ │
├────────────────────►│ │ │
│ │ create(type, sub, │ │
│ │ { value, │ │
│ │ digest }) │ │
│ ├──────────────────────►│ │
│ │ digest lookup: │ │
│ │ already taken │ │
│ │ by someone │ │
│ │ else and │ │
│ │ verified? tell │ │
│ │ THEM, not the │ │
│ │ caller │ │
│ │ │ │
│ │ row stored, │ │
│ │ verify is NULL │ │
│ │ │ -verify template │
│ │ ├─────────────────────►│
│ token, 6 digits, 10min, sent to the address being claimed │
│◄───────────────────────────────────────────────────────────────────┤
│ │ id │ │
│◄────────────────────┤◄──────────────────────┤ │
│ │ │ │
│ enter token │ │ │
├────────────────────►│ │ │
│ │ verifyToken(type, │ │
│ │ sub, token, id) │ │
│ ├──────────────────────►│ │
│ │ token is otp: │ │
│ │ consumed, then │ │
│ │ verify is set │ │
│ │ │ -create template, │
│ │ │ to the ALREADY │
│ │ │ verified messengers │
│ │ ├─────────────────────►│
│ │ ok │ │
│◄────────────────────┤◄──────────────────────┤ │ Two details worth noticing, because both are deliberate:
A taken address notifies its owner, not the caller. If the digest already belongs to a
different, verified subject, create returns nothing and sends the -exists template to the
existing owner. The caller cannot tell “taken” from “created”, so the API does not answer the
question is this address registered here — which is the question an enumeration attack asks.
Unverified claims don’t block, the first verified one wins. Another account can claim an
address that is still unverified, so someone typing your address first can’t lock you out of it.
When one account verifies, every other account’s unverified claim on that type and value is
removed along with its token. Only the verified owner resolves through exists and lookup. On
SQL stores a partial unique index on (type, digest) allows one verified owner. DynamoDB can’t
enforce that, so two owners verifying in the same instant would both stand.
createToken deletes previous tokens first. Only the newest token for a messenger can be
redeemed, so requesting a second one invalidates the first rather than widening the window.
API
randomId(options)
Generate a random messenger ID.
token(options)
Token configuration: 6-digit numeric OTP with 10 minute expiry by default, the out-of-band minimum in NIST SP 800-63B-4 Sec. 3.1.3. That minimum assumes the verifier limits failed attempts: @1auth/authn removes the token after otpMaxAttempts wrong guesses. Rate limit requests in your app as well.
exists(type, value)
Check if a verified messenger of the given type and value exists.
Returns: the owner’s sub, or undefined
count(type, sub)
Count verified messengers of a type for a subject.
lookup(type, value)
Find the verified messenger of a type and value. Unverified claims are ignored.
list(type, sub)
List all messengers of a type for a subject.
select(type, sub, id)
Get a specific messenger.
create(type, sub, values)
Create a new messenger with a verification token. Triggers a notification with the token.
createToken(type, sub, sourceId)
Generate a new verification token for an existing messenger.
verifyToken(type, sub, token, sourceId)
Verify a token and mark the messenger as verified.
remove(type, sub, id)
Delete a messenger.
Post-quantum
Uses only SHA3-384 seasoned digests, symmetric field encryption, Argon2id hashing, and CSPRNG one-time codes, all post-quantum safe. No migration needed. See the full assessment in Post-quantum.