This site is being rebuilt and some pages are out of date. For current details, write to reach@inorbit.hr. This notice goes away when the rebuild is done.

No analytics unless you allow it, no tracking. This site keeps in your browser the language you pick, the theme, its colour, which site you chose, the currency on the pricing page and that you closed this notice; signing in adds session cookies. The legal page has the details.

Sign in

← Back to Platform

RFC 0049decided2026-10-04

Sign-in that recovers by itself

Every way a sign-in can end up stuck (a spent passkey challenge, a linking form that offered the wrong account's passkey, a second tab, an expired or foreign form, an error page that printed the identity server's own words) now starts the same sign-in again to the same place, or says in one plain sentence what to do.

Problem

The sign-in pages run on and , rendered with Elements. On 2026-10-04 a person with two accounts could not sign in. The chain, read from the identity server's log and its source:

  1. A sign-in with Google returned an email that belonged to an existing account. turned the form into a linking form: sign in to that account once, and Google becomes one more way into it.
  2. The linking form showed every method, the passkey included, and the browser offered the passkey on the email field. The passkey belonged to the person's other account. checked it, found that it did not sign in the account being linked, and refused with "linked credentials do not match; please start a new flow".
  3. had already used the form's one WebAuthn challenge and removed it. The page showed the same form again, the person tried the passkey again, and the identity server answered with a 500: "Expected WebAuthN in internal context to be an object". The error page printed that sentence.

Looking for the rest of the edges turned up more of the same kind:

  • A second tab. A form opened before the person signed in elsewhere answered its submit with ' "A valid session was detected and thus login is not possible. Did you forget to set ?refresh=true?", a sentence for developers, and stopped there.
  • Restarts lost the destination. An expired form, a form from another browser or one had replaced started again at the bare sign-in endpoint. The address to go back to and the OAuth2 login challenge stayed behind, so a person who came from a gated host landed in the console instead.
  • The error page showed raw words. "unknown error", ' reasons, 's error descriptions, all in English.

Proposal

The sign-in pages sort what and hand back by their stable ids, never by showing the words:

  • A spent form starts again. "Linked credentials do not match", or a passkey that accepted and a later step refused, means the form cannot finish. The page starts a new one to the same place and says why in one line above it.
  • A linking form offers no passkey. A passkey and a security key choose their own account from the browser's list, so on a linking form they can sign in a different person than the one being linked. The page drops both, the script that offers a passkey on the email field included, and keeps the password and the other providers, which checks against the owning account. A "Not your account? Start again" link leaves the linking for a plain sign-in. ' login hints, which would name the owning account's methods, stay off: they tell anyone who types an email which ways into that account exist.
  • A signed-in person goes on. When a sign-in fails, the page first asks whether a session exists. If it does, an OAuth2 sign-in is accepted at as that person and anything else goes to its return address. A step-up to a second factor and a re-authentication keep their errors, because they run with a session on purpose.
  • Restarts keep the destination. Each tab remembers where its sign-in was going: the return address and the login challenge, which keeps for thirty minutes. Every restart, ours and Elements', goes through that memory. Two restarts within two minutes drop it and start clean through the console, so a bad challenge cannot loop.
  • The error page explains in the reader's language. Each known error (expired, from another browser, a spent passkey, already signed in, signed in as someone else, a second factor needed, a fresh sign-in needed, a return address we do not send people to, a method switched off, access refused, a broken link, a fault on our side) has a sentence in English and Croatian and one button. The page shows the first part of the error's id as a reference a person can quote; the identity server keeps the rest in its log.

The rules are pure functions with tests; a browser check runs the edges against the live identity stack, and can run a new build of the pages against it before a roll.

A diagram is drawn here in the RFCs product; this page does not show diagrams yet.

Alternatives considered

  • Patch so a refused passkey gets a new challenge. The fault is upstream, and worth reporting there, but the pages must not depend on a fork of the identity server.
  • Switch ' login hints on. The linking form would name exactly the owning account's methods and could keep a passkey for an account that has one. The cost is that a sign-up with a taken email learns which methods that account uses. Refused: deny by default. An account whose only method is a passkey links a provider from the console's security page instead.
  • Leave restarts to Elements. Its restart is correct for a single page and loses the OAuth2 challenge every time, which is the case this platform has most.

Decision

The pages recover from every edge listed above by starting the same sign-in again to the same place, or by saying in one sentence what the person can do. The identity server's configuration does not change and no control is weakened: login hints stay off, and the linking form drops the passkey instead.

Publication

The sign-in pages carry the rules and the new error page; the identity server's configuration is unchanged. The browser check of the edges joins the other identity checks, and can run a new build of the pages against the live identity stack before it is rolled.

Status log

  • 2026-10-04: opened after the incident above. Measured before, against the live stack with throwaway accounts: a second tab's submit stopped on ' developer sentence; a form from another browser restarted without its return address; the error page for an unknown error showed "unknown error" and the raw id.
  • 2026-10-07: Decided: built and live. The recovering sign-in pages (#239) run in auth-ui at e0c09030.

← Back to Platform