fix(core): make the dev-bypass token idempotent by name#1584
Conversation
The dev setup-bypass endpoint (`?token=1`) created a fresh `dev-bypass-token` PAT on every call, so re-running it after a dev reset left duplicate rows. The e2e API-tokens page then tripped Playwright strict mode (the unscoped `dev-bypass-token` locator matched more than one). The endpoint now deletes any existing token of that name before minting a new one, via a new `deleteApiTokensByName` helper, keeping exactly one while still returning a usable raw token.
🦋 Changeset detectedLatest commit: 9e39e2d The changes in this PR will be included in the next version bump. This PR includes changesets to release 16 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
docs | 9e39e2d | Jun 22 2026, 06:16 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-playground | 9e39e2d | Jun 22 2026, 06:17 PM |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-cache | 9e39e2d | Jun 22 2026, 06:18 PM |
There was a problem hiding this comment.
Approach
This is the right change at the right layer. The dev-only GET /_emdash/api/setup/dev-bypass?token=1 endpoint minted a fresh dev-bypass-token PAT on every call. Because the e2e harness legitimately re-runs it — global-setup calls it once at startup, and refresh-server-pat calls it again after a dev reset — and on the Cloudflare lane D1 persists across tests within a shard, duplicate dev-bypass-token rows accumulated and tripped Playwright strict mode on text=dev-bypass-token (resolved to 2 elements). Making the endpoint idempotent by name (delete any existing dev-bypass-token for the user, then mint a fresh one) fixes the root cause inside the endpoint rather than coupling the harness to token internals. It fits EmDash's handler-layer conventions and is correctly scoped to the ?token=1 path (the devBypassAuth fixture calls dev-bypass without ?token=1, so it's unaffected). A flawless implementation of the right thing.
What I checked
- SQL safety:
deleteApiTokensByNameuses parameterized Kysely (where("user_id", "=", userId),where("name", "=", name)) — nosql.raw, no interpolation. Scoped by bothuser_idandname, so it cannot touch other users' tokens. - Return-value handling:
Number(result.numDeletedRows ?? 0n)correctly converts the bigint, with a defensive null guard. TheexecuteTakeFirst()+result.numDeletedRowspattern matches the existinghandleApiTokenRevokein the same file. - Idempotency logic: delete-then-create is the correct shape — the raw token is only available at creation and only the hash is stored, so you cannot reuse an old row; you must create fresh. After delete+create there is exactly one
dev-bypass-tokenrow, which resolves the strict-mode violation. The non-atomic delete+create is non-impactful here: the endpoint is dev-only, the old token's raw value was already unrecoverable so deleting it loses no usable secret, a create failure surfaces loudly (caller throws on the missing token), and the harness polls/retries so it self-heals. Not worth flagging. - Tests: Two real reproducing tests — scoped bulk delete (removes only the matching name for that user, leaves
keepme) and the idempotent re-issue loop (exactly onedev-bypass-tokenafter two delete+create cycles). Theusersinsert matches the migrated schema (avatar_url,email_verified,disabled,updated_atfrom migrations 008/009), so the FK on_emdash_api_tokensis satisfied and the test is valid. Satisfies AGENTS.md's "failing test → fix → verify." - Conventions: imports ordered, tabs, no admin UI strings (Lingui/RTL n/a), dev-only endpoint so no authorization requirement, no content-table/locale concerns, changeset present and well-written (
"emdash": patch, leads with a present-tense verb, describes the observable effect).
Conclusion
Clean fix. No findings.
What does this PR do?
The dev setup-bypass endpoint (
GET /_emdash/api/setup/dev-bypass?token=1) minted a freshdev-bypass-tokenPAT on every call. Because the token's raw value is only available at creation, the e2e harness (global-setupandrefresh-server-patafter a dev reset) re-runs it to obtain a usable token — so on the Cloudflare lane, where D1 persists across tests within a shard, duplicatedev-bypass-tokenrows accumulated. The API-tokens settings test then tripped Playwright strict mode, since itstext=dev-bypass-tokenlocator matched more than one row:This makes the dev-bypass token idempotent by name: it deletes any existing
dev-bypass-tokenfor the user before creating a new one, so exactly one row exists while callers still receive a usable raw token. Adds a smalldeleteApiTokensByNamehandler for the delete.Surfaced while reviewing #1378 (object cache); the failure is unrelated to that work — its merge of
mainsimply reshuffled e2e sharding and exposed this pre-existing isolation bug.Closes #
Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change) — newapi-tokens.test.tsunit tests, plus the existingapi-tokens.spec.tse2epnpm formathas been runAI-generated code disclosure
Screenshots / test output