feat: add GitHub OAuth login and registration#44
Conversation
|
@SakethSumanBathini is attempting to deploy a commit to the Harsh Yadav's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
🎉 Thank you @SakethSumanBathini for your first PR to Gsecure!
We really appreciate your contribution 🙌
What happens next:
- 🔍 Maintainers will review your PR
- 🧪 Automated checks will run
- ✨ Feedback may be shared if needed
Please confirm your PR includes:
- ✔️ Clear summary of changes
- ✔️ Linked issue (e.g., Fixes #123)
- ✔️ Steps to test
- ✔️ Screenshots (for UI changes)
📘 Contribution Standards:
👉 https://github.com/HarshYadav152/Gsecure/blob/main/CONTRIBUTING.md
💬 Stay Connected with Our Community
💚 WhatsApp (Friendly Community)
For informal chats, quick help, and building friendships with contributors:
👉 https://chat.whatsapp.com/I8GYXd3mHlDCC2iXhNGeqV
🌟 Our Philosophy: We value both professional collaboration (Discord) and personal connections (WhatsApp). Join both to get the complete SS-Capture community experience!
Thanks for helping improve Gsecure 🚀
Let's build something amazing together! 💪
|
Warning Review limit reached
More reviews will be available in 49 minutes and 18 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR implements GitHub OAuth 2.0 authentication, adding social login as an alternative to credential-based signup and login. The feature spans configuration, database schema updates, OAuth authorization and callback endpoints, account linking/creation logic, and client-side UI integration with a post-auth success interstitial. ChangesGitHub OAuth Authentication
Sequence DiagramsequenceDiagram
participant Browser
participant LoginPage
participant GitHubAuth
participant GitHubAPI
participant CallbackHandler
participant MongoDB
participant SuccessPage
participant VaultPage
Browser->>LoginPage: User clicks "Continue with GitHub"
LoginPage->>GitHubAuth: GET /api/v1/auth/github
GitHubAuth->>GitHubAuth: Generate state UUID, set CSRF cookie
GitHubAuth->>Browser: Redirect to GitHub authorize URL
Browser->>GitHubAPI: User authorizes app
GitHubAPI->>Browser: Redirect to callback with code+state
Browser->>CallbackHandler: GET /api/v1/auth/github/callback?code=...&state=...
CallbackHandler->>CallbackHandler: Validate CSRF state cookie
CallbackHandler->>GitHubAPI: Exchange code for access token
CallbackHandler->>GitHubAPI: Fetch user profile (githubId, login)
CallbackHandler->>GitHubAPI: Fetch verified email
CallbackHandler->>MongoDB: Find/link/create user by githubId or email
MongoDB->>CallbackHandler: User record
CallbackHandler->>CallbackHandler: Generate authToken, set HTTP-only cookie
CallbackHandler->>Browser: Redirect to /success
Browser->>SuccessPage: Load /auth/success interstitial
SuccessPage->>SuccessPage: Retry /api/v1/auth/me with credentials
SuccessPage->>Browser: Update auth context (setUser, setAuthenticated)
SuccessPage->>VaultPage: Redirect to /vault
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@gsecure/app/api/v1/auth/github/callback/route.js`:
- Around line 82-105: The code currently falls back to emails[0] and
unconditionally assigns githubId to an account found by email; restrict the
email selection to verified addresses only (use primaryVerified || anyVerified
and do not use emails[0] as fallback) and when handling the email-match branch
in the account-linking logic (the User.findOne/email path) only attach githubId
if byEmail.githubId is null/undefined or already equals the current githubId—do
not overwrite an existing, different byEmail.githubId; if a conflict exists,
skip linking (or create a new user / return an error) instead of reassigning and
ensure you still lowercase email and call byEmail.save() only after setting
githubId safely.
- Around line 42-79: The three outbound fetches (tokenRes ->
fetch(GITHUB_TOKEN_URL), userRes -> fetch(GITHUB_USER_URL), emailsRes ->
fetch(GITHUB_EMAILS_URL)) need explicit timeouts: create an AbortController for
each call, start a setTimeout that calls controller.abort() after a reasonable
timeout (e.g. 5s or configurable), pass controller.signal into the fetch
options, and clear the timeout on success; also catch AbortError/failed fetches
and map them to the existing loginRedirect error paths (e.g. treat as token
exchange / profile fetch / emails fetch failure) so a stalled upstream cannot
hang the auth flow.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c05d11a2-59e2-4c62-826c-349d901f3997
📒 Files selected for processing (6)
gsecure/.env.local.examplegsecure/app/(auth)/login/page.jsgsecure/app/(auth)/register/page.jsgsecure/app/api/v1/auth/github/callback/route.jsgsecure/app/api/v1/auth/github/route.jsgsecure/lib/models/User.js
…guard and fetch timeouts
|
Thanks for the review! Addressed in the latest commit: Security (verified-email linking) — the callback now only ever trusts a Request timeouts — all three GitHub calls (token, /user, /user/emails) now Docstrings — added JSDoc to the exported handler and helpers. On the linked-issue note: this implements GitHub OAuth per your guidance |
HarshYadav152
left a comment
There was a problem hiding this comment.
@SakethSumanBathini All good.
Just some minor changes
- Currently you redirect to
/vaultafter oauth authentication but there is an issue in this when you redirect in some browsers or in some cases cookies not be read instantly it require a page refresh or a manual /vault visiting.
So for the solution of this add a page
/auth/success/so instead of redirect to /vault redirect user first to/auth/success/then make a request to/meroute for confirming cookies exist and then redirect to /vault.
DO these change and let me know.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@gsecure/app/auth/success/page.js`:
- Around line 35-63: The confirmSession retry loop can hang because fetch() has
no timeout; modify confirmSession to create an AbortController for each attempt,
pass controller.signal into fetch(`${apiHost}/api/v1/auth/me`, ...), and start a
timeout (e.g., via setTimeout) that calls controller.abort() after a defined
REQUEST_TIMEOUT_MS; ensure you clear the timeout on success/failure, catch the
abort error (treat as a failed attempt) so the loop continues to the next
attempt or falls back to /login, and keep the existing active checks, setStatus
updates, and router.replace('/vault') behavior intact.
- Around line 24-30: Remove the useRef-based "startedRef" run-once guard and its
import usage (drop useRef from the import) so the effect in useEffect that calls
confirmSession() can run on the second StrictMode pass; instead rely on the
existing active flag/cleanup inside confirmSession() to prevent duplicate state
updates. In addition, add a per-attempt timeout/AbortController to the fetch()
calls targeting "/api/v1/auth/me" inside confirmSession() (and any retry loop)
so each fetch is aborted after a reasonable timeout and the retry logic can
proceed; ensure the AbortController is cleaned up in the effect cleanup so
in-flight requests are cancelled on unmount.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6df6e5a5-6853-4f07-84c5-1139f6504657
📒 Files selected for processing (2)
gsecure/app/api/v1/auth/github/callback/route.jsgsecure/app/auth/success/page.js
🚧 Files skipped from review as they are similar to previous changes (1)
- gsecure/app/api/v1/auth/github/callback/route.js
@HarshYadav152 Done — pushed. |
…AbortController timeout
|
@HarshYadav152 Everything's addressed — the /auth/success page is pushed and both CodeRabbit issues (StrictMode guard + per-fetch timeout) are resolved and marked closed by CodeRabbit. Ready for your approval whenever you get a chance! |
|
Hi @HarshYadav152 — all your requested changes are addressed in the latest commit 95254d8. The /auth/success page is live, both CodeRabbit issues (StrictMode guard + per-fetch timeout) are resolved. Could you take a re-look whenever you get a chance? Thanks! |
|
@SakethSumanBathini All good just a bit tweek. |
Done — /auth/success is now at app/(auth)/success/page.js, consistent with login/register/forgot-password. Since (auth) is a route group the URL becomes /success, so I've updated the OAuth callback redirect from /auth/success to /success in the same commit. No other references to /auth/success remain. |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
gsecure/app/api/v1/auth/github/callback/route.js (2)
157-163: 💤 Low valueConsider capping username deduplication iterations.
The
whileloop has no upper bound. While collisions are statistically improbable due to the random suffix, a defensive cap (e.g., 10 iterations) would prevent theoretical runaway loops and make intent clearer.🛡️ Suggested defensive cap
let username = login.toLowerCase(); let attempt = 0; - while (await User.findOne({ username })) { + const MAX_USERNAME_ATTEMPTS = 10; + while (await User.findOne({ username })) { + if (attempt >= MAX_USERNAME_ATTEMPTS) { + return loginRedirect('github_username_conflict'); + } attempt += 1; username = attempt === 1 ? `${login.toLowerCase()}-${githubId.slice(-4)}` : `${login.toLowerCase()}-${Math.random().toString(36).slice(2, 8)}`; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@gsecure/app/api/v1/auth/github/callback/route.js` around lines 157 - 163, The username deduplication while loop using User.findOne has no iteration cap; add a MAX_USERNAME_ATTEMPTS constant (e.g., 10) and increment attempt on each iteration, breaking/throwing a clear error (or returning a 500 response) if attempt >= MAX_USERNAME_ATTEMPTS to avoid a runaway loop; keep the existing username-generation logic (using githubId.slice and random suffix) but ensure you check attempt against the cap before continuing the loop and surface a descriptive failure from the surrounding handler so callers can handle the error.
182-187: ⚡ Quick winAdd explicit
path: '/'to the authToken cookie for consistency.The state cookie (line 194) explicitly sets
path: '/', and the kickoff route's state cookie also uses explicit path. While Next.js likely defaults to/, being explicit ensures the cookie is readable by/api/v1/auth/meand the middleware across all paths, and maintains consistency with the other cookie in this file.🔧 Suggested fix
response.cookies.set('authToken', authToken, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict', maxAge: 60 * 60 * 24 * 7, // 7 days + path: '/', });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@gsecure/app/api/v1/auth/github/callback/route.js` around lines 182 - 187, The authToken cookie set via response.cookies.set('authToken', authToken, {...}) is missing an explicit path option; update the cookie options passed to response.cookies.set in the callback route to include path: '/' (matching the state cookie and kickoff route) so the token is accessible to /api/v1/auth/me and middleware across all paths and to keep behavior consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@gsecure/app/api/v1/auth/github/callback/route.js`:
- Around line 157-163: The username deduplication while loop using User.findOne
has no iteration cap; add a MAX_USERNAME_ATTEMPTS constant (e.g., 10) and
increment attempt on each iteration, breaking/throwing a clear error (or
returning a 500 response) if attempt >= MAX_USERNAME_ATTEMPTS to avoid a runaway
loop; keep the existing username-generation logic (using githubId.slice and
random suffix) but ensure you check attempt against the cap before continuing
the loop and surface a descriptive failure from the surrounding handler so
callers can handle the error.
- Around line 182-187: The authToken cookie set via
response.cookies.set('authToken', authToken, {...}) is missing an explicit path
option; update the cookie options passed to response.cookies.set in the callback
route to include path: '/' (matching the state cookie and kickoff route) so the
token is accessible to /api/v1/auth/me and middleware across all paths and to
keep behavior consistent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3b2facab-8b8e-435e-aa4b-a07874cd4a90
📒 Files selected for processing (2)
gsecure/app/(auth)/success/page.jsgsecure/app/api/v1/auth/github/callback/route.js
Addressed both CodeRabbit points — added a MAX_USERNAME_ATTEMPTS = 10 cap to the username deduplication loop so it returns a clear username_generation_failed error rather than running indefinitely, and added path: '/' to the authToken cookie for consistency with the state cookie. Both are valid defensive improvements. |
Description
Adds "Continue with GitHub" OAuth to both the login and register pages, so
users can authenticate without a username/password.
The flow is fully server-side:
GET /api/v1/auth/githubsets a short-lived CSRFstatecookie (sameSitelax so it survives GitHub's redirect) and redirects to GitHub's authorize
screen with
read:user user:emailscope.GET /api/v1/auth/github/callbackvalidates the state, exchanges the codefor an access token, fetches the GitHub profile, and resolves a verified
primary email server-side (with a deterministic GitHub no-reply fallback so
the required email field is always satisfied).
authTokencookiethe password login uses, redirecting to
/vault.Account linking prevents duplicates:
githubIdgithubIdlinked onto itde-duplicated against the unique index)
Because the existing
AuthContextalready hydrates from/api/v1/auth/meonmount, no client-side auth logic needed to change.
Fixes #13
Type of change
How Has This Been Tested?
npx eslint .— no new warnings or errors vs the pre-existing baselinevalidateSync): GitHub account withoutpassword/keyword validates; local account missing either is still rejected
username de-duplication, link-vs-create branch (17/17 pass)
To run locally: register a GitHub OAuth App, copy the three
GITHUB_OAUTH_*values from
.env.local.exampleinto.env.local, set callback tohttp://localhost:3000/api/v1/auth/github/callback, then click"Continue with GitHub" on the login or register page.
Checklist:
Summary by CodeRabbit
New Features
Chores