Skip to content

[Auth] Migrate registration, login, and profile flows to the Node.js API#19

Merged
Josan88 merged 5 commits intomainfrom
copilot/migrate-auth-and-profile-flows
May 8, 2026
Merged

[Auth] Migrate registration, login, and profile flows to the Node.js API#19
Josan88 merged 5 commits intomainfrom
copilot/migrate-auth-and-profile-flows

Conversation

Copy link
Copy Markdown

Copilot AI commented May 7, 2026

Replaces the legacy PHP/MySQL auth layer (plaintext passwords, email enumeration, unauthenticated profile access) with a Node.js/Express + PostgreSQL implementation backed by bcrypt and JWT.

Backend — new endpoints at /resources/api_user.php

  • POST — register (name + email + password) or login (email + password); registration branches on field presence so an empty name returns 400 instead of falling through to login; passwords hashed with bcrypt (12 rounds); login returns {id, name, email, token} (24 h JWT)
  • GET /id/:id — fetch own profile; requires `Authorization: ******; 403 if token user ≠ path user; 404 if user not found
  • PUT /id/:id — update name, email (duplicate-checked), and/or password (re-hashed); same auth guard; returns real rowCount and 404 when no row is updated
  • Not implemented: ?getAllEmails=true and ?email=… — email enumeration and unauthenticated lookup are intentionally dropped
  • Rate limiting: 20 req/15 min on auth, 100 req/15 min on profile endpoints

New files: backend/src/db.js (pg pool via DATABASE_URL), backend/src/routes/users.js. Added bcryptjs, jsonwebtoken, pg, express-rate-limit dependencies.

Infrastructure

  • compose.yml: JWT_SECRET is now wired into the backend service environment with a required-variable guard (${JWT_SECRET:?...}); docker compose up will fail fast with a clear message if it is not set
  • .env.example: documents JWT_SECRET with a generation command

Frontend

  • app-register.js: sends name instead of username; age and gender fields removed (not in new schema)
  • app-login.js: stores full {id, name, email, token} response in authState.user (persisted to sessionStorage via existing watcher in app.js); fallback error message corrected to "Email or password incorrect"
  • app-login.js: "Forgot Password?" link replaced with an informational note directing users to change their password from their profile after logging in
  • app-profile.js: attaches Authorization: Bearer header on fetch/update; UI adapted to name/email-only schema
  • app-reset-password.js: requires the user to be authenticated; guards submitNewPassword() against undefined userId/token (surfaces a clear message if auth state is missing); uses the JWT token to PUT the new password; removes the two-step email-enumeration → unauthenticated reset flow entirely

Tests

Backend test suite expanded from 8 → 24 tests (all using t.mock.method on db.query, no real database required), covering:

  • Registration success, duplicate-email 409, empty-name 400
  • Login + JWT issuance and signature verification, wrong password 401, user-not-found 401
  • GET /id/:id — 200 with own profile, 403 mismatched sub, 404 user not found
  • PUT /id/:id — name/password update 200, no-fields 400, short password 400, empty name 400, 404 when user deleted, email-conflict 409

Example login flow

// POST /resources/api_user.php  { email, password }
// → { id: "uuid", name: "Alice", email: "alice@example.com", token: "eyJ..." }

// Subsequent profile fetch
fetch(`resources/api_user.php/id/${user.id}`, {
  headers: { Authorization: `****** }
});

Copilot AI and others added 2 commits May 7, 2026 06:19
- Add bcryptjs, jsonwebtoken, pg dependencies to backend
- Create backend/src/db.js (PostgreSQL pool via DATABASE_URL)
- Create backend/src/routes/users.js with:
  - POST /resources/api_user.php: register (bcrypt hash) or login (JWT)
  - GET  /resources/api_user.php/id/:id: fetch own profile (Bearer auth)
  - PUT  /resources/api_user.php/id/:id: update profile/password (Bearer auth)
  - Email enumeration (getAllEmails) and unauthenticated lookup not implemented
- Mount user routes in backend/src/index.js
- Add DATABASE_URL and JWT_SECRET to backend/.env.example
- Expand test suite in backend/src/index.test.js (8 tests, all passing)
- Update js/components/app-register.js: use name field, remove age/gender
- Update js/components/app-login.js: handle new {id,name,email,token} response
- Update js/components/app-profile.js: send Authorization header, adapt to new schema
- Update js/components/app-reset-password.js: require auth, remove email-enumeration calls

Agent-Logs-Url: https://github.com/Josan88/BookRunner/sessions/75df8ea5-09c5-4995-b931-d149817a40e2

Co-authored-by: Josan88 <124897328+Josan88@users.noreply.github.com>
…on profile update

- Apply express-rate-limit to auth (20 req/15 min) and profile endpoints (100 req/15 min)
- Use String() coercion for JWT sub vs URL param comparison
- Validate name/email are non-empty on PUT, hash-validate password length
- Check for duplicate email when updating a user's email address

Agent-Logs-Url: https://github.com/Josan88/BookRunner/sessions/75df8ea5-09c5-4995-b931-d149817a40e2

Co-authored-by: Josan88 <124897328+Josan88@users.noreply.github.com>
Copilot AI changed the title [WIP] Migrate registration, login, and profile flows to the new API [Auth] Migrate registration, login, and profile flows to the Node.js API May 7, 2026
Copilot finished work on behalf of Josan88 May 7, 2026 06:23
Copilot AI requested a review from Josan88 May 7, 2026 06:23
@Josan88 Josan88 marked this pull request as ready for review May 7, 2026 06:23
Copilot AI review requested due to automatic review settings May 7, 2026 06:23
Copy link
Copy Markdown

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

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: a910b12deb

ℹ️ 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 on lines +37 to +39
const secret = process.env.JWT_SECRET;
if (!secret) throw new Error('JWT_SECRET is not configured');
return jwt.sign({ sub: String(user.id), email: user.email }, secret, { expiresIn: '24h' });
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 Configure JWT_SECRET for the compose backend

In the Docker Compose flow I inspected (compose.yml backend environment), JWT_SECRET is not passed to the backend, so after a user registers successfully, any successful login reaches signToken() with no secret and throws before returning a token. That makes login/profile access fail out of the box unless operators discover and add an undocumented root-level env value; either wire JWT_SECRET into the compose service or provide a safe startup/default behavior.

Useful? React with 👍 / 👎.

Comment thread js/components/app-reset-password.js Outdated
Comment on lines +84 to +86
<template v-if="!authState.isLoggedIn">
<div class="alert alert-warning text-center">
Please <router-link to="/login" class="alert-link">log in</router-link> to reset your password.
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 Keep forgot-password usable for logged-out users

When users follow the unchanged Forgot Password? link on the login form, they are necessarily logged out, but this new branch only tells them to log in before resetting. That removes the actual forgot-password path for anyone who cannot authenticate with their current password; either keep an unauthenticated reset flow or relabel/move this page as an authenticated password-change screen.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates the app’s registration, login, profile, and password reset flows away from the legacy PHP/MySQL auth endpoints to a Node.js/Express API backed by PostgreSQL, bcrypt password hashing, JWT auth, and rate limiting.

Changes:

  • Added Node/Express auth + profile endpoints at /resources/api_user.php (POST register/login, authenticated GET/PUT profile) with bcrypt + JWT and per-route rate limiting.
  • Updated frontend auth-related components to use {id, name, email, token} and attach Authorization: Bearer <token> for profile/password updates.
  • Expanded backend test coverage for basic input validation and auth-required behavior (401/400 cases).

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
js/components/app-reset-password.js Replaces legacy email-enumeration reset flow with authenticated password update via JWT.
js/components/app-register.js Updates registration payload to send name (and removes old schema fields like age/gender).
js/components/app-profile.js Fetches/updates profile via authenticated Node API and adapts UI to name/email-only schema.
js/components/app-login.js Stores full user object (incl. token) in authState and improves error handling.
backend/src/routes/users.js Implements auth/profile endpoints with bcrypt hashing, JWT verification, and rate limiting.
backend/src/index.test.js Adds tests for new endpoint validation/auth-guard behavior without requiring a DB.
backend/src/index.js Mounts the new user routes in the Express app.
backend/src/db.js Adds a pg Pool initialized from DATABASE_URL.
backend/package.json Adds dependencies for pg, bcryptjs, jsonwebtoken, and express-rate-limit.
backend/package-lock.json Locks newly added backend dependencies.
backend/.env.example Documents new required env vars: DATABASE_URL and JWT_SECRET.
Files not reviewed (1)
  • backend/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +33 to +41
const userId = this.authState.user?.id;
const token = this.authState.user?.token;

fetch(`resources/api_user.php/id/${userId}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
Comment thread backend/src/routes/users.js Outdated
Comment on lines +64 to +72
const { email, password } = req.body ?? {};
const name = req.body?.name ?? req.body?.username;

if (!email || !password) {
return res.status(400).json({ error: 'Email and password are required' });
}

// --- REGISTRATION ---
if (name !== undefined && name !== null && name !== '') {
Comment on lines +77 to +80
// Duplicate email check
const existing = await db.query('SELECT id FROM users WHERE email = $1', [email]);
if (existing.rows.length > 0) {
return res.status(409).json({ error: 'Email already registered' });
Comment thread backend/src/routes/users.js Outdated
Comment on lines +184 to +189
await db.query(
`UPDATE users SET ${setClauses.join(', ')} WHERE id = $${values.length}`,
values,
);

return res.status(200).json({ success: true, affected_rows: 1 });
Comment thread js/components/app-login.js Outdated
if (!data) {
this.msg = "Username or password incorrect.";
if (!data || data.error) {
this.msg = data?.error || "Username or password incorrect.";
Comment thread backend/src/index.test.js
Comment on lines +41 to +44
// ---------------------------------------------------------------------------
// POST /resources/api_user.php – input validation (no database required)
// ---------------------------------------------------------------------------

Copy link
Copy Markdown
Owner

@Josan88 Josan88 left a comment

Choose a reason for hiding this comment

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

Reviewed the auth migration. I would not merge this as-is.

Blocking issues:

  1. JWT_SECRET is not wired into the Docker Compose backend environment. The documented docker compose up --build path starts the backend without the secret, so successful login reaches signToken() and fails before returning a JWT. docker compose config confirms only DATABASE_URL, HOST, NODE_ENV, and PORT are passed.

  2. The unchanged login-page Forgot Password? link now points to a page that requires the user to already be logged in. That removes password recovery for users who cannot authenticate. Either keep a real unauthenticated recovery flow, or rename/move this to an authenticated password-change screen.

  3. PUT /resources/api_user.php/id/:id ignores the UPDATE result and always returns { success: true, affected_rows: 1 }, even if no row was updated. Capture rowCount and return 404 or the real affected count.

  4. The test suite passes, but it does not cover the main new behavior: successful register/login, JWT issuance, authenticated profile GET/PUT, duplicate email handling, or password update. These paths should be covered before merging an auth rewrite.

Verification run locally on the PR branch:

cd backend
npm ci
npm test

Result: 8/8 tests passed.

@Josan88
Copy link
Copy Markdown
Owner

Josan88 commented May 7, 2026

Reviewed the auth migration. I would not merge this as-is.

Blocking issues:

  1. JWT_SECRET is not wired into the Docker Compose backend environment. The documented docker compose up --build path starts the backend without the secret, so successful login reaches signToken() and fails before returning a JWT. docker compose config confirms only DATABASE_URL, HOST, NODE_ENV, and PORT are passed.
  2. The unchanged login-page Forgot Password? link now points to a page that requires the user to already be logged in. That removes password recovery for users who cannot authenticate. Either keep a real unauthenticated recovery flow, or rename/move this to an authenticated password-change screen.
  3. PUT /resources/api_user.php/id/:id ignores the UPDATE result and always returns { success: true, affected_rows: 1 }, even if no row was updated. Capture rowCount and return 404 or the real affected count.
  4. The test suite passes, but it does not cover the main new behavior: successful register/login, JWT issuance, authenticated profile GET/PUT, duplicate email handling, or password update. These paths should be covered before merging an auth rewrite.

Verification run locally on the PR branch:

cd backend
npm ci
npm test

Result: 8/8 tests passed.

@copilot

Agent-Logs-Url: https://github.com/Josan88/BookRunner/sessions/c6df6a62-9aea-4d42-97c7-37ef44948af7

Co-authored-by: Josan88 <124897328+Josan88@users.noreply.github.com>
@Josan88
Copy link
Copy Markdown
Owner

Josan88 commented May 8, 2026

@copilot try again

Copy link
Copy Markdown
Author

Copilot AI commented May 8, 2026

@copilot try again

All four blocking issues are addressed in commit 35f8887:

  1. JWT_SECRET wired in Compose — added JWT_SECRET: ${JWT_SECRET:?JWT_SECRET must be set – see .env.example} to the backend service in compose.yml, and documented it in .env.example.
  2. "Forgot Password?" link removed — replaced with an informational note ("To change your password, visit your profile after logging in.") so unauthenticated users aren't pointed at a page that requires login.
  3. PUT now returns real rowCount — captures the UPDATE result and returns 404 when rowCount === 0.
  4. Test suite expanded from 8 → 24 tests — new tests cover: registration success, duplicate-email 409, login + JWT issuance and verification, wrong password, user-not-found, GET profile 200/403/404, PUT profile name/password updates, no-fields 400, short password 400, PUT 404 when user deleted, and email-conflict 409 on update. All 24 pass (npm test).

Copilot finished work on behalf of Josan88 May 8, 2026 15:22
@Josan88 Josan88 merged commit 977f3bb into main May 8, 2026
6 checks passed
@Josan88 Josan88 deleted the copilot/migrate-auth-and-profile-flows branch May 8, 2026 15:53
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.

[Auth] Migrate registration, login, and profile flows to the new API

3 participants