Skip to content

chore: update version and changelog - #116

Merged
Bccorb merged 1 commit into
mainfrom
changeset-release/main
Jul 30, 2026
Merged

chore: update version and changelog#116
Bccorb merged 1 commit into
mainfrom
changeset-release/main

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

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/users and PATCH /admin/users/:userId now reject any role that is not in
    available_roles with 400 Invalid roles, naming the offending roles in details.roles.
    Previously a typo like admin:reed was accepted and stored, granted nothing because enforcement
    never 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/:userId previously returned its 400 through a schema that stripped
    details, so the caller could not see what was rejected. Both user endpoints now use a validation
    error schema that carries it, and POST /admin/users declares its 400 and 409 responses in
    OpenAPI.

    The OWNER_EMAIL grant is now admin:write rather than a bare admin, stating the owner's
    authority in the scoped vocabulary. Instances whose available_roles lists only admin still get
    admin, which is equivalent in power, so the grant never becomes a no-op.

    .env.example now ships admin:read and admin:write in AVAILABLE_ROLES so the console's role
    picker offers read-only admin. AVAILABLE_ROLES seeds available_roles on first boot only, so
    existing instances need the scoped roles added through the admin system-config endpoints.

  • 28d6c22: Support TLS to Postgres. DB_SSL (true/false, or an sslmode value such as require or
    verify-full) and an sslmode query parameter on the connection string now set Sequelize's
    dialectOptions.ssl, and DB_SSL_CA supplies a server CA bundle as inline PEM or a file path.
    Certificate verification follows libpq semantics: require encrypts without verifying, verify-ca
    and verify-full verify, and supplying a CA bundle turns verification on. DB_SSL_REJECT_UNAUTHORIZED
    overrides it either way. TLS stays off by default.

    DB_URI is now accepted as an alias for DATABASE_URL. Connection and TLS resolution is shared
    between 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.0 as the source for the shared contract. 88 schema definitions
    across 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, and
    ROLE_NAME_PATTERN now come from the package too, and the local transport widening shim added
    alongside the passkey fix is gone since the shared TransportSchema carries the full WebAuthn set.

    Two response shapes change as a result:

    • Organization and membership createdAt/updatedAt were z.any() and are now typed ISO
      date-times and required. They were always sent; they are now documented accurately.
    • magic_link_email delivery marks token optional, and anomaly events mark type optional,
      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/categories fields, the grouped-summary outcomes, the OTP and magic-link success
    envelope that carries token and delivery, PRF salt validation, role assignment validation, and
    the JWKS and health responses that no other repo consumes. Each is commented with why.

  • 5ecd1f2: Give POST /login a single rejection shape and record the server's security posture.

    Every /login failure now answers an identical 401 { "error": "Not Allowed" }. An unknown
    identifier, 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_failed auth
    event 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 401 on the email path and 403 on the phone path.
    Both now answer 500 { "error": "Server error" }, so an outage is reported as an outage instead
    of as a failed login.

    loginMethods is unchanged on the success response. Removing it buys no enumeration benefit until
    unknown identifiers also receive a decoy token, and dropping it early would degrade legitimate
    clients to a default method list.

    New docs/security-posture.md states the deliberate tradeoffs and their mitigating controls: the
    residual enumeration on /login and what closing it would take, ephemeral token replay within its
    5 minute TTL, email and phone being plaintext at rest, and the per-deployment aud scheme.

  • 8ebdc2e: Standardize the error response shape. Every 4xx and 5xx response now carries a required
    error string, with message reserved for optional extra detail, so a consumer reads one field
    to learn why a call failed.

    25 handlers across the WebAuthn, user, internal metrics, dashboard, and security endpoints
    returned { message } with no error. They now set error. Eight error responses on the
    /internal/* routes were declared with MessageSchema, which has no error field and would have
    stripped it; they now use ErrorSchema.

    InternalErrorSchema was a byte-identical duplicate of ErrorSchema and is now a deprecated alias
    of it. Nothing changes on the wire for the 53 routes that reference it.

    Success responses are untouched: { "message": "Success" } on a 200 is a success payload, not an
    error body.

    A new conformance test walks every registered route and fails if a failure response declares a
    schema without a required error string, 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 actively
    queried 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, and logout_failed were emitted and
    never searched for. The admin event filter had the same problem: otp expanded to a type nothing
    emits and missed every verify_otp_* and mfa_otp_* event, and suspicious named two types that
    are never written.

    Removed: the bearer_token_* and jwks_* families, recovery_otp_*, user_data_failed,
    user_data_success, otp_failed, mfa_otp_suspicious, service_token_suspicious, and
    webauthn_login_suspicious.

    The failure and suspicious groupings are now derived from AUTH_EVENT_TYPES instead of being
    hand-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 query
    filter 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, so webauthn_login_success and
    oauth_login_success were counted as plain logins and never appeared in their own buckets.

    • /internal/auth-events/timeseries buckets now carry total and a categories map.
      success and failed stay login-only, so existing dashboards keep working.
    • /internal/auth-events/grouped returns the new categories in summary plus an outcomes
      roll-up, counts in the database instead of loading every auth event into memory, and accepts
      from, to, and userId.
    • /internal/auth-events/login-stats and /internal/auth-events/summary accept the same
      from, to, and userId filters as the other metrics endpoints.

    Two date-window fixes: the timeseries used to validate from/to and then return a now-relative
    window anyway (and with interval=day it queried only the last 24 hours while filling 30 daily
    buckets). Buckets now span the requested window. The maximum window is also capped per interval,
    31 days for hour and 366 days for day, and an open-ended window is measured against the
    current time rather than being unbounded.

  • ddf9b41: Generate a real typed client from OpenAPI. src/generated/api.ts was an empty
    openapi-typescript stub (paths = Record<string, never>) with no way to regenerate it. It now
    carries the full contract, generated from the live route definitions along with a committed
    openapi.json.

    npm run generate:api produces both. It loads the route modules into a throwaway Express app to
    populate 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 tracks package.json and is bumped by
    Changesets on release.

  • b3cb4d3: Add a one-command local stack. docker compose up now brings up Postgres and the published API
    image 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, a
    development signing keypair is generated, and the admin console is served at
    http://localhost:5312/console. OWNER_EMAIL defaults to owner@example.com, so signing up with
    that address gives a working admin.

    The previous source-built stack moved to docker-compose.dev.yml for contributors. It no longer
    requires a .env file (one is used when present, and its values win) and it no longer depends on
    a prior npm run build, which meant it could not start from a fresh clone. A new dev:container
    script runs the watcher without --env-file.

    Neither compose file pins container_name any more, so a leftover container from an older stack
    no longer blocks startup.

  • 81d8ea5: Ship admin dashboard v0.4.0 in the API image.

    SEAMLESS_ADMIN_DASHBOARD_REF moves from v0.3.0 to v0.4.0, so the SPA served at /console picks
    up 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-invite is gone, along with
    the bootstrapToken field on POST /registration/register, the bootstrap_invite_email external
    delivery kind, the bootstrap_admin_granted and bootstrap_admin_check_skipped auth event types,
    and the SEAMLESS_BOOTSTRAP_ENABLED, SEAMLESS_BOOTSTRAP_SECRET, and SEAMLESS_AUTH_DEBUG_SECRETS
    environment variables. A migration drops the bootstrap_invites table.

    The first admin is now granted through OWNER_EMAIL: a user who signs up with a configured owner
    email 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 dependencies next to sequelize@6 and
    shipped to the runtime image), @types/bcrypt (the code uses bcrypt-ts, which carries its own
    types, and a types package does not belong in production dependencies), and ts-node (everything
    runs through tsx). The production dependency tree drops by 38 packages.

    tsx moves to ^4.23.1, which takes esbuild ~0.28.0 and so resolves the patched esbuild
    0.28.1, and the allowScripts pin follows it. Together with npm audit fix, this clears the
    esbuild, fast-uri, js-yaml, tar, and nested minimatch advisories.

    @types/node moves to ^24.10.1 to match the >=24 <25 engine and the pinned Node 24 runtime,
    and @eslint/js moves to ^10.0.1 to line up with the already-installed ESLint 10. The ESLint 10
    recommended set adds preserve-caught-error, which the log-and-rethrow sites in src/lib/token.ts
    and src/utils/otp.ts already satisfy, so enabling it needs no further source changes.

    Two advisories are knowingly left in place because neither has a non-breaking fix. sequelize-cli
    6.6.5 (the latest) still depends on js-beautify, which reaches a vulnerable brace-expansion;
    pinning brace-expansion to the patched 5.0.8 is not viable because its CJS entry exports
    { expand } while the minimatch@9 in that chain calls the default export, which would break
    migrations at container start. sequelize@6 pins uuid ^8, and the uuid advisory covers only
    v3/v5/v6 with a buf argument while Sequelize uses v1 and v4, so it is not reachable.

  • 870c370: Stop truncating passkey transports. A cross-device passkey reports hybrid, older Chrome reported
    cable, and security keys can report smart-card, but the credential serializer filtered
    transports down to the pre-hybrid usb/ble/nfc/internal set before building the response.
    Those credentials reported an empty transport list to every client, including /users/me and the
    admin 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.

    CredentialResponseSchema carries a local override widening transports to the full WebAuthn set,
    because @seamless-auth/types@0.1.3 still declares the narrow one. The override and
    src/lib/authenticatorTransports.ts can both be deleted once the widened types release is adopted.

@github-actions
github-actions Bot force-pushed the changeset-release/main branch 13 times, most recently from 8c40486 to 74e1d7a Compare July 30, 2026 13:31
@github-actions
github-actions Bot force-pushed the changeset-release/main branch from 74e1d7a to 1dc39ae Compare July 30, 2026 14:23
@Bccorb
Bccorb merged commit e10bcba into main Jul 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant