/technology/modules/ · auth-magic-link Shipping

auth-magic-link

Sign in by email. A bearer credential sent over a channel this service does not control, and everything here follows from that.

What it is

auth-magic-link.

A link arrives in an inbox and signs somebody in. That is a bearer credential travelling over a channel this service does not control and cannot audit, so: thirty-two random bytes, stored only as a SHA-256, valid for fifteen minutes by default — TTL_SECS is a deployment's to set — and spent exactly once — single-use under concurrency, by a guarded update whose affected-row count is checked, rather than single-use by convention.

It is also the answer to things the other methods leave undone. Password registration creates an account before anyone has proved they can read the address; a magic link is what proves it. And a person who has forgotten a password does not need a second flow to reset one — they need a way in.

Crate

factory0-auth-magic-link · in the repo, not yet on crates.io. MIT.

Ports

Database · Clock · IdGen · Mailer · RateLimiter and Captcha optional

Routes

  • POST /v1/auth-magic-link/request — always 202, always the same body
  • GET /v1/auth-magic-link/consume?token=… — the URL in the mail. Signs in when the request looks like a person clicking, and shows a confirm button when it does not
  • POST /v1/auth-magic-link/consume — the confirm button

Why a confirm button

Mail clients follow links to scan them, and a GET that always signed somebody in would be spent by a scanner before the person read the message. A click carries Sec-Fetch-Mode: navigate and Sec-Fetch-Dest: document; anything else gets the button. It does not catch a scanner that copies a browser's headers — what it must not do is refuse a real person, so a request with no fetch metadata at all gets the button rather than a refusal.

The token never touches the database in a form that could be replayed from a dump.