Skip to content

feat(auth): add withAuthenticatedUser and withOptionalUser wrappers#34

Merged
joshmu merged 11 commits into
mainfrom
claude/improve-architecture-tdd-fQAvU
May 2, 2026
Merged

feat(auth): add withAuthenticatedUser and withOptionalUser wrappers#34
joshmu merged 11 commits into
mainfrom
claude/improve-architecture-tdd-fQAvU

Conversation

@joshmu
Copy link
Copy Markdown
Owner

@joshmu joshmu commented May 2, 2026

Hoist token extraction, JWT verification, user lookup, and 401 responses
out of every API route into a single deep wrapper. Handlers receive a
populated AuthContext (userDoc, email, freshly rotated token) and can
focus on the resource they own. The optional sibling supports note.ts'
guest creation flow without conflating contracts behind a flag.

Removes the @ts-ignore in utils/jwt.ts by giving authenticateToken and
generateAccessToken proper return types via a TokenPayload alias.

Tests cover the missing-header, invalid-token, missing-user, happy-path,
error-propagation, and guest paths via mocked jwt + Mongoose User.

claude added 11 commits May 2, 2026 08:43
Hoist token extraction, JWT verification, user lookup, and 401 responses
out of every API route into a single deep wrapper. Handlers receive a
populated AuthContext (userDoc, email, freshly rotated token) and can
focus on the resource they own. The optional sibling supports note.ts'
guest creation flow without conflating contracts behind a flag.

Removes the @ts-ignore in utils/jwt.ts by giving authenticateToken and
generateAccessToken proper return types via a TokenPayload alias.

Tests cover the missing-header, invalid-token, missing-user, happy-path,
error-propagation, and guest paths via mocked jwt + Mongoose User.
Drop the hand-rolled token extraction, JWT verification, and 401
boilerplate now that the wrapper owns that contract.
Remove the duplicated auth boilerplate. Widen UserDocInterface to
expose the schema fields (settings, projects, etc.) the route reads
through the typed userDoc on AuthContext.
Drop the duplicated token/email plumbing. The wrapper hands the route
both the userDoc and a freshly rotated newToken so the trailing
generateAccessToken call is no longer needed.
Drop the duplicated auth boilerplate. The action switch now uses
ctx.userDoc and ctx.newToken, removing two interleaved
generateAccessToken calls that were threading the same email through
the function.
Replace the local isGuestUser flag and the inline JWT/User lookup with
the OptionalAuthContext supplied by the wrapper. Guest creation flow
is now expressed by the discriminated ctx rather than a stateful
boolean threaded through the handler.
Centralise the password contract for shared projects in a small pure
module so the read path (public_project) and write path (project) can
agree on what "open", "passwordRequired", "incorrect" and "ok" mean.

The discriminated ShareAccessResult lets callers handle each case
explicitly instead of branching on string messages. Tests use a real
bcrypt round-trip to prove the seam, not a mock.
Both SHARE branches now delegate password hashing to the share module.
The optional-chaining null guard is kept so the existing
sharing.test.ts regression continues to detect the original Bug 4
crash, but the salt-rounds policy and the bcrypt dependency now live
in one place.
Replace the inline bcrypt branch with a call to the shared module. The
read path is now null-safe — the previous \`shareDoc.password.length\`
crash when password was null/undefined is gone, since the helper treats
empty/null as "open access" by contract.

Also adds an explicit null guard for \`shareDoc\` so a missing share url
returns a clear 400 instead of falling into the bare catch.

Status codes for passwordRequired/incorrect are intentionally kept at
200 to preserve the current client contract; see CONTEXT.md follow-ups.
- README "API conventions" section explains how to use
  withAuthenticatedUser, withOptionalUser, hashSharePassword and
  verifySharePassword in route handlers.
- CONTEXT.md (new) defines the project vocabulary across the Domain
  (User/Project/Note/Share/Settings) and the two new architecture
  seams (Identity/Auth, Share). Includes a "Known follow-ups"
  section so future architecture reviews don't re-suggest the same
  out-of-scope items.
- .markdownlint-cli2.jsonc relaxes MD013 (line length) and MD028
  (blank line in blockquote) so prose docs are not constrained to
  80-char wrapping; pre-existing README content already exceeded
  that threshold.
- pages/api/auth: drop the second User.findOne and populate the
  userDoc the wrapper already loaded. Auth checks now make one Mongo
  round-trip instead of two.
- pages/api/note: delete the empty if (action === REMOVE) {} branch
  carried over from the original; it never did anything.
- utils/auth/withAuthenticatedUser: document why extractBearer anchors
  the prefix at the start of the header.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
videonote Ready Ready Preview, Comment May 2, 2026 9:02am

@joshmu joshmu merged commit 1f4bb00 into main May 2, 2026
10 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.

2 participants