Repository navigation
BE: Volunteer login + session - #57
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
natphua
approved these changes
Nov 11, 2026
natphua
left a comment
Collaborator
There was a problem hiding this comment.
Amazing work guys! No comments from me here - you did everything perfectly 😁
Thanks for being thorough with the PR and adding all the screenshots too; it really helps. Keep up the good work and I hope you were able to learn a lot about session cookies lol
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.
Describe your changes
Added volunteer login and sessions. A volunteer logs in with the email and cell phone saved when they were approved. If the pair matches an ACTIVE volunteer, they get a signed, httpOnly session cookie that holds only their id and lasts 7 days. Added
requireVolunteer()so later endpoints can find out which volunteer is calling, the same wayrequireAdmin()works for admins. Unknown emails, wrong phones, and INACTIVE volunteers all get the same 401 message, so the login form can't be used to find out which emails belong to volunteers.Partner: @nirvaango
Related Issue
Closes #40
Files changed
src/lib/volunteer-session.ts: creates, reads, and clears thevolunteer_sessioncookie. Signs a JWT holding{ volunteerId }withVOLUNTEER_SESSION_SECRETusing jose (HS256, 7 days). The cookie is httpOnly,securein production only, andsameSite: "lax".getVolunteerSessionId()returns null for a missing, tampered, malformed, or expired token, and throws if the secret is missing.src/lib/volunteer-auth.ts:requireVolunteer()reads the session and checks the database on every call. Returns{ volunteerId }for an ACTIVE volunteer, 401 for no session or a deleted volunteer, and 403 for an INACTIVE one.src/app/api/auth/volunteer/login/route.ts:POSTendpoint. Validates the body (400 if bad), normalizes the email and phone withnormalizeEmail/normalizeCellPhone, looks the volunteer up by theemail_cellPhonekey, and sets the session cookie. Returns{ volunteer: { id, firstName } }.src/app/api/auth/volunteer/logout/route.ts:POSTendpoint that clears the session cookie and returns{ ok: true }.How to test
Run
npx prisma db seed, addVOLUNTEER_SESSION_SECRETfrom Dev Resources to.env, and runnpm run dev. Then add a temporaryGET /api/volunteer-testroute that callsrequireVolunteer()(code is in the ticket) and test in Postman:GET /api/volunteer-test→ 401" Active.Volunteer@Example.com "and"(617) 555-0101"→ 200 with{ volunteer: { id, firstName } }and an HttpOnlyvolunteer_sessioncookieGET /api/volunteer-test→ 200{ volunteerId: "test-volunteer-active" }POST /api/auth/volunteer/logout→ 200, thenGET /api/volunteer-test→ 401active.volunteer@example.comwith6175550199→ 401Email or cell phone is incorrect.nobody@example.com→ 401, same messageinactive.volunteer@example.comwith6175550102→ 401, same message, no cookiecellPhone,cellPhoneas a number, body not JSON → 400 eachScreenshots
1. Not logged in → 401

2. Login → 200 + HttpOnly cookie


3. Logged in → 200

4. Logout → 200, then test route → 401


5. Wrong phone → 401

6. Unknown email → 401

7. Inactive volunteer → 401, no cookie


8. Bad bodies → 400


<img width="721" height="491" alt="Screenshot 2026-11-10 at 11 37 18 PM" src="https://github.com/user-attachments/assets/ae96030e-0
17a-43cf-a519-fd8fa5e3ac51" />
9. Tampered cookie

→ 401
10. INACTIVE after login → 403

Estimated Hours Spent
3 hours
Checklist before requesting a review