chore(db): drop the vestigial user challenge columns - #226
Merged
Conversation
WebAuthn challenges moved to the webauthn_challenges table, which gave them a purpose, an expiry and one-time use. users.challenge and users.challenge_context were left behind so that release could be rolled back without losing challenge state, and nothing has read or written either since. Left in place they read as live state to anyone opening src/models/users.ts, and the next person to touch a WebAuthn flow has to work out that they are dead. Both columns and their model declarations are gone. The down restores them nullable, which is the shape they had. It does not restore data and does not need to: a challenge lives 300 seconds, so anything a rollback could carry across has already expired, and the worst case is an in-flight ceremony the user starts again. The table move has not shipped either, so both land in the same release and a deployment sees one step rather than two. Tests that asserted the columns never leak through a response are dropped rather than rewritten. They were guarding an allowlist, and the same tests still assert it for emailVerificationToken and phoneVerificationToken, which are real. Verified against a throwaway PostgreSQL 17 rather than mocks: the up removes both columns, the down restores them as nullable varchar and jsonb, re-applying removes them again, and the User model reads and writes against the new schema. Closes #209
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.
Closes #209.
On the precondition
The issue asks to "confirm #164 has been running in production long enough that a
rollback to before it is no longer plausible". Strictly it has not: the package is
still
0.7.4with 25 pending changesets, so thewebauthn_challengesmigrationhas never been released.
That resolves the concern rather than blocking on it. Because neither has shipped,
both land in the same release, so a deployment moves from "challenges on the
user row" to "challenges in their own table with the old columns gone" in one
step. There is no window in which a rollback lands on code that expects the
columns to still be there and populated.
The residual risk is the data, and it is not material: a challenge lives 300
seconds (
CHALLENGE_TTL_SECONDS), so anything a rollback could have carriedacross has already expired. The worst case is an in-flight ceremony that the user
starts again.
What changed
Both columns are dropped, along with their declarations in
src/models/users.ts. Nothing has read or written either since #164; left inplace they read as live state to the next person opening the model.
The
downrestores both nullable, which is the shape they had.On the deleted assertions
Several tests asserted that
challengeandchallengeContextnever appear in anadmin or user response. Those are dropped rather than rewritten. They were
guarding an allowlist in
serializeApiUser, and the same tests still assert itfor
emailVerificationTokenandphoneVerificationToken, which are real andsensitive, so the guard keeps its teeth.
Verified against a real database
Mocks would not have caught a schema mismatch here, so this was run against a
throwaway PostgreSQL 17 rather than asserted:
db:migrateremoves both columnsdb:migrate:undorestores them ascharacter varyingandjsonb, bothnullable
Usermodel reads and writes against the new schema (findAllandcreateboth succeed), which is what would fail if a declaration had been leftbehind
npm run typecheck,npm run lint,npm run format:check,npm run build,npm run test:run(1173 passing) andnpm run coverage(98.78% statements) allpass.