feat(GRO-565): Better Auth Phase 3 - password change, OIDC discovery, session cleanup, email verification #268
Reference in New Issue
Block a user
Delete Branch "feature/gro-565-better-auth-phase3"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
9cce0bcDefinition of Done
cc @cpfarhood
Deployed to groombook-dev
Images:
pr-268URL: https://dev.groombook.farh.net
Ready for UAT validation.
Deployed to groombook-dev
Images:
pr-268URL: https://dev.groombook.farh.net
Ready for UAT validation.
Security Review: PR #268 (GRO-565: Better Auth Phase 3)
Status: APPROVED with findings
Reviewed files: auth.ts, reminders.ts, AccountSettings.tsx, auth-client.ts
Finding 1: OIDC Discovery URL Injection (Medium)
Location:
apps/api/src/lib/auth.ts:183-224Issue:
discoveryUrlStris constructed via string interpolation fromproviderConfig.issuerUrl(a user-controlled environment variable). An attacker controllingissuerUrlcould set it to a malicious URL that returns a crafted OIDC configuration with attacker-controlledtoken_endpointanduserinfo_endpointURLs.When
internalBaseUrlis set, the code rewrites the token/userinfo hosts, but the initial fetch URL itself remains attacker-controlled. The discovery fetch happens before any validation, and the fetched config is used to set production OAuth endpoints.Risk: If an attacker can influence
issuerUrl(e.g., via environment variable injection or a misconfigured deployment), they could redirect token exchange and userinfo requests to an attacker-controlled endpoint, enabling token interception.Recommendation: Validate that
issuerUrluses an allowed scheme (https://only) and point to an allowlist of trusted OIDC providers before using it to construct discovery URLs.Finding 2: Password Change CSRF / Session Fixation (Low)
Location:
apps/web/src/portal/sections/AccountSettings.tsx:165Issue: The password change form requires
currentPasswordin the API call but the UI submit button is disabled only whennewPasswordis empty or passwords don't match. IfcurrentPasswordstate is somehow cleared or manipulated client-side before submission, the request still fires to the backend.The button condition:
Risk: Potential for session fixation or CSRF if an attacker can manipulate the form state. The backend correctly requires
currentPassword, but the frontend doesn't visually indicate thatcurrentPasswordis required until the user clicks submit and gets an error.Recommendation: Add client-side validation that visually marks
currentPasswordas required before submit is enabled, or requirecurrentPasswordin the button disabled condition.Finding 3: Email Verification Token in Plaintext (Info)
Location:
apps/api/src/lib/auth.ts:245-252The email verification sends the verification URL directly in plaintext:
Risk: If emails are intercepted (MITM, compromised mail server, logs), the verification token is exposed. However, this is standard practice for email verification flows.
Recommendation: Consider using a signed JWT or short-lived token format that doesn't grant indefinite access if intercepted. Ensure verification links expire promptly (Better Auth defaults should be checked).
Findings Not Applicable
exec()orspawn()calls in changed code.Verdict
APPROVED for production with findings above. The code is generally well-structured with proper input validation. Findings 1 and 2 should be addressed in follow-up PRs to harden the security posture. Rate limiting change from
memorytodatabaseis a security improvement for distributed deployments.cc @cpfarhood