TILLAUTH · CHANGELOG

TillAuth, in order.

What we shipped on the auth side of TillDev. Newest first. Material breaking changes are flagged in bold — these can require code changes on your side.

October 2026 · Data policy for webhooks

  • Webhook payloads follow the workspace data policy. Credentials are always masked; contact details, payment data, government ID numbers and IP addresses are kept by default and can be masked from the Data policy page. Data policy.

October 2026 · SSO session

  • Sign in once per browser. Signing in on the hosted pages starts an SSO session, kept in an HttpOnly cookie page script can't read. The next client of the same app that sends the browser to TillAuth signs the user straight in, with a session of its own. SSO session.
  • It can't outlive the sign-in. It ends after 7 idle days or 30 days from the real sign-in, whichever comes first, and as soon as the sign-in behind it ends: sign-out, a password change, ended sessions, SCIM deactivation, theft detection or session risk. Using it never extends it. Every reuse is scored from the browser's own network, so under enforce an unusual one gets the full sign-in page.
  • Freshness holds. auth_time is when the user really signed in, so max_age and prompt=login still ask for a fresh sign-in. prompt=none answers login_required, interaction_required or consent_required instead of showing a page.
  • Logout signs out everywhere on that browser. The end_session_endpoint is now https://tilldev.dev/oidc/logout; the old address redirects there. With an id_token_hint it signs out without asking, and a hint for another user never signs anyone out by itself.
  • On by default. Turn it off or change the idle and absolute limits (1 to 2160 hours) on the Policies page, with tilldev auth apps update --sso-session off|on --sso-idle-hours N --sso-absolute-hours N, or settings.sso_session. The Policies page counts live SSO sessions and their sign-ins; sso.session.used, sso.session.refused and sso.session.ended can be sent to webhooks.

October 2026 · Risky sign-ins

  • Sign-ins are scored too. The session-risk policy now judges each sign-in against where the user has signed in over the last 90 days: a browser, country or network none of those sessions had, impossible travel from the last one, and addresses TillGate lists. At sign-in.
  • One more proof when it looks wrong. On enforce, a sign-in at reauth_at owes the user's authenticator code or push approval, or, with neither, a 6-digit code we email: method: "email", then POST /v1/signin/email-code. At revoke_at it is refused with code: signin_blocked and the user is emailed a password reset link. Passkey sign-ins are never blocked, and a magic link counts as its own proof.
  • The hosted pages, @tillstack/auth-react 0.3 (useMfa().verifyEmailCode) and Till Authenticator handle both. The two emails are editable on the Emails page, the Policies page counts extra steps and blocks over 7 days, and signin.risk, signin.risk_step, signin.risk_blocked and signin.email_code.ok can be sent to webhooks.
  • Your own sign-in form must handle method: "email" and signin_blocked before you turn enforce on; monitor, the default, never stops anyone.

October 2026 · Groups

  • Groups from your directory. SCIM now provisions Groups as well as users, in the shapes Entra ID, Okta and OneLogin send: create, rename, add and remove members, replace, delete, and the lookups they make by name or member. Directory groups stay read-only in TillAuth. Provisioning.
  • Groups made by hand in a new Groups page, with tilldev auth groups and the admin API. Adding people, renaming and deleting ask for approval when your workspace requires it. Groups.
  • A groups claim. Ask for the groups scope and the id_token and userinfo list the user's groups, up to 100 with groups_overage when there are more. The signed-in user always lists them.
  • Internal apps can let a group in with require.groups, and the assertion your app verifies lists the person's groups. tilldev auth access create --allow-group starts from a group. Rules.

October 2026 · Internal apps

  • Put self-hosted tools behind your app's sign-in. The proxy you already run asks TillAuth about every request: nginx, Caddy, Traefik, Envoy, a Cloudflare Worker, or tilldev auth access proxy when there is none. The dashboard and tilldev auth access setup write the configuration, without the key in it. Internal apps.
  • Rules per path and method, read top to bottom: open, refused, or allowed for named people, domains or roles, with a second factor, a sign-in within the last few minutes, a session-risk ceiling, and networks or countries. Anything no rule matches is refused.
  • An identity the app can check. Allowed requests carry the person's user id, email, role and second-factor state, and a short-lived EdDSA assertion signed for that host and internal app. @tillstack/auth-node 0.5 adds createAccessVerifier.
  • Each request is compared with where its session signed in, under the app's session-risk policy. Revoking someone's sessions or removing them ends their access too.
  • Keys rotate with a 24-hour overlap. A decision log shows who was allowed, asked to sign in or refused, by which rule. access.signed_in and access.signed_out can be sent to webhooks.

October 2026 · Sessions that keep checking

  • Session risk. Every refresh is scored against the session's own history: a different browser or system, a new country, impossible travel, a different network and addresses TillGate lists. monitor, the default, records the score and logs refreshes at 30 or more; enforce asks for a fresh sign-in, with the second factor, or ends every session of the user. The refresh answers 401 with code: reauth_required or risk_revoked, and @tillstack/auth-react 0.3 shows it as state.reason. Session risk.
  • The Sessions pages show each session's country and risk, with a Risky filter, and tilldev auth sessions lists, filters and ends them from a terminal. session.risk, session.risk_reauth and session.risk_revoked can be sent to webhooks.
  • Ending a session now stops its access tokens at their next check wherever it is ended: the dashboard, the CLI, a TillShield rule or playbook. Before, only sign-outs and SCIM did; the rest waited out the token's 10 minutes. Revoking a session from the dashboard also ends the whole session, not only its latest token.
  • Your back office can sign a user out everywhere with the app's provisioning token: DELETE /admin/apps/<public id>/users/<user id>/sessions. Revoke on someone's behalf.
  • Fixed: the address TillAuth rate-limited and recorded by could be set by the client. It now comes from the edge network in front of TillAuth only when the request really came through it, and from the connection otherwise.

October 2026 · Sessions and second factors, tightened

  • Refresh tokens are now recognisable. New ones start with tau_rt_, so a token committed by mistake is caught by secret scanners, including TillForge's at push and in scans of a commit's files. Tokens issued before keep working. Refresh tokens.
  • Fixed: a refreshed session could claim MFA it never did. The mfa claim used to come from the user's last MFA anywhere, so a session that never stepped up became mfa: true on its first refresh, and the same was true of OIDC sign-ins. It now comes from the session itself and carries across refreshes. Refresh tokens.
  • Fixed: two refreshes at once could split a session in two without tripping theft detection. Only one request can now rotate a token. A second tab in the same browser that refreshes within 15 seconds gets a fresh pair instead of being signed out; any other replay still ends the whole session.
  • Fixed: magic links, social sign-in and enterprise SSO skipped Till Authenticator push. They asked only for TOTP, so an account protected by push alone signed straight in. Every sign-in path now asks for whichever second factor the account has, and the hosted pages run the push approval. MFA.
  • Fixed: on IPv6, a magic link only worked from the exact address that requested it. Links are now bound to the IPv6 /64, as they are to the IPv4 /24. A link opened on another network now stops working, as the docs said it did. Magic links.
  • Password checks are capped so a burst of sign-ins can't starve the service. When the queue is full, sign-in, sign-up and password changes answer 503 with code: auth_busy and Retry-After, for known and unknown accounts alike.

September 2026 · Custom domains that watch themselves

  • Every live custom domain is checked every 6 hours: its DNS still reaches TillAuth and its certificate is valid and renewing. The Domains tab shows each domain's health, certificate expiry and last check, with Check now. Records a renewal needs appear on the domain. Health checks.
  • Alerts. Owners and admins get one email and a dashboard notification when a domain fails or needs attention for two checks in a row, or at once when its certificate expires within 7 days. The next comes only after the domain recovers.
  • Pending and verified domains are checked in the background too, so a domain goes live without anyone clicking. One whose TXT record isn't visible within 24 hours, or whose certificate isn't issued within 24 hours of verification, becomes failed and says why; Verify again picks it back up. A certificate whose validation timed out is restarted automatically.
  • Fixed: a hostname another workspace added but never verified could not be added by anyone else. Such a claim now lapses after 24 hours.
  • Fixed: the owner override could mark a domain live before anyone proved they control it. It now requires a verified domain.
  • The add-domain dialog said bare domains work. They can't take the CNAME; use a subdomain such as auth.example.com.
  • Publishable keys. Authorization: Bearer tau_pub_… now names the app, for clients that can only set that header. The key is the public id respelled and isn't a secret; it's under Settings → Keys & integration, printed by tilldev auth apps create and returned by the app API. A request whose X-TillAuth-App and publishable key name different apps is refused. @tillstack/auth-node 0.4.0 accepts one as its appId.
  • In CLI 0.8.0: tilldev auth domains <app> [add | check | rm <hostname>].

September 2026 · Every app signs with its own key

  • New apps sign their tokens with their own key, under their own issuer https://auth.tilldev.dev/realms/<app id>. The key set is published at /realms/<public id>/.well-known/jwks.json (the app id works too) and holds only that app's keys, so no other app's key can produce a token your servers accept. Verifier.
  • Older apps switch when you choose, under Settings → Token signing or with tilldev auth signing <app> rotate. The key is published at once and signs 15 minutes later; from then on the shared key can no longer sign for the app. The issuer on the app's tokens changes when it switches. Servers using @tillstack/auth-node 0.4.0 follow on their own; a verifier or OIDC client that pins https://auth.tilldev.dev or the shared key set must move to the app's issuer and key set.
  • Rotation with an overlap. A new key is published 15 minutes before it signs and the previous one keeps verifying for 24 hours. Replace now (--now) retires the old key at once when it may have leaked. Going back to the shared key keeps the app's recent tokens verifying for 24 hours. Owners only, up to 6 changes an hour, each audited.
  • Signing keys are stored encrypted under the app's data key, and rotating the data key re-encrypts them.
  • Fixed: @tillstack/auth-node set up with the public id, as the docs showed, refused every token. Tokens now carry pid, the app's public id, and 0.4.0 accepts either id. The docs also showed public ids as tau_pub_…; they are tau_…, and tau_pub_… is now the app's publishable key.
  • OIDC: an app with its own key has its own discovery document at https://auth.tilldev.dev/realms/<app id>/.well-known/openid-configuration. The Clients page, tilldev auth clients <app> and the client API now give the issuer and discovery URL to configure. Provider.
  • Custom domains serve the app's key set at https://<your domain>/.well-known/jwks.json. Custom domains.
  • Fixed: custom domain setup showed one CNAME target and copied another, and a verified domain had no way to check its certificate or see the records the certificate needs. The Domains tab now shows the real target, lists the certificate's records and has Check certificate. The custom-domain guide named the wrong TXT record and callback address, and promised a certificate alert and pinning that didn't exist; it now describes what happens, and the alerts are built (above).
  • In CLI 0.8.0: tilldev auth signing <app> [rotate [--now] | shared] [--yes].

September 2026 · Data key rotation you can rely on

  • Fixed: rotating an app's data key skipped webhook secrets, so the app's webhook deliveries then failed. Rotation now re-encrypts them along with provider client secrets, authenticator secrets and linked-account tokens.
  • Fixed: a rotation that stopped partway could lose secrets. Both keys now stay readable until every value has moved, so sign-ins and deliveries carry on, and running it again finishes the job. Data key.
  • Stored secrets now start with their suite id, aead-aes256gcm.v1|. Older ones stay readable and gain it at the next rotation.
  • tilldev auth apps rotate-key <app> in CLI 0.8.0 (owners, up to 3 an hour). A rotation reports what it re-encrypted and is audited as dek.rotated.

September 2026 · Webhooks and fixes

  • verifyWebhook in @tillstack/auth-node 0.4.0 checks a webhook's signature and timestamp over the raw body and returns the typed event. Endpoints subscribed to an unknown event name are now refused instead of saved and never fired.
  • Fixed: accepting an invite twice at once, such as a double click or two open tabs, could fail with a server error. The second accept now reports the invite as already accepted.

v0.6 — July 2026 · The API becomes a product

  • The end-user /v1 surface is now specified as OpenAPI 3.1 and served by the API itself (auth.tilldev.dev/openapi.json), with a CI drift-guard: a documented route that doesn't exist fails the build.
  • Embedded account settings: your own settings screen can call the same API the hosted pages use — set/change password, passkeys, TOTP — from any registered origin with the user's bearer token. Server-side invariants (policy, step-up, the credential floor) cannot be weakened by a client.
  • Password-less accounts (magic-link / OAuth-born) set a first password from a signed-in session — no mailbox round-trip. Step-up-gated when a second factor exists.
  • Counter-locked passkeys are recoverable: unlock after a device restore, or remove one you don't recognise. Locked passkeys never count toward sign-in.
  • The credential floor: removing a last way in — or a last second factor on an MFA-required app — is refused with a stated reason and next action. Dashboard policy changes that would strand existing users are rejected with the affected count.
  • Workers/edge verification documented: @tillstack/auth-node is jose on Web Crypto — runs on workerd/Deno/Bun unchanged, including a fetch-native cookie proxy.

v0.4 — June 2026 · Per-app admin shell

  • App-scoped sidebar at /<org>/auth/<appId>/* — drilling into an app replaces the org nav with App / Users / Sessions / Audit / Providers / Policies, removing the duplicate tab strip.
  • Per-app branding editor — logo, accent, hosted-login copy.
  • Empty-state landing on /<org>/auth when zero apps exist — pillars, numbered quickstart, single CTA.
  • SDK reference index — /docs/tillauth/sdk.

v0.3 — May 2026 · TillDev integration

  • Cross-product audit log — TillAuth rows now also surface under the unified /<org>/audit view alongside TillPulse + TillDev cross-product events.
  • Module-aware org settings — TillAuth's settings group only appears when the module is enabled.
  • Post-signin smart redirect — orgs whose only configured product is TillAuth no longer land on an empty TillPulse dashboard.
  • Onboarding now picks a product before walking through setup.

v0.2 — April 2026 · OAuth + SSO

  • OAuth · Google + GitHub per app — client IDs stored per-app, secrets encrypted at rest.
  • OIDC SSO — JWKS-verified id_token, optional allowed-domain gate.
  • Secret rotation — per-app secrets re-encrypted at rest in one atomic transaction.
  • Custom domains — auth.your-domain.com via customer DNS.
  • Breaking: the verify() server SDK return shape adds identities[]. Existing destructures keep working; new field is optional.

v0.1 — March 2026 · Spine

  • Email + password — modern, memory-hard password hashing (Argon2id), progressive lockout.
  • Passkeys — WebAuthn primary and 2FA, with counter-regression theft detection.
  • TOTP + backup codes — first-class, on every plan.
  • Magic links — IP-prefix bound, redirect allow-list checked.
  • Sessions — refresh-token rotation with theft detection.
  • Webhooks — signed and replay-protected, exponential-backoff retries.
  • Audit log — append-only, stable action vocabulary, 365-day retention.
  • @tillstack/auth-react and @tillstack/auth-node at 0.1.

Looking for another product? See the TillShield and TillForge changelogs, or the full TillDev shipping log.