Skip to main content
Magic codes are the simplest sign-in flow Pylon ships. The user types an email, gets a numeric code (6 digits by default), and types it back to sign in. There are no passwords to remember and no OAuth credentials to obtain. Most apps should start here.

How it works

If the user does not exist when the code is verified, Pylon creates a User row with that email and stamps emailVerified. Typing the code proves control of the address.

Send a code

Response:
codeLength is the number of digits in the code (see Code length). Use it to size the code input. In dev mode (PYLON_DEV_MODE=true), a request from the same machine also gets dev_code in the response, so you can sign in without an email provider configured. Requests from other devices don’t; the code prints to the pylon dev terminal instead.

Verify a code

Response:
Store the token and pass it as Authorization: Bearer pylon_a1b2c3... on subsequent requests.

From the SDKs

Built-in security

  • Constant-time code comparison — no timing leak.
  • 5-attempt cap per code — burned after 5 wrong tries; even a correct subsequent attempt returns RATE_LIMITED.
  • 10-minute expiry — codes are short-lived.
  • 60-second send cooldown per email — clients can’t flood a user with codes. Returns 429 RATE_LIMITED with retry_after_secs.
  • Single-use — verifying a code consumes it.
  • CSPRNG-generated — codes are uniform random over every code of the configured length, zero-padded (0427 is a valid 4-digit code).
  • Daily wrong-guess limit per email — see Wrong-guess limit.

Code length

PYLON_AUTH_CODE_LENGTH sets the number of digits in every one-time code Pylon sends: magic codes, email verification codes, and phone codes. The value is a whole number from 4 to 8. The default is 6.
Any other value (3, 9, six) stops the server at boot with an error that names the variable. The send responses return the length as codeLength.

Wrong-guess limit

Each code burns after 5 wrong tries, and a new code can be requested every 60 seconds. Without a limit across codes, an attacker could make thousands of guesses a day against one email. Pylon counts wrong guesses per email (and per phone number) over a rolling 24 hours. When the count reaches the limit, Pylon refuses new codes and verifies for that email until the oldest counted wrong guess is more than 24 hours old. The limit scales with the code length, so an attacker’s daily chance of guessing a code stays near 0.1%: A correct code clears the count for that email. The count is stored in the auth database (_pylon_code_failures in SQLite or Postgres), so a restart or deploy does not reset it. While an email is locked, /magic/send, /magic/verify, /email/send-verification, and /email/verify return:
with status 429. A locked email refuses even the correct code.

Configuring email delivery

Magic codes need an email provider configured, otherwise they only work in dev mode. Pylon supports:
On Stack0 Cloud, magic-link email is included. You need no provider config for the built-in sender. Configure your own domain in Settings → Email for production to keep deliverability high. If you set PYLON_EMAIL_PROVIDER=webhook, also set PYLON_EMAIL_ENDPOINT to your custom URL. Pylon POSTs { to, from, subject, body } to it. To give auth email its own key and from-address, separate from your app’s ctx.email, set the PYLON_AUTH_EMAIL_* family (_PROVIDER, _API_KEY, _FROM, _ENDPOINT). It overrides PYLON_EMAIL_* for auth flows only. See Auth → Configuration. See Stack0, Resend, SendGrid for transactional sending services.

Customizing the email

The default subject is “Your sign-in code” and the body is plain text:
To change the copy, set PYLON_EMAIL_TEMPLATE_MAGIC_CODE_SUBJECT and PYLON_EMAIL_TEMPLATE_MAGIC_CODE_BODY. The template can use {{code}} and {{app_name}} (from PYLON_EMAIL_APP_NAME). Any other {{...}} is removed.
The plugin system also supports replacing the email transport. See Plugins → Integrations.

Error responses

Testing

In dev mode, skip the email entirely:
For integration tests, this is the fastest sign-in path — no SMTP mock needed.

When to choose magic codes vs alternatives

Most apps should start with magic codes, then add OAuth for one-click sign-in once they have users. Password is the third option, useful when email is not available (for example a shared device with no email access).