Skip to content

Feat: User invitation flow#111

Open
Ayush8923 wants to merge 46 commits intomainfrom
feat/invitation-flow
Open

Feat: User invitation flow#111
Ayush8923 wants to merge 46 commits intomainfrom
feat/invitation-flow

Conversation

@Ayush8923
Copy link
Copy Markdown
Collaborator

@Ayush8923 Ayush8923 commented Apr 8, 2026

Issue: #112

Summary:

  • Create the one invite page where user will be landed after invitation.
  • Function name updated for common purpose uses.

Summary by CodeRabbit

Release Notes

  • New Features
    • Added invite link verification flow—users can now access the platform via personalized invite links that automatically verify and log them in.
    • Implemented visual progress indicator during verification with automatic redirect to the evaluations dashboard on success.
    • Added error handling with retry options for invalid or expired invite links.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

📝 Walkthrough

Walkthrough

A new invite verification flow is introduced, enabling users to authenticate via email tokens. A client-side page component verifies tokens through an API route that forwards to a backend service. Upon successful verification, the user is logged in via a renamed context method and redirected to the evaluations dashboard.

Changes

Cohort / File(s) Summary
Invite Verification Feature
app/(auth)/invite/page.tsx, app/api/auth/invite/route.ts
New invite page component reads and verifies token query parameter via API call, manages verification/success/error UI states, and redirects on successful login. New API route extracts token from request, forwards to backend verification endpoint, and propagates response with cookies.
Authentication Context & Types
app/lib/context/AuthContext.tsx, app/lib/types/auth.ts
Renamed loginWithGoogle to loginWithToken in context provider and type definitions. Added InviteVerifyResponse interface for API response structure.
Component Updates
app/components/auth/LoginModal.tsx
Updated to call loginWithToken instead of loginWithGoogle after successful credential exchange.
Styling
app/globals.css
Added @keyframes progress CSS animation for width-based progress visualization.

Sequence Diagram

sequenceDiagram
    participant User as User/Browser
    participant Page as Invite Page
    participant API as Next.js API Route
    participant Backend as Backend Service
    participant Auth as Auth Context
    
    User->>Page: Visit /invite?token=xxx
    Page->>API: fetch /api/auth/invite?token=xxx
    API->>Backend: POST /api/v1/auth/invite/verify
    Backend-->>API: {success, data: {access_token, user}}
    API-->>Page: Response + Set-Cookie headers
    Page->>Auth: loginWithToken(access_token, user)
    Auth->>Auth: Update auth state
    Page->>Page: Show success + progress animation
    Page->>User: Redirect to /evaluations
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A token hops through digital space,
Verified swift with backend's grace,
Success blooms bright with progress so wide,
To evaluations the user will glide,
Authentication's invited to stay! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feat: User invitation flow' accurately reflects the main objective of the PR, which implements a complete user invitation flow including an invite verification page, API endpoint, authentication context updates, and supporting UI components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/invitation-flow

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Ayush8923 Ayush8923 self-assigned this Apr 8, 2026
Base automatically changed from feat/google-integration to feat/admin-flow April 9, 2026 11:05
Base automatically changed from feat/admin-flow to main April 10, 2026 05:08
@Ayush8923 Ayush8923 linked an issue Apr 10, 2026 that may be closed by this pull request
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/globals.css (1)

165-172: Remove or wire the unused progress keyframes.

The invite progress bar currently animates via inline width updates (app/(auth)/invite/page.tsx, Line 166-168), so this keyframes block is unused right now.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/globals.css` around lines 165 - 172, The `@keyframes` progress block in
globals.css is unused; either delete it or wire it up from the invite UI: remove
the `@keyframes` progress declaration if you prefer inline width updates, or
update the invite progress element in app/(auth)/invite/page.tsx to use the
keyframes by applying an animation (e.g., animation-name: progress plus
duration/timing) or a CSS class that references it so the keyframes actually
drive the progress bar instead of inline width styles.
app/(auth)/invite/page.tsx (1)

33-65: Prefer AbortController over a local cancel flag for the verification request.

React's documentation recommends either approach for effect cleanup, but AbortController is the preferred pattern because it actually cancels the in-flight request, avoiding unnecessary network work. The current local cancelled flag prevents stale state updates but leaves the request running.

♻️ Suggested refactor
-    let cancelled = false;
+    const controller = new AbortController();

     (async () => {
       try {
         const res = await fetch(
           `/api/auth/invite?token=${encodeURIComponent(token)}`,
-          { credentials: "include" },
+          { credentials: "include", signal: controller.signal },
         );

         const data: InviteVerifyResponse = await res.json();

-        if (cancelled) return;
-
         if (!res.ok || !data.success || !data.data) {
           setStatus("error");
           setError(data.error || "Invitation link is invalid or has expired.");
           return;
         }

         loginWithToken(data.data.access_token, data.data.user);
         setStatus("success");
-      } catch {
-        if (!cancelled) {
-          setStatus("error");
-          setError("Failed to verify invitation. Please try again.");
-        }
+      } catch (err) {
+        if (controller.signal.aborted) return;
+        setStatus("error");
+        setError("Failed to verify invitation. Please try again.");
       }
     })();

     return () => {
-      cancelled = true;
+      controller.abort();
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(auth)/invite/page.tsx around lines 33 - 65, Replace the local cancelled
flag with an AbortController: create a controller in the effect, pass
controller.signal into the fetch call that requests
`/api/auth/invite?token=${encodeURIComponent(token)}`, and call
controller.abort() in the cleanup function; in the catch block ignore/return on
an aborted request (check for an AbortError or error.name === "AbortError") so
you don't setStatus("error")/setError after abort, and only call
loginWithToken(...) and setStatus("success") when the fetch resolves
successfully and is not aborted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/api/auth/invite/route.ts`:
- Around line 21-28: The response returned from NextResponse.json (const res)
must include explicit no-cache headers to prevent caching of sensitive auth data
and forwarded cookies; after building res and before returning it (near
NextResponse.json, headers.getSetCookie, and res.headers.append usage), set
Cache-Control to "no-store, no-cache, must-revalidate, max-age=0" and add
complementary headers such as "Pragma: no-cache" and "Expires: 0" on res.headers
(using res.headers.set or equivalent) so the invite verification response is
non-cacheable.

---

Nitpick comments:
In `@app/`(auth)/invite/page.tsx:
- Around line 33-65: Replace the local cancelled flag with an AbortController:
create a controller in the effect, pass controller.signal into the fetch call
that requests `/api/auth/invite?token=${encodeURIComponent(token)}`, and call
controller.abort() in the cleanup function; in the catch block ignore/return on
an aborted request (check for an AbortError or error.name === "AbortError") so
you don't setStatus("error")/setError after abort, and only call
loginWithToken(...) and setStatus("success") when the fetch resolves
successfully and is not aborted.

In `@app/globals.css`:
- Around line 165-172: The `@keyframes` progress block in globals.css is unused;
either delete it or wire it up from the invite UI: remove the `@keyframes`
progress declaration if you prefer inline width updates, or update the invite
progress element in app/(auth)/invite/page.tsx to use the keyframes by applying
an animation (e.g., animation-name: progress plus duration/timing) or a CSS
class that references it so the keyframes actually drive the progress bar
instead of inline width styles.
🪄 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: 0e715f56-ff8e-4bdf-aee3-a9eff7bf5ace

📥 Commits

Reviewing files that changed from the base of the PR and between bc65a03 and 250769d.

📒 Files selected for processing (6)
  • app/(auth)/invite/page.tsx
  • app/api/auth/invite/route.ts
  • app/components/auth/LoginModal.tsx
  • app/globals.css
  • app/lib/context/AuthContext.tsx
  • app/lib/types/auth.ts

Comment on lines +21 to +28
const res = NextResponse.json(data, { status });

const setCookies = headers.getSetCookie?.() ?? [];
for (const cookie of setCookies) {
res.headers.append("Set-Cookie", cookie);
}

return res;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether auth route handlers set explicit no-store cache headers.
fd -p 'route.ts' app/api/auth | while read -r f; do
  echo "== $f =="
  rg -n "Cache-Control|Pragma|NextResponse\\.json|Set-Cookie" "$f"
done

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 740


🏁 Script executed:

cat -n app/api/auth/invite/route.ts

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 1247


🏁 Script executed:

# Check if there's middleware that might handle cache headers globally
find . -name "middleware.ts" -o -name "middleware.js" | head -5

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 58


🏁 Script executed:

# Search for any global cache-control patterns in the codebase
rg -l "Cache-Control|no-store" --type ts --type tsx | head -10

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 103


🏁 Script executed:

# Check other API routes to see if any set headers via NextResponse.json options
rg -n "NextResponse\.json.*headers" --type ts

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 58


🏁 Script executed:

# Look for NextResponse patterns with options to understand the API
rg -B1 -A3 "NextResponse\.json.*\{" app/api --type ts | head -50

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 2853


🏁 Script executed:

# Check if there's a Next.js config or any cache-related setup
cat next.config.js 2>/dev/null || cat next.config.ts 2>/dev/null || echo "No next.config found"

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 317


🏁 Script executed:

# Check the google and logout routes to see their full pattern
cat -n app/api/auth/google/route.ts

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 1419


🏁 Script executed:

# Search for any response.headers patterns to understand how headers are actually set
rg "response\.headers|res\.headers" app/api -A2 -B2 --type ts | head -80

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 1253


🏁 Script executed:

# Check if NextResponse constructor accepts headers option by looking for similar patterns
rg "NextResponse\.(json|text|redirect)" --type ts -A5 | grep -E "NextResponse|headers" | head -40

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 3002


🏁 Script executed:

# Look for NextResponse type definitions or usage with headers
rg "NextResponse" node_modules --type ts -A5 2>/dev/null | head -100

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 58


🏁 Script executed:

# Check if there's a tsconfig that might have type definitions
cat tsconfig.json 2>/dev/null | head -30

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 690


🏁 Script executed:

# Search for any example in the codebase where response object headers are set before construction
rg "headers\s*=" app/api --type ts -B3 -A3 | head -80

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 58


🏁 Script executed:

# Look at Next.js version to understand NextResponse API
cat package.json | grep -A5 -B5 "next"

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 972


🏁 Script executed:

# Check Next.js 16 documentation or search for ResponseInit usage
rg "ResponseInit|init\?" app/api --type ts -B2 -A2 | head -60

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 58


🏁 Script executed:

# Look for any headers being set in constructor anywhere in the codebase
rg "new NextResponse\|NextResponse\.(json|text)" --type ts -A8 | grep -E "NextResponse|headers" | head -50

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 58


🏁 Script executed:

# Verify the actual Next.js API: check if response headers can be set in constructor
python3 << 'EOF'
import json

# Next.js NextResponse API for version 16.0.7 analysis
# Based on Next.js documentation, ResponseInit includes:
# { status?: number; statusText?: string; headers?: HeadersInit }

# This means headers can be passed in the options object
# The proposed patch should work syntactically

print("NextResponse.json() signature in Next.js 16.0.7:")
print("- Accepts: (body, init?: ResponseInit)")
print("- ResponseInit includes: { status, headers, statusText }")
print("\nProposed patch syntax appears valid.")
print("\nThe concern raised in review is legitimate:")
print("- GET endpoint returns sensitive auth data")
print("- Returns Set-Cookie headers")
print("- Should be explicitly non-cacheable")
EOF

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 412


Add explicit no-cache headers to invite verification response.

This GET endpoint returns sensitive auth data and forwards session cookies. Responses should be marked non-cacheable to prevent browsers or proxies from caching the auth token/cookies.

🔧 Proposed patch
-    const res = NextResponse.json(data, { status });
+    const res = NextResponse.json(data, {
+      status,
+      headers: {
+        "Cache-Control": "no-store, private, max-age=0",
+        Pragma: "no-cache",
+      },
+    });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const res = NextResponse.json(data, { status });
const setCookies = headers.getSetCookie?.() ?? [];
for (const cookie of setCookies) {
res.headers.append("Set-Cookie", cookie);
}
return res;
const res = NextResponse.json(data, {
status,
headers: {
"Cache-Control": "no-store, private, max-age=0",
Pragma: "no-cache",
},
});
const setCookies = headers.getSetCookie?.() ?? [];
for (const cookie of setCookies) {
res.headers.append("Set-Cookie", cookie);
}
return res;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/auth/invite/route.ts` around lines 21 - 28, The response returned
from NextResponse.json (const res) must include explicit no-cache headers to
prevent caching of sensitive auth data and forwarded cookies; after building res
and before returning it (near NextResponse.json, headers.getSetCookie, and
res.headers.append usage), set Cache-Control to "no-store, no-cache,
must-revalidate, max-age=0" and add complementary headers such as "Pragma:
no-cache" and "Expires: 0" on res.headers (using res.headers.set or equivalent)
so the invite verification response is non-cacheable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Authentication: Magic link login flow

1 participant