Skip to content

Secure Login V2#16

Open
SynthicProgram wants to merge 9 commits into
mainfrom
secure-login
Open

Secure Login V2#16
SynthicProgram wants to merge 9 commits into
mainfrom
secure-login

Conversation

@SynthicProgram

Copy link
Copy Markdown
Collaborator

Auth: GitHub OAuth + password signup, hardened sessions

The old login compared passwords in plaintext and had dead Google/Apple buttons.
This replaces it with real auth: local email/password signup and GitHub OAuth,
both ending in the same server side session.

Server

GitHub OAuth at /auth/github, using passport-github2.

  • Links to an existing local account when the email matches.
  • Derives a free username when the GitHub login is already taken.
  • Rejects an email already owned by a different GitHub account.

Sessions via express-session + passport.

  • Cookie is httpOnly, sameSite=lax, secure in production.
  • Session id regenerates on every login, which closes session fixation.
  • Only the user id is serialized, so password_hash never reaches the store.

Passwords via bcrypt.

  • Hashed on signup, compared with bcrypt.compare on login.
  • Failures return one generic error, so the endpoint does not leak which emails
    are registered.
  • Signup validates email, username and password shape on the server, and returns
    a clean 409 on duplicates.

Other

  • CORS added, credentialed, restricted to CLIENT_URL.
  • Origins centralized in config/urls.js.
  • Final Express error handler added. A throw used to render a raw stack to the
    browser.
  • PGSSL=false escape hatch for a local Postgres that does not speak SSL.

Client

  • New /signup page with validation matching the server.
  • Login page has a GitHub button in place of the non functional OAuth buttons,
    and shows a readable message for ?error= codes from the callback.
  • Home revalidates the server session instead of trusting localStorage. The
    old check rendered the page as signed in when no live session existed.

Schema (breaking, requires a DB reset)

  • users.password_hash is now nullable.
  • users.github_id VARCHAR(255) UNIQUE added.
  • users_has_credential CHECK enforces at least one of the two.
  • Seed data holds plaintext demo passwords that resetDatabase.js hashes on insert.

Before running

  1. Copy Code/server/.env.example to .env.
  2. Generate SESSION_SECRET. The server refuses to start without it.
  3. Add GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET. The callback URL registered
    on the GitHub app must be exactly ${SERVER_URL}/auth/github/callback.
  4. Re run resetDatabase.js.

Note for reviewers

createUser and the OAuth verify callback both check then insert, so they lean on
the unique constraint as the real guarantee under concurrency. Both catch 23505 and
turn it into a clean 409 or account_conflict. Worth a second pair of eyes.

SynthicProgram and others added 9 commits July 26, 2026 11:53
main replaced config/reset.js with db/schema.sql + db/seedData.js +
db/resetDatabase.js and renamed every users column (id -> user_id,
password -> password_hash, points -> total_points, favorite_team moved
out to the followed_teams table). This branch's GitHub OAuth work was
built on the old names, so the merge needed more than a textual resolve.

Schema (db/schema.sql is now the single source of truth):
- password_hash is nullable — an OAuth account has no password of ours
- added github_id and access_token; reused main's profile_image_url for
  the GitHub avatar instead of adding a second column
- users_has_credential CHECK enforces password_hash OR github_id

Code:
- auth.js: githubid/avatarurl/accesstoken -> github_id/profile_image_url/
  access_token; linking a GitHub account to an existing local one no
  longer overwrites that user's chosen username
- usersController.js: main's column names + this branch's bcrypt compare;
  PUBLIC_COLUMNS keeps password_hash and access_token out of responses
- server.js: kept the passport/session/cors setup, updated deserializeUser
  to the new columns, session secret now reads SESSION_SECRET
- resetDatabase.js: bcrypt-hashes demo passwords instead of seeding
  plaintext, so `npm run reset` produces logins the API can verify
- .env.example: documented the GitHub OAuth / session vars

Verified: schema.sql applies cleanly against Postgres, the CHECK accepts
an OAuth-only row and rejects a credential-less one, and a seeded hash
round-trips through bcrypt.compare (all in a rolled-back transaction).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The merged schema made users.username NOT NULL UNIQUE, but the OAuth
verify callback wrote the GitHub login into that column unconditionally.
Any GitHub login matching a username already taken by a local signup
raised a unique violation and 500'd the callback
Add a /signup page wired to POST /api/users, which previously had no UI.
…ate a session cookie/secret in .env before bootup
@SynthicProgram SynthicProgram self-assigned this Jul 27, 2026
ShalomDee added a commit that referenced this pull request Jul 27, 2026
Frontend: core pages, Fixture Calendar, Team Follow, Profile Customization (excludes login, deferred to #16)
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.

1 participant