---
title: "EAuth moves to eauth.me, and every door gets counted"
summary: "EAuth has its own domain, eauth.me, and auth.elchi.dev redirects there. With the move came limits on every sign-in, checked forms and confirmed addresses."
author: "Samuel Krauss"
author_title: "Founder"
publisher: "Elchi Studios"
published: 2026-10-03
updated: 2026-10-03
url: https://elchi.dev/en/journal/eauth-moves-to-eauth-me
language: en
tags: ["eauth","security"]
words: 1458
---

# EAuth moves to eauth.me, and every door gets counted

*By Samuel Krauss, Founder. Published 3 October 2026.*

> EAuth has its own domain, eauth.me, and auth.elchi.dev redirects there. With the move came limits on every sign-in, checked forms and confirmed addresses.

EAuth has a domain of its own now: **eauth.me**. The issuer is `https://eauth.me`, and sign-in and the account page live on the apex. Since 3 October 2026, auth.elchi.dev answers every request with a permanent `308` redirect to the same path on eauth.me. The move was the reason to go through everything a person touches on the way in: every door is counted now, forms prove where they came from, signing out needs proof, and no application gets an address nobody has confirmed.

## Why EAuth moved

EAuth started as the sign-in for our own console and dashboard, so elchi.dev was the obvious home. An address under an agency's website says "a feature of that website". EAuth is a product other developers can build on, and it gets an address that says so.

There is a security reason as well. Browsers send cookies by site, and a site is the registrable domain, not the hostname. On auth.elchi.dev, every page on every elchi.dev subdomain was the same site as the page that holds everybody's sign-in session. None of them misbehaved, but that is a wider circle of trust than a sign-in service should have.

And it had to happen before passkeys and single sign-on. A passkey belongs to one domain, and the browser offers it nowhere else; an organisation's identity provider is set up once with EAuth's entity ID and reply address. Both live on eauth.me from the start, so neither will have to be made again because of an address.

What it costs: one more sign-in for everybody, since sessions belonged to the old host. And every integration has to name the new issuer. A client compares the issuer as a string, tokens from eauth.me say `https://eauth.me`, and a library that checks it, as it should, refuses them until its setting matches.

## No overlap, and why there did not need to be one

A changed issuer breaks integrations, and our terms promise notice before a change like that. The plan was to keep auth.elchi.dev as a complete issuer for ninety days. We did not need it: on the day of the move, every application registered with EAuth belonged to Elchi Studios, and nobody outside the company had an account. So the old address redirects from the day of the move, and the terms say so in version 1.7, clause 2.4.

The redirect is a `308` rather than a `301`, so a token request sent to the old address stays a `POST` and arrives intact. The tokens it gets name `https://eauth.me`. For our own console and dashboard that was one setting, since every surface reads the issuer from the same value.

I did it now because now it was cheap. With a few hundred outside integrations it would be a migration project with a notice period.

## Every door is counted

Until this release the API behind elchi.dev had rate limits and the pages where people type a password did not. The arithmetic that made it urgent: a second factor is a six-digit code, and at any moment three codes are accepted, because clocks drift. With the password, a script gets five minutes for the code and, without a limit, thousands of tries. That is not a second factor, it is a delay.

Now every place that takes a credential is counted twice: by the address a request comes from, and by the account it names.

| Door | Limit |
|---|---|
| Sign-in attempts from one address, any method | 30 a minute |
| Wrong passwords, one account from one address | 5 in 15 minutes |
| Wrong passwords, one account from anywhere | 20 in 15 minutes |
| Wrong sign-ins from one address | 50 an hour |
| Wrong second-factor codes, one account | 5 in 15 minutes, 20 a day |
| New accounts from one address | 10 an hour |
| `/authorize`, per address | 60 a minute |
| `/token`, per application | 120 a minute by default, up to 600 in the console |

With twenty wrong codes a day, somebody who has the password and nothing else needs on average about 45 years to guess the code.

Four details matter more than the numbers.

**Every attempt is counted before it is checked.** The obvious lock checks the lock, then the password, and counts a failure afterwards. Requests sent at the same moment all pass the first step, so a script firing a hundred guesses at once got a hundred guesses. A review of the first version found exactly that. Now each attempt takes its place in the count in one atomic step, before anything is checked, and gets it back only if it was right.

**The limits fail closed.** If the store that counts them does not answer, sign-in refuses and says so, instead of letting everything through. The one exception is a loose limit of 600 requests a minute per address on the rest of the host, discovery and keys among them: nothing to guess there, so an outage of the counter should not take it down.

**A lock cannot be used against the owner.** Locking an account after a few wrong passwords lets anybody keep its owner out: five wrong guesses every quarter of an hour, forever. So a browser that has completed a sign-in to an account carries a cookie for 90 days and keeps a small allowance of its own while the account is locked for everybody else. Someone guessing can make the account slow for strangers, not for its owner.

**IPv6 is counted by its /64**, because one connection can pick any source address in it.

The token endpoint counts an application's requests only after the client has authenticated, or anybody could use up someone else's allowance. A browser or mobile app has no secret, so there the allowance is counted per application and address: a stranger spending it spends only their own.

## Forms prove where they came from, and so does signing out

The consent screen and the account page relied on the browser's SameSite rules, a default with a history of exceptions, to keep other sites from posting to them. Every form there now carries a token bound to the session and has to be posted from eauth.me itself.

Signing out used to happen on any `GET /logout`, so any page could sign anybody out with a link: the first step of getting somebody to sign in again on a page of your choosing. Sign-out now follows OpenID Connect RP-Initiated Logout and is listed in discovery as `end_session_endpoint`. The session ends when the application sends an `id_token_hint` that names the person signed in and was issued during this session. Without it, EAuth asks first.

## No application gets an unconfirmed address

`email_verified` was false in every ID token EAuth issued before this release. True, since nothing had ever checked an address, and useless. Registration now sends a link that works once, for 24 hours. Opening it confirms the address.

The stricter part is new on 3 October: no application gets an account whose address is not confirmed. Somebody who registers is stopped on the way back to the application by a page that sends a new link, and the link leads back to where the sign-in stopped. With `prompt=none` the application gets `interaction_required` instead. So in a token from a sign-in, `email_verified` is always true. Our own console is held to the same rule.

An unconfirmed address is a string anyone could type, and signed in under it a stranger would receive invitations meant for that mailbox. A password reset, single sign-on through an organisation's identity provider, or an import marked as confirmed also count; any other imported account is asked at its first sign-in. The cost is one more step for somebody trying an application for the first time. I think it is worth it.

The same mechanism brings password reset, which until now meant writing to us. Asking says the same thing whether an account exists or not, and a link works once, for an hour, and only the newest one. Opening it shows a form and spends nothing, because mail scanners open links too. Setting the new password signs out every device and every application.

The links are 256 random bits, stored only as digests, like every other credential here. A copy of the database cannot confirm or reset anything.

## What is not done

Passkeys work beside passwords, and an organisation can have its members sign in through its own identity provider with SAML. What is still missing: the mails EAuth sends are in English only. There has been no independent security audit; the review mentioned above was our own. Measured availability goes on status.elchi.dev once a full month has been measured, not before. Sign-in with the Swiss e-ID is in preparation, and nothing an application can use yet.

Anybody building on EAuth names one thing by hand: the issuer, `https://eauth.me`. Everything else comes from discovery.

## Sources

1. [RFC 9700: Best Current Practice for OAuth 2.0 Security](https://www.rfc-editor.org/rfc/rfc9700), IETF, read 2026-10-03
2. [OpenID Connect RP-Initiated Logout 1.0](https://openid.net/specs/openid-connect-rpinitiated-1_0.html), OpenID Foundation, read 2026-10-03
3. [OpenID Connect Core 1.0, Standard Claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims), OpenID Foundation, read 2026-10-03
4. [RFC 6238: TOTP, Time-Based One-Time Password Algorithm](https://www.rfc-editor.org/rfc/rfc6238), IETF, read 2026-10-03
5. [RFC 9110: HTTP Semantics, 308 Permanent Redirect](https://www.rfc-editor.org/rfc/rfc9110.html#name-308-permanent-redirect), IETF, read 2026-10-03
6. [Web Authentication: An API for accessing Public Key Credentials, Level 3](https://www.w3.org/TR/webauthn-3/), W3C, read 2026-10-03
7. [Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html), OWASP, read 2026-10-03
8. [Site (glossary)](https://developer.mozilla.org/en-US/docs/Glossary/Site), MDN, read 2026-10-03
