Skip to content

Run it locally

The whole provider, on your machine, over a local D1 database. Same code as https://auth.omxus.com. Checked on 30 Oct 2026 with Wrangler 4.152 and Node 26: all migrations applied, discovery and JWKS answered, a client registered, the runtime smoke test passed 4 of 4.

You only need this to work on the provider itself. To add sign-in to your site, register it on auth.omxus.com instead.

1. Install

cd auth
npm install

2. Make .dev.vars

wrangler dev reads local secrets and settings from .dev.vars in the repo root. It is gitignored. Mint fresh secrets:

npm run worker:keys

That prints three wrangler secret put lines, each followed by its value. It is not a .dev.vars file: copy the values into one by hand, like this.

OIDC_SIGNING_JWK={"kty":"EC","crv":"P-256",...}
PAIRWISE_SALT=3f9c...
ISSUER=http://localhost:8787
RP_ID=localhost
INSECURE_COOKIES=1
Setting Why
OIDC_SIGNING_JWK Signs every ID token. A local key, never the production one.
PAIRWISE_SALT Behind every sub. Change it and every sub changes.
ISSUER wrangler.toml sets https://auth.omxus.com. Locally it must be the address you run on, port included, or every redirect, token and discovery answer points at production.
RP_ID Overrides auth.omxus.com from wrangler.toml.
INSECURE_COOKIES=1 Session cookies are Secure by default and never arrive over plain http.

Optional: RECOVERY_JWK (the third printed value) turns on the email and password door; without it /email/* answers 404. ACCOUNTS_LOG_CODES=1 prints sign-up codes to the console when no email binding can deliver them. See Email and texts.

3. Make the database

npm run worker:db:local

This applies 20 migration files in order to a local D1 in .wrangler/state/ and ends with local D1 ready: 41 tables. It is safe to run twice; it skips any column already there. To start clean, delete .wrangler/state/ and run it again.

4. Start it

npm run worker:dev        # http://127.0.0.1:8787

Check it:

curl -s localhost:8787/.well-known/openid-configuration | head
curl -s -o /dev/null -w '%{http_code}\n' localhost:8787/oidc/jwks   # 200

The issuer in the first answer must read http://localhost:8787. If it reads https://auth.omxus.com, ISSUER is missing from .dev.vars.

The designed sign-in page comes from frontend/dist. Build it once with cd frontend && npm run build; without it the plain page is served.

5. Register a client

The same call as production, against your own provider:

curl -s -X POST localhost:8787/oidc/register \
  -H 'content-type: application/json' \
  -d '{"client_name":"My Site",
       "redirect_uris":["http://localhost:3000/callback"],
       "token_endpoint_auth_method":"none"}'

The answer holds your client_id. The form at http://localhost:8787/developers does the same. Fields and rules: Registering a site.

The smoke test

worker/smoke.test.mjs signs in against a running Worker, takes a code, verifies the ID token against the Worker's JWKS and checks sub is pairwise. It is the one test that sees the Workers runtime, so it is not in npm test.

It expects the provider on port 8789 and a client called smoke-test, so run a second copy for it:

npx wrangler dev --port 8789 --ip 127.0.0.1 \
  --var ISSUER:http://localhost:8789 --var INSECURE_COOKIES:1
npx wrangler d1 execute omxus-auth-v2 --local --command \
  "INSERT OR REPLACE INTO oidc_clients
     (client_id, name, redirect_uris, secret_hash, sector_identifier)
   VALUES ('smoke-test','Smoke Test','[\"https://example.test/cb\"]',NULL,NULL)"
npm run worker:smoke      # 4/4 passed

--var overrides .dev.vars for that one run, so both copies share the signing key and the database.

When it will not start

Address already in use (127.0.0.1:8787): something holds the port. Wrangler dev servers outlive the terminal that started them:

lsof -nP -iTCP:8787 -sTCP:LISTEN

Pick another port only if you change ISSUER to match.

Deploying your own

Running a provider covers what a real deployment needs. In short: create your own D1 and put its id, your account and your route in wrangler.toml; set ISSUER there to your https address; run npm run worker:db:remote; wrangler secret put each printed secret (new ones, not the local ones); cd frontend && npm run build; then npm run worker:deploy.

Costs

Free tier covers it: Workers 100,000 requests a day, D1 5 million row reads and 100,000 writes a day, static assets not billed. Roughly 30,000 sign-ins a day before anything costs money.