Repository navigation
security: replace open routes with authenticated, validated actions (PR 1.4) - #12
Merged
Merged
Conversation
PR 1.4 of docs/MILESTONES.md. Closes RISKS R2.
- Deleted /api/auth/signout, /api/daily-login/*, /api/profile/update.
Replaced by server actions in features/{auth,profile,feedback}:
session first (requireUser / requireUserChecked), zod second,
identity never taken from the client.
- signUp/signIn now verify the Firebase ID token server-side and take
uid/email from its claims (the old signUp trusted a client uid).
Password policy raised to 8 characters (shared zod schema).
- createFeedback: user from session, interview existence checked,
transcript bounded (500 turns x 4000 chars, role enum), existing
feedback resolved server-side (no client feedbackId); generation moved
to server/services/feedback.service.ts with the transcript delimited
as data (SECURITY §2.4). Also fixes a latent mismatch between the
prompt's category names and the zod literals.
- /api/vapi/generate (server-to-server from Vapi): shared secret in
x-vapi-secret with constant-time compare, zod body, target user must
exist, disabled (503) unless VAPI_WEBHOOK_SECRET is set.
- server/services/streak.service.ts: pure, timezone-aware streak logic
recorded on the first authenticated request per day from the layout;
the streak widget takes props from the session user (no polling).
- server/db/types.ts: canonical transcript/id schemas (features compose
them; server never imports features).
- scripts/revoke-all-sessions.ts for the one-off R1 step post-release.
Verified: pnpm lint, typecheck, knip, vitest 44/44 (unauthenticated,
identity-from-session, malformed input, error-leak, webhook 503/401/
400/404 cases), next build; runtime on next start: legacy routes 404,
webhook 503, authenticated dashboard 200 with lastLoginDate/streak
written by the layout.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch had an error being deployed
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.
Summary
PR 1.4 of
docs/MILESTONES.md(Phase 1). Closes R2 — every remaining unauthenticated entry point is gone or gated.POST /api/auth/signout(open)features/auth/actions.ts#signOut→destroySession(revokes tokens)GET /api/daily-login/{check,streak}?userId=(enumerable)server/services/streak.service.ts, recorded on the first authenticated request from the layout; widget takes props from the session userPOST /api/profile/update(cookie read by hand)features/profile/actions.ts#updateProfile—requireUserChecked+ zod (description ≤ 500, image whitelist/size cap)createFeedback(userId, feedbackId)trusted the clientfeatures/feedback/actions.ts— user from session, interview existence checked, transcript bounded, existing feedback resolved server-side; generation inserver/services/feedback.service.tswith the transcript delimited as data (§2.4)signUp(uid, …)trusted a client uidPOST /api/vapi/generate(open)x-vapi-secret(constant-time), zod body, target user must exist; 503 unlessVAPI_WEBHOOK_SECRETis setAlso:
server/db/types.ts(canonical transcript/id schemas — features compose them, server never imports features),scripts/revoke-all-sessions.tsfor the one-off R1 step, a latent bug fixed (prompt category names didn't match the zod literals).Type
Verification
pnpm lint✔ ·typecheck✔ ·knip✔ ·vitest44/44 — unauthenticated, identity-from-session, malformed input, no-error-leak, webhook 503/401/400/404, streak date logic across time zones ·next build✔lastLoginDate/loginStreakwritten by the layout; user cleaned upServer checklist
requireUser()/requireUserChecked()(or secret check) first in every action/routeprocess.envoutsideconfig/env.ts; no PII in logs (opLogger, redaction)Risk watch-list
Docs
docs/SECURITY.md§4 (webhook listed as signed),docs/RISKS.md,.env.example(VAPI_WEBHOOK_SECRET)Config note: set
VAPI_WEBHOOK_SECRETin Vercel and the same value as anx-vapi-secretheader in the Vapi workflow's API Request node, or the generate route stays disabled.🤖 Generated with Claude Code