Skip to content

feat: add JWT auth, usage/quota tracking, and frontend auth flow (issue #4)#21

Open
zjy4fun wants to merge 2 commits into
mainfrom
codex/implement-feature-from-issue-4
Open

feat: add JWT auth, usage/quota tracking, and frontend auth flow (issue #4)#21
zjy4fun wants to merge 2 commits into
mainfrom
codex/implement-feature-from-issue-4

Conversation

@zjy4fun

@zjy4fun zjy4fun commented Apr 22, 2026

Copy link
Copy Markdown
Owner

Motivation

  • Replace the ad-hoc/mock user checks with a proper JWT-based authentication flow and access/refresh token rotation.
  • Track per-user token usage and enforce daily quotas for free/pro plans to enable usage-based limits and billing signals.
  • Gate chat/conversation endpoints behind authentication and move identity + usage accounting into the database-backed path.

Description

  • Implemented JWT auth core in server/src/core/auth.ts with issueAuthTokens, authenticateAccessToken, rotateRefreshToken, password hashing (bcryptjs), and requireAuth middleware that includes a legacy user_id fallback for compatibility.
  • Added auth endpoints in server/src/routes/auth.ts (/auth/register, /auth/login, /auth/refresh) and a /usage/me endpoint in server/src/routes/usage.ts.
  • Migrated data model and logic in server/src/core/db.ts to SQLite tables for users, refresh_tokens, and usage_events, plus helper functions recordUsage, getUsageSummary, and assertDailyQuotaAvailable; seeded demo users for development.
  • Updated chat and conversation handlers to read identity from req.auth, check daily quotas, apply rate limits, and record token usage (recordUsage) instead of the prior mock balance flow; adjusted types and request schemas accordingly.
  • Frontend changes in web/src/api.ts and web/src/App.tsx to support in-memory access/refresh tokens, automatic Authorization header injection with a 401->refresh retry, new login/register flows, usage display, and updated API calls (no client-side user_id fields).
  • Added types and minor UI wiring: server/src/types/express.d.ts, updated web/src/types.ts, and included new runtime dependencies (bcryptjs, jsonwebtoken) and dev types.

Testing

  • Ran server unit/integration tests with pnpm --filter llm-chat-server test and the server test suite passed (26 passed after changes).
  • Built server and web bundles with pnpm --filter llm-chat-server build and pnpm --filter llm-chat-web build, both completed successfully.
  • Ran web tests with pnpm --filter llm-chat-web test which currently fail because the test fixtures expect the previous unauthenticated conversation UI; the app now requires login before the full conversation workspace, so those assertions need to be updated (web tests: failing).

Codex Task

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ce8dff3b04

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/src/core/auth.ts Outdated
Comment on lines +150 to +153
const legacyUserId = (req.body?.user_id ?? req.query?.user_id) as string | undefined;
if (legacyUserId) {
const user = getUserById(db, legacyUserId);
if (!user || !user.active) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove unauthenticated user_id fallback in auth middleware

The requireAuth middleware accepts req.body.user_id/req.query.user_id when no Bearer token is present and treats that as authenticated identity. This reopens all protected routes (/chat, /chat/stream, /conversations, /usage) to unauthenticated impersonation by anyone who can guess a valid user id (the seeded ids are predictable), which defeats the JWT gate introduced in this change.

Useful? React with 👍 / 👎.

Comment thread server/src/core/auth.ts Outdated
}

function verifyToken(rawToken: string, secret: string): JWTPayload {
const parsed = jwt.verify(rawToken, secret, { issuer: 'llm-chat' }) as JWTPayload;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize JWT verification failures to 401

verifyToken directly propagates errors from jwt.verify, but those errors do not carry status/code. In this app there is no custom error-mapping middleware, so invalid or expired tokens can surface as generic 500 responses instead of an authentication failure, which breaks client refresh/re-login flow and misclassifies auth errors as server faults.

Useful? React with 👍 / 👎.

Comment thread server/src/routes/auth.ts Outdated
Comment on lines +34 to +37
const user = createUser(db, {
email: input.email,
passwordHash,
plan: input.plan

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject client-controlled plan on registration

The registration path forwards input.plan from the public request body straight into createUser. That lets any caller self-register as pro and immediately receive higher quota/entitlements without any privileged check, which is a direct authorization and billing-control bypass.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant