chore: update version and changelog - #116
Merged
Merged
Conversation
github-actions
Bot
force-pushed
the
changeset-release/main
branch
13 times, most recently
from
July 30, 2026 13:31
8c40486 to
74e1d7a
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
July 30, 2026 14:23
74e1d7a to
1dc39ae
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or setup this action to publish automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
seamless-auth-api@0.6.0
Minor Changes
d248f4e: Make scoped admin roles assignable and discoverable.
POST /admin/usersandPATCH /admin/users/:userIdnow reject any role that is not inavailable_roleswith400 Invalid roles, naming the offending roles indetails.roles.Previously a typo like
admin:reedwas accepted and stored, granted nothing because enforcementnever matches an unlisted role, and reported no error. The match is exact, so wildcards and deeper
scopes must be listed to be handed out. An empty or unreadable role catalog skips the check rather
than rejecting every assignment.
PATCH /admin/users/:userIdpreviously returned its400through a schema that strippeddetails, so the caller could not see what was rejected. Both user endpoints now use a validationerror schema that carries it, and
POST /admin/usersdeclares its400and409responses inOpenAPI.
The
OWNER_EMAILgrant is nowadmin:writerather than a bareadmin, stating the owner'sauthority in the scoped vocabulary. Instances whose
available_roleslists onlyadminstill getadmin, which is equivalent in power, so the grant never becomes a no-op..env.examplenow shipsadmin:readandadmin:writeinAVAILABLE_ROLESso the console's rolepicker offers read-only admin.
AVAILABLE_ROLESseedsavailable_roleson first boot only, soexisting instances need the scoped roles added through the admin system-config endpoints.
28d6c22: Support TLS to Postgres.
DB_SSL(true/false, or ansslmodevalue such asrequireorverify-full) and ansslmodequery parameter on the connection string now set Sequelize'sdialectOptions.ssl, andDB_SSL_CAsupplies a server CA bundle as inline PEM or a file path.Certificate verification follows libpq semantics:
requireencrypts without verifying,verify-caand
verify-fullverify, and supplying a CA bundle turns verification on.DB_SSL_REJECT_UNAUTHORIZEDoverrides it either way. TLS stays off by default.
DB_URIis now accepted as an alias forDATABASE_URL. Connection and TLS resolution is sharedbetween the running app and the startup migrations, so a connection string configured without the
discrete
DB_*variables no longer breaks migrations.7932ec7: Adopt
@seamless-auth/types@0.4.0as the source for the shared contract. 88 schema definitionsacross 30 files become re-exports, removing roughly 900 lines of definitions this repo maintained
in parallel with the SDKs, the dashboard, and the CLI.
roleGrantsAccess,hasScopedRole, andROLE_NAME_PATTERNnow come from the package too, and the local transport widening shim addedalongside the passkey fix is gone since the shared
TransportSchemacarries the full WebAuthn set.Two response shapes change as a result:
createdAt/updatedAtwerez.any()and are now typed ISOdate-times and required. They were always sent; they are now documented accurately.
magic_link_emaildelivery markstokenoptional, and anomaly events marktypeoptional,matching the shared schemas. Both are still always present in responses.
Several schemas stay local because the shared versions would lose behavior: the pruned auth event
list and the query filter built on it, the interval-aware metrics window cap, the timeseries
total/categoriesfields, the grouped-summaryoutcomes, the OTP and magic-link successenvelope that carries
tokenanddelivery, PRF salt validation, role assignment validation, andthe JWKS and health responses that no other repo consumes. Each is commented with why.
5ecd1f2: Give
POST /logina single rejection shape and record the server's security posture.Every
/loginfailure now answers an identical401 { "error": "Not Allowed" }. An unknownidentifier, an unverified account, an account with no permitted continuation method, and a failure
to mint the pre-auth token previously produced three distinguishable bodies, so an unauthenticated
caller could tell which case it had hit. The reason is still recorded in the
login_failedauthevent metadata, so operators keep the detail.
The two identifier-lookup failure branches, which fire when the database is unreachable rather
than when the identifier is unknown, answered
401on the email path and403on the phone path.Both now answer
500 { "error": "Server error" }, so an outage is reported as an outage insteadof as a failed login.
loginMethodsis unchanged on the success response. Removing it buys no enumeration benefit untilunknown identifiers also receive a decoy token, and dropping it early would degrade legitimate
clients to a default method list.
New
docs/security-posture.mdstates the deliberate tradeoffs and their mitigating controls: theresidual enumeration on
/loginand what closing it would take, ephemeral token replay within its5 minute TTL, email and phone being plaintext at rest, and the per-deployment
audscheme.8ebdc2e: Standardize the error response shape. Every
4xxand5xxresponse now carries a requirederrorstring, withmessagereserved for optional extra detail, so a consumer reads one fieldto learn why a call failed.
25 handlers across the WebAuthn, user, internal metrics, dashboard, and security endpoints
returned
{ message }with noerror. They now seterror. Eight error responses on the/internal/*routes were declared withMessageSchema, which has noerrorfield and would havestripped it; they now use
ErrorSchema.InternalErrorSchemawas a byte-identical duplicate ofErrorSchemaand is now a deprecated aliasof it. Nothing changes on the wire for the 53 routes that reference it.
Success responses are untouched:
{ "message": "Success" }on a200is a success payload, not anerror body.
A new conformance test walks every registered route and fails if a failure response declares a
schema without a required
errorstring, so a new route cannot reintroduce the split.ecaee6b: Prune auth event types that nothing emits, and fix the consumers that searched for them.
15 of the 74 declared types had no emit site anywhere in
src/. Five of them were activelyqueried by the security anomaly detector (
bearer_token_failed,jwks_failed,otp_failed,recovery_otp_failed,user_data_failed), so those failure categories always returned nothing,while
verify_otp_failed,totp_failed,magic_link_failed, andlogout_failedwere emitted andnever searched for. The admin event filter had the same problem:
otpexpanded to a type nothingemits and missed every
verify_otp_*andmfa_otp_*event, andsuspiciousnamed two types thatare never written.
Removed: the
bearer_token_*andjwks_*families,recovery_otp_*,user_data_failed,user_data_success,otp_failed,mfa_otp_suspicious,service_token_suspicious, andwebauthn_login_suspicious.The failure and suspicious groupings are now derived from
AUTH_EVENT_TYPESinstead of beinghand-copied, so adding an event type files it in the right bucket automatically. A test asserts
every declared type has an emit site, which is what would have caught the removed
bootstrap_admin_*entries when that feature went away.
Reading historical rows is unaffected: stored events are returned as
z.string()and the queryfilter accepts any string, so events recorded under a removed name still appear.
f7f95dc: Broaden the internal metrics endpoints beyond login success and failure.
Auth event types are now rolled up into mutually exclusive categories (
suspicious,login,registration,webauthn,oauth,magicLink,otp,totp,stepUp,token,system,other). Previously the grouping matched on substrings in order, sowebauthn_login_successandoauth_login_successwere counted as plain logins and never appeared in their own buckets./internal/auth-events/timeseriesbuckets now carrytotaland acategoriesmap.successandfailedstay login-only, so existing dashboards keep working./internal/auth-events/groupedreturns the new categories insummaryplus anoutcomesroll-up, counts in the database instead of loading every auth event into memory, and accepts
from,to, anduserId./internal/auth-events/login-statsand/internal/auth-events/summaryaccept the samefrom,to, anduserIdfilters as the other metrics endpoints.Two date-window fixes: the timeseries used to validate
from/toand then return a now-relativewindow anyway (and with
interval=dayit queried only the last 24 hours while filling 30 dailybuckets). Buckets now span the requested window. The maximum window is also capped per interval,
31 days for
hourand 366 days forday, and an open-ended window is measured against thecurrent time rather than being unbounded.
ddf9b41: Generate a real typed client from OpenAPI.
src/generated/api.tswas an emptyopenapi-typescript stub (
paths = Record<string, never>) with no way to regenerate it. It nowcarries the full contract, generated from the live route definitions along with a committed
openapi.json.npm run generate:apiproduces both. It loads the route modules into a throwaway Express app topopulate the OpenAPI registry, so no server, environment, or database is needed, and it refuses to
write an empty spec. Output is prettier-formatted so regenerating never leaves the tree failing
format:check.Both artifacts are covered by a test that rebuilds the document and compares it to what is
committed, so a route or schema change that is not regenerated fails CI instead of silently
drifting. The comparison ignores
info.version, which trackspackage.jsonand is bumped byChangesets on release.
b3cb4d3: Add a one-command local stack.
docker compose upnow brings up Postgres and the published APIimage together with development defaults, so trying the project no longer means setting up
Postgres, writing a
.env, and generating secrets by hand. Migrations run on first boot, adevelopment signing keypair is generated, and the admin console is served at
http://localhost:5312/console.OWNER_EMAILdefaults toowner@example.com, so signing up withthat address gives a working admin.
The previous source-built stack moved to
docker-compose.dev.ymlfor contributors. It no longerrequires a
.envfile (one is used when present, and its values win) and it no longer depends ona prior
npm run build, which meant it could not start from a fresh clone. A newdev:containerscript runs the watcher without
--env-file.Neither compose file pins
container_nameany more, so a leftover container from an older stackno longer blocks startup.
81d8ea5: Ship admin dashboard v0.4.0 in the API image.
SEAMLESS_ADMIN_DASHBOARD_REFmoves from v0.3.0 to v0.4.0, so the SPA served at/consolepicksup that release. It brings keyboard and screen-reader operability across the app shell and charts,
a step-up path for admins with no passkey, editable organization memberships, inline validation and
unsaved-changes warnings in system configuration, refresh and export on monitoring, and fixes to
the user directory, the events view, and sign-in failure messaging.
The ref is a release tag rather than a floating branch, so the dashboard only changes when this
value does.
e9c10b1: Remove the admin bootstrap invite flow.
POST /internal/bootstrap/admin-inviteis gone, along withthe
bootstrapTokenfield onPOST /registration/register, thebootstrap_invite_emailexternaldelivery kind, the
bootstrap_admin_grantedandbootstrap_admin_check_skippedauth event types,and the
SEAMLESS_BOOTSTRAP_ENABLED,SEAMLESS_BOOTSTRAP_SECRET, andSEAMLESS_AUTH_DEBUG_SECRETSenvironment variables. A migration drops the
bootstrap_invitestable.The first admin is now granted through
OWNER_EMAIL: a user who signs up with a configured owneremail receives the admin role on account creation.
Patch Changes
81d8ea5: Prune unused dependencies and clear the fixable audit findings.
Removed three declared dependencies that nothing in the repo imports:
@sequelize/postgres(an alpha of the Sequelize v7 rewrite, which sat in
dependenciesnext tosequelize@6andshipped to the runtime image),
@types/bcrypt(the code usesbcrypt-ts, which carries its owntypes, and a types package does not belong in production dependencies), and
ts-node(everythingruns through
tsx). The production dependency tree drops by 38 packages.tsxmoves to^4.23.1, which takesesbuild ~0.28.0and so resolves the patchedesbuild0.28.1, and the
allowScriptspin follows it. Together withnpm audit fix, this clears theesbuild,fast-uri,js-yaml,tar, and nestedminimatchadvisories.@types/nodemoves to^24.10.1to match the>=24 <25engine and the pinned Node 24 runtime,and
@eslint/jsmoves to^10.0.1to line up with the already-installed ESLint 10. The ESLint 10recommended set adds
preserve-caught-error, which the log-and-rethrow sites insrc/lib/token.tsand
src/utils/otp.tsalready satisfy, so enabling it needs no further source changes.Two advisories are knowingly left in place because neither has a non-breaking fix.
sequelize-cli6.6.5 (the latest) still depends on
js-beautify, which reaches a vulnerablebrace-expansion;pinning
brace-expansionto the patched 5.0.8 is not viable because its CJS entry exports{ expand }while theminimatch@9in that chain calls the default export, which would breakmigrations at container start.
sequelize@6pinsuuid ^8, and theuuidadvisory covers onlyv3/v5/v6with abufargument while Sequelize usesv1andv4, so it is not reachable.870c370: Stop truncating passkey transports. A cross-device passkey reports
hybrid, older Chrome reportedcable, and security keys can reportsmart-card, but the credential serializer filteredtransportsdown to the pre-hybridusb/ble/nfc/internalset before building the response.Those credentials reported an empty transport list to every client, including
/users/meand theadmin user detail view.
Stored data was never affected: the column is unconstrained JSON and registration writes the value
through verbatim, so the correct transports come back as soon as the read path stops dropping them.
No migration or backfill is needed.
CredentialResponseSchemacarries a local override wideningtransportsto the full WebAuthn set,because
@seamless-auth/types@0.1.3still declares the narrow one. The override andsrc/lib/authenticatorTransports.tscan both be deleted once the widened types release is adopted.