npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@activescott/auth-provider-sms

v0.1.2

Published

SMS one-time-code provider for @activescott/auth

Readme

@activescott/auth-provider-sms

npm version License: MIT

SMS one-time-code provider for @activescott/auth. The user enters their mobile number, gets texted a 6-digit code, and types (or autofills) it to sign in.

This package has no vendor dependencies — message delivery is injected via the SmsTransport interface. Use a vendor package or write your own:

  • @activescott/auth-sms-twilio — Twilio (SMS, or RCS via a Messaging Service)
  • ConsoleTransport (included) — prints codes to the server console for development
  • Custom: implement SmsTransport { sendMessage(to, message): Promise<boolean> }. An AWS End User Messaging transport is drafted in PR #37 — implemented and unit-tested but unverified against a live AWS account; if you want AWS and can test it end to end, feel free to take over that PR (#36 has the checklist).

Usage

import { Auth, InMemoryChallengeStore } from "@activescott/auth"
import { SmsProvider, ConsoleTransport } from "@activescott/auth-provider-sms"
import { TwilioTransport } from "@activescott/auth-sms-twilio"

const auth = new Auth({
  session: { secret: process.env.JWT_SECRET! /* ... */ },
  userStore,
  identityStore,
  challengeStore: new InMemoryChallengeStore(), // DB-backed in production
  providers: [
    new SmsProvider(
      { appName: "MyApp", webOtpDomain: "myapp.example" },
      process.env.NODE_ENV === "production"
        ? new TwilioTransport({
            accountSid: process.env.TWILIO_ACCOUNT_SID!,
            authToken: process.env.TWILIO_AUTH_TOKEN!,
            messagingServiceSid: process.env.TWILIO_MESSAGING_SERVICE_SID,
          })
        : new ConsoleTransport(),
    ),
  ],
})

The login form posts the phone number to /auth/sms/initiate (the provider texts the code, sets an HttpOnly challenge cookie, and redirects back with ?sent=1); the code form posts to /auth/sms/verify. A runnable app demonstrating the full flow is at examples/react-router:

<form method="post" action="/auth/sms/initiate">
  <input name="phone" type="tel" autocomplete="tel" required />
  <button>Text me a code</button>
</form>

<form method="post" action="/auth/sms/verify">
  <input
    name="code"
    autocomplete="one-time-code"
    inputmode="numeric"
    pattern="[0-9]{6}"
    required
  />
  <button>Sign in</button>
</form>

Configuration

| Option | Default | Description | | ----------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | appName | "App" | Shown in the default message text | | expiry | "10m" | Code lifetime ("30s", "10m", "1h", ...) | | webOtpDomain | (off) | Appends the WebOTP line @domain #code for Android/Chrome one-tap autofill | | messageTemplate | built-in | (code, appName) => string to customize the text | | otp.length | 6 | Digits in the code | | otp.maxAttempts | 5 | Wrong guesses before the challenge is invalidated (RATE_LIMITED) | | otp.cookieName | "auth_sms_challenge" | Challenge cookie name |

The default message (with webOtpDomain set) looks like:

Your MyApp sign-in code is: 123456

@myapp.example #123456

The last line is the origin-bound one-time code format (a WICG spec co-edited by Apple and Google — what WebOTP and Safari's code autofill parse). The human-readable sentence matters too: Apple publishes no exact grammar for iOS Security Code AutoFill, which recognizes codes heuristically from the message text (see autocomplete="one-time-code" on MDN) — Your <app> sign-in code is: <code> follows the widely observed patterns. Custom templates should keep a similar shape for autofill to work.

Phone numbers

Input is normalized (spaces, dashes, dots, parentheses stripped; leading 00+) and must then be valid E.164 (+ and country code required) — the provider never guesses a default country. normalizePhoneNumber is exported if you want the same normalization client-side.

Security model

Same challenge model as the email provider: one server-side challenge per send, code stored only as a salted SHA-256 hash, attempts counted before each comparison (capped at maxAttempts), constant-time compare, single-use (deleted on success), and the challenge is bound to the initiating browser by an HttpOnly cookie — a code alone is useless without it.

Delivery-level protections (per-number send throttling, fraud/pumping protection) belong at the app and vendor level; both Twilio and AWS offer built-in fraud guards.

If you think any of this should work differently — protections that belong in this library, a weakness in the model — please open an issue or PR to discuss.