Skip to content
FoxAuth

Site built from commit 16f191bb, ahead of v1.1.0 (unreleased). Releases: GitHub.

Changelog

All notable changes to this project are documented here.

The format is based on Keep a Changelog and versions follow Semantic Versioning. Spec numbers refer to the (untracked) specs/ directories; open work is tracked as GitHub issues; full retrospectives live in the git history of the retired TASKS.md and in the knowledge base at wiki/.

Unreleased

Added

1.1.0 - 2026-10-16

Provider buttons on the sign-in page fit any number of providers, the console says when one is missing from it and why, and a super administrator can send a test message from the mail settings. No migration and no new storage area.

Added

Changed

Fixed

1.0.1 - 2026-10-16

A new instance tells applications the person’s email address. No migration and no new storage area; an instance that saved its claims setting adds the email scope there.

Fixed

1.0.0 - 2026-10-16

The first stable release, and the one that makes FoxAuth fit to be offered to strangers. A project, formerly a bucket, is the one container: it is its own issuer and holds its applications, APIs and people for life. A team, formerly an administrator group, owns projects. A project can live on a subdomain of the instance. Plans put limits on what a team can create. The gaps one team could use against another are closed, and email_verified is true only for an address that was proven. From this release on the version numbers mean what Semantic Versioning says: an incompatible change to the protocol endpoints, the admin API, the MCP tools or the settings comes only in a major release.

The upgrade asks a backup, db:setup (or db:setup:pg) for the new TeamInvitationThrottle area and the unique index on project slugs, and db:migrate for five migrations (2026-10-15 to 2026-10-19), some of which cannot be undone. What an integration meets, with no aliases kept: admin API paths, fields and MCP tool names say project and team; the old /admin/api/projects routes are gone; can.createHost is can.addressForms; a team owner invites rather than adds; team invitations, signing keys, names and request bodies are bounded; and new end users arrive with email_verified: false until they prove their address. The upgrade guide walks through it.

Added

Changed

Fixed

Security

0.12.0 - 2026-10-14

The pages people see are rebuilt for the people who see them. The consent page says who is asking, which account is agreeing and what the application will be able to do, in plain sentences; people can leave out what they do not want to give and change their mind later; the organization’s own applications show their logo and policy links; and every bucket has a page where people see and remove the access they gave. Every end-user page follows the device’s light or dark scheme, and every end-user page and email is shown in English, Russian or Simplified Chinese, chosen from the person, the application’s ui_locales and the browser.

The upgrade asks a backup and db:setup (or db:setup:pg) for the new ClientLogo area. There is no schema migration. What an integration meets: the consent form now carries the account it was shown for and is refused for any other, so a script that posts the consent form must send accountId; the consent page’s wording and markup changed; end-user pages declare their language and answer with Content-Language and Vary: Accept-Language, Cookie; and both discovery documents publish ui_locales_supported.

Added

Changed

0.11.0 - 2026-10-12

The admin console is rebuilt around addresses, pages and previews. Every screen has a link; buckets, identity providers, provisioning connections, groups and people each have a page; and a change that can lock people out or break applications says whom it affects, counted, before it applies. Deleting a bucket can be undone for 30 days, a signing key rotates in one step and can be revoked at once, administrators can sign in through their organisation’s identity provider, and end users can be invited, signed out of one application or changed a hundred at a time. Usage is open to every administrator, over their own groups’ buckets. Security fixes close a leak of administrators’ authenticator secrets, a verification bypass through an email change, and the repointing of an identity provider under the accounts linked to it.

Upgrading asks four things of an operator. Take a backup. Run db:setup (or db:setup:pg) for the new provisioningRefusals area and the new indexes. Then run db:migrate: migration 2026-10-12-static-token-expiry gives every SCIM static token that has no expiry one, a year from its issue and never sooner than 90 days from the upgrade, and cannot be undone. Administrators who enrolled an authenticator before this release should enrol again. An integration meets these changes: the users list answers { users, totalResults } a page at a time; setting or clearing a project’s bucket, tightening a bucket’s sign-in and handing a group to a directory answer 409 with a preview and apply only when repeated with confirm: true and the same expect; deleting a bucket schedules its destruction instead of destroying it; new buckets are closed to sign-up unless asked otherwise; and a user’s profile now wins over a stored claim of the same name.

Fixed

Security

Added

Changed

Fixed

0.10.0 - 2026-10-10

An instance can now be run as a service for other people. Every bucket counts its monthly and daily active users, a super administrator’s Usage page shows each bucket beside the customer that owns it, and a bucket moves to another administrator group together with its projects. The administrators’ bucket is configured like any other, with open sign-up, email verification and the second factor, and a personal group has exactly one member again. A DPoP proof dated ahead of the server’s clock can no longer be replayed, and foxauth.dev is now also in Russian and Simplified Chinese.

Upgrading asks three things of an operator. Take a backup. Run db:setup (or db:setup:pg) for the new activityMarks and activityFigures areas and the audit trail’s new index. Then run db:migrate: migration 2026-10-09-personal-groups-single-member removes every member but the owner from each personal group and cannot be undone, so list the shared ones (group_list, kind: personal) first. An agent that used admin_settings_read or admin_settings_update now uses bucket_get/bucket_update on the administrators’ bucket, and a setting edited into the database by hand with the wrong type now stops startup until it is fixed.

Added

Changed

Fixed

Security

0.9.0 - 2026-10-07

A sign-out at the upstream provider now reaches this server. A bucket accepts OpenID Connect Back-Channel Logout from the Keycloak, Auth0 or Ping providers an administrator opts in, and ends the sessions that came from the upstream session it names; signing in through Keycloak, which could not complete before, does. Releases now deploy themselves as the signed image they published, the image takes Alpine’s security patches on every build again, and a Compose deployment upgraded by pulling a newer image runs its migrations instead of refusing to start.

Upgrading asks one thing of an operator: run db:setup (or db:setup:pg) for the new UpstreamSession area. This release declares no migration. A Compose deployment that skipped db:migrate when moving to 0.7.0 or 0.8.0 gets it from the updated docker-compose.yml; take the backup those releases asked for first.

Added

Fixed

Security

Changed

0.8.0 - 2026-10-07

An enterprise directory can now run a bucket’s users. Microsoft Entra ID, Okta or any SCIM 2.0 client creates, updates, deactivates and deletes them and their groups through a provisioning connection, an upstream provider can sign one of them out of everything, and a guard holds a connection that starts deprovisioning too many at once. Roles become groups, which relying parties read in a groups claim, and ending a user’s access — by deactivation, a lock or deletion — now ends their sessions and tokens at once rather than only their next sign-in. The instance’s signing keys rotate without a restart, and the rest is the protocol held to its specs: amr in every ID token, the grant types discovery advertises and no others, and a client, assertion, DPoP proof or request object checked against the bucket it was sent to.

Upgrading asks three things of an operator. Take a backup, then run db:setup and db:migrate in that order: this release carries two schema migrations — the root signing keys move into the store a bucket’s keys use, and every role and its holders become a group and its members — and neither can be undone. A deployment whose clients refresh without the offline_access scope among the supported scopes will see unsupported_grant_type from now on, and refreshToken.enabled is gone. And a script or agent that sets roles on an administrator, a bucket or an end user — through the admin API or over MCP — now grants membership of Super administrators (admin_super_grant, admin_super_withdraw) or of a bucket group instead. The upgrade guide walks through the steps.

Added

Changed

Fixed

0.7.0 - 2026-09-29

Tenants stop sharing what should have been theirs alone. A declared protected resource is unique per issuer rather than across the instance, and a bucket with an address of its own signs with keys of its own, so one tenant can neither claim another’s MCP server nor mint a token that verifies at another’s resource server. A security audit found the rest: sign-ins that another browser could continue, single-use artifacts and attempt caps that parallel requests got through, outbound requests that reached private addresses, client metadata that named internal attributes, an agent token that worked on the management API directly, and a consent waiver and password policy that did not hold for every bucket. Each is fixed below, and so are signed responses from a named bucket that named the root issuer.

Upgrading asks four things of an operator. Take a backup, then run db:setup and db:migrate in that order: this release carries the first schema migration, it re-keys declared resources, and it cannot be undone. A resource server of a bucket with an address must verify against that bucket’s own jwks_uri, which the bucket’s metadata now names; tokens the bucket signed before the upgrade stop verifying there, and the instance issuer’s keys and metadata are unchanged. A deployment reading a forwarded client certificate switches on mTLS.trustProxyCertificateHeader once its proxy sets Client-Cert and strips any incoming copy — no certificate is read until then. And declaring a resource for a project without a bucket of its own is now a super administrator’s; give such a project a bucket with an address to declare it yourself. The upgrade guide walks through each.

Security

Added

Fixed

0.6.0 - 2026-09-28

The server stops taking its own data on trust. Every record read back from storage — a token, a session, an account, a bucket — is checked against its schema on the way out, and the request context, request parameters and clients are typed from the schemas the endpoints validate against instead of asserted. Checking what is actually stored found what the assertions had hidden: on PostgreSQL every account update and every group-filtered audit read failed, and a lost race for a bucket hostname answered 500; MongoDB stored an undefined member as null; a host-addressed bucket forgot its sign-ins; and a client’s default_max_age of 0 depended on whether a clock second had passed. Error codes at the token, device and CIBA endpoints, and several request parameters, now follow their RFCs.

Upgrading asks two things of an integration. Extension functions, policy checks, registration policies and RAR validators receive the request context itself rather than { oidc }, with no shim; and a client reading its registration with the access token in the query string sends it in the Authorization header instead. A stored record that does not match its schema now fails its request as a server error the error store records, rather than passing as a malformed value — the shapes 0.5.0 writes are accepted, and no schema migration is declared. Deployments that lengthened a token lifetime will see older browser refresh-token chains sign in once more.

Fixed

Changed

0.5.0 - 2026-09-21

A user bucket finishes becoming a tenant. 0.4.0 gave it a path beneath the server; this release lets it hold a hostname of its own instead — acme.auth.example.com rather than auth.example.com/acme — which is what puts its sign-in cookie on an origin of its own and publishes one metadata location where a path-bearing issuer forces two. A bucket holds one form of address or the other, never both, since two addresses would be two issuer identifiers for one population. Changing an address is its own operation and an administrator of the instance’s alone: it names every client that will stop validating tokens before anything changes, and completes only on a second call.

Signing in to a bucket stops being only a password. Google, Microsoft, Apple and GitHub can each be connected to a bucket by name, asking only for what that provider actually issues, and the console now shows the callback address to register — which differs per bucket and appeared nowhere before. Projects and user buckets can also be deleted from the console, which the management API has always accepted and nothing in the console reached; a container may take its contents with it, on an election made separately from confirming, and neither deletion completes by clicking.

Upgrading signs your administrators in once. The session cookie is now named after the bucket the client signs into rather than the address the request arrived at, because the administrators’ bucket and the default one are both served at the root and only the client says which population a sign-in is for. That is what fixes a console login failing with interaction session and authentication session mismatch whenever the browser already held an end-user sign-in from the same server; the price is that a console session held from before this release is not read, and the administrator signs in again. End users are unaffected, no schema migration is declared, and nothing is lost — accounts, grants, consent and refresh tokens are untouched.

Added

Fixed

0.4.0 - 2026-09-16

One thing defines this release: a user bucket is a tenant of its own. It is addressed in the URL, publishes its own metadata, mints tokens carrying itself as iss, and holds its own sign-in — so a browser can be signed in to two buckets at once and neither disturbs the other. The default bucket keeps the bare issuer and the bare paths, which is what makes the change deliverable to an existing deployment without a flag day.

Two sign-in defects found on the way are fixed with it, and both had the same shape: a browser rule no headless test could see. A sign-in that started at a relying party answered 422 for every real client while the identical URL opened by hand returned 200, because SameSite=Strict withholds a cookie on exactly the navigation the flow is made of. And the “Remember me” checkbox had never decided anything.

Upgrading signs your end users in once. A session cookie now carries the bucket it belongs to; one written before this release does not, is never read, and is expired on the first request that presents it. Nothing is lost — accounts, grants, consent and refresh tokens are untouched — and the upgrade guide says what to expect. The admin console stays at /admin.

Added

Fixed

0.3.0 - 2026-09-14

Three things define this release. The server runs on PostgreSQL as well as MongoDB, behind a schema-migration layer that gates startup on either. The OpenID Foundation conformance suite was run against it for the first time — twelve plans, 16 680 conditions — and the twelve defects those runs found are fixed here, from the PAR response’s content type to the code_verifier alphabet, with CONFORMANCE.md recording where it stands and the five that are still open. And a server setting now takes effect when it is saved, without a restart.

Added

Changed

Fixed

0.2.0 - 2026-09-08

Two things define this release. The server is now an authorization server for third-party MCP servers rather than only for its own administrative plane, and SECURITY.md has evidence behind it: a written threat model, a scanning pipeline, and a container image that is signed and ships an SBOM and build provenance. A plain OAuth 2.1 client can also discover a deployment at last, through RFC 8414 metadata.

Added

Fixed

Security

0.1.0 - 2026-09-03

The first tagged release: everything the server accumulated before a version number existed.

Added

Changed

Removed

Fixed

Security