-
Notifications
You must be signed in to change notification settings - Fork 70
Implementaion of fully functional Authentication System for frontend #132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughImplements Supabase email/password authentication: adds a Supabase client and env placeholders, integrates session handling and auth listeners in App, adds signup/login/forgot/reset pages and routing, updates LandingPage navigation, and includes a small editor config file. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant App
participant Supabase
participant Backend
rect rgba(225,245,254,0.5)
note over App: Init - check session & subscribe
App->>Supabase: auth.getSession()
Supabase-->>App: session|null
App->>App: set isAuthenticated
App->>Supabase: auth.onAuthStateChange(cb)
end
rect rgba(232,245,233,0.5)
note over User,App: Login
User->>App: submit email/password
App->>Supabase: signInWithPassword(credentials)
alt success
Supabase-->>App: session
App-->>User: toast success, navigate("/")
else error
Supabase-->>App: error
App-->>User: toast error
end
end
rect rgba(252,228,236,0.5)
note over User,App: Signup
User->>App: submit signup form
App->>Supabase: signUp({email,password},{emailRedirectTo})
alt success
Supabase-->>App: confirmation
App-->>User: show "check inbox"
else error
Supabase-->>App: error
App-->>User: toast error
end
end
rect rgba(255,249,196,0.5)
note over User,App: Forgot / Reset password
User->>App: request reset (email)
App->>Supabase: resetPasswordForEmail(email,{redirectTo})
Supabase-->>App: ack/error
App-->>User: confirmation or error
User->>App: open magic link (tokens/code)
App->>Supabase: setSession / exchangeCodeForSession
alt valid
Supabase-->>App: session set
User->>App: submit new password
App->>Supabase: updateUser({password})
Supabase-->>App: ack/error
App-->>User: toast + navigate
else invalid
App-->>User: toast error, navigate /login
end
end
rect rgba(224,242,241,0.5)
note over User,Backend: Landing -> Repo fetch
User->>App: submit repo query
App->>Backend: POST /api/repo-stats
Backend-->>App: repo data
App-->>User: setRepoData, toast, navigate("/dashboard")
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (21)
.vimrc (2)
1-1: Invalid Vim setting; use standard directive or drop file
syntax-highlighting=Trueisn’t a valid Vim command. If you need editor config in-repo, prefersyntax on; otherwise remove this file to avoid noise in the root.Apply:
-syntax-highlighting=True +syntax on
3-3: Stray character in fileThe lone
3looks accidental. Remove it.-3 +frontend/.env example (1)
1-3: Fix .env example formatting and placeholders; consider standard filename
- Remove spaces around
=.- Use realistic, copy-pastable values without spaces (or quote them).
- Consider renaming file to
.env.example(convention).-VITE_SUPABASE_URL=YOUR SUPABASE URL -VITE_BASE_URL = http://localhost:5173/ -VITE_SUPABASE_KEY=YOUR SUPABASE ANON KEY +VITE_SUPABASE_URL=https://YOUR-PROJECT.supabase.co +VITE_BASE_URL=http://localhost:5173 +VITE_SUPABASE_KEY=YOUR-ANON-KEYfrontend/src/lib/supabaseClient.ts (2)
7-9: Throwing at import time can break buildsThrowing during module eval will crash the app immediately (including static builds/tests). Prefer failing fast during app bootstrap or surfacing a user-facing error without crashing the entire bundle.
-if (!supabaseUrl || !supabaseAnonKey) { - throw new Error('Missing Supabase configuration. Please set VITE_SUPABASE_URL and VITE_SUPABASE_KEY environment variables.'); -} +if (!supabaseUrl || !supabaseAnonKey) { + // In dev, crash loudly; in production, log and render a friendly screen upstream. + if (import.meta.env.DEV) { + throw new Error('Missing Supabase configuration. Set VITE_SUPABASE_URL and VITE_SUPABASE_KEY.'); + } else { + // eslint-disable-next-line no-console + console.error('Missing Supabase configuration. Auth features will not work.'); + } +}
12-12: Add typed client for safer queriesIf you generate Supabase types, pass them to
createClientfor end-to-end type safety.-export const supabase = createClient(supabaseUrl, supabaseAnonKey) +// import type { Database } from '../../types/supabase'; // generated via supabase CLI +export const supabase = createClient/*<Database>*/(supabaseUrl, supabaseAnonKey);I can add a types generation step and wire it up if desired.
frontend/src/components/landing/LandingPage.tsx (1)
23-27: Avoid hard-coded backend URL; validate input URL
- Replace
http://localhost:8000with an env-driven base URL.- Add basic URL validation before calling the API.
- const response = await axios.post('http://localhost:8000/api/repo-stats', { repo_url: repoUrl }); + try { new URL(repoUrl) } catch { toast.error('Enter a valid GitHub repository URL.'); return; } + const apiBase = import.meta.env.VITE_API_URL || '/api'; + const response = await axios.post(`${apiBase}/repo-stats`, { repo_url: repoUrl });I can add
VITE_API_URLto the env example and centralize an axios instance.frontend/src/components/pages/LoginPage.tsx (3)
1-1: Import React types explicitly to avoid “Cannot find namespace 'React'”Use named type imports instead of
React.*type references without a namespace import.-import { useState, ReactNode, FormEvent } from "react"; +import { useState } from "react"; +import type { ElementType, InputHTMLAttributes, ReactNode, FormEvent } from "react";And update the interfaces:
-interface InputFieldProps extends React.InputHTMLAttributes<HTMLInputElement> { - icon: React.ElementType; +interface InputFieldProps extends InputHTMLAttributes<HTMLInputElement> { + icon: ElementType; }
54-74: Harden auth call and fix user-facing typoWrap Supabase call in try/catch and correct “occured” → “occurred”.
- const handleLogin = async (e: FormEvent<HTMLFormElement>) => { + const handleLogin = async (e: FormEvent<HTMLFormElement>) => { e.preventDefault(); const formData = new FormData(e.currentTarget); const email = formData.get('email') as string; const password = formData.get('password') as string; setIsLoading(true); - const {data,error} = await supabase.auth.signInWithPassword({ - email: email, - password: password, - }); - setIsLoading(false); - if(data && !error){ - toast.success('Successfully logged in!'); - onLogin(); - navigate('/'); - } - else - { - toast.error(error?.message ||"An Unknown error occured!"); - } + try { + const { data, error } = await supabase.auth.signInWithPassword({ email, password }); + if (error) { + toast.error(error.message || 'An unknown error occurred!'); + return; + } + toast.success('Successfully logged in!'); + onLogin(); + navigate('/'); + } catch (err) { + toast.error('Network error. Please try again.'); + } finally { + setIsLoading(false); + } };
106-118: “Remember me” is currently a no-opSupabase persists sessions by default; either wire this to a behavior (e.g., toggle persist) or remove it to reduce confusion.
frontend/src/components/pages/ForgotPasswrdPage.tsx (2)
1-1: Import React types explicitlyAvoid
React.*type references without an import.-import { useState, ReactNode, FormEvent } from "react"; +import { useState } from "react"; +import type { ElementType, InputHTMLAttributes, ReactNode, FormEvent } from "react";And adjust:
-interface InputFieldProps extends React.InputHTMLAttributes<HTMLInputElement> { - icon: React.ElementType; +interface InputFieldProps extends InputHTMLAttributes<HTMLInputElement> { + icon: ElementType; }
49-49: Typo in component/file nameConsider renaming to
ForgotPasswordPagefor consistency and discoverability.Please confirm route and import paths if you rename the file and component.
frontend/src/components/pages/ResetPasswordPage.tsx (4)
98-101: Grammar: fix mismatch messageSmall UX polish.
- toast.error("Passwords doesn't match. Try Again"); + toast.error("Passwords don't match. Try again.");
150-166: Add accessibility and autofill hints on password fieldsImprove UX and a11y for password managers and screen readers.
<InputField icon={Key} type="password" name="password-1" - className="mb-7" + className="mb-7" + autoComplete="new-password" + aria-label="New password" placeholder="New Password" required /> <InputField icon={Key} type="password" name="password-2" className="mb-7" + autoComplete="new-password" + aria-label="Re-enter new password" placeholder="Reenter Password" required />
122-136: Success redirect: confirm desired post-reset behaviorCurrently you keep the session and navigate to "/". If security policy requires re-authentication after password change, sign out and redirect to "/login" instead.
Proposed variant:
- toast.success("Password updated successfully."); - navigate("/"); + toast.success("Password updated successfully."); + // If you prefer re-login after reset: + // await supabase.auth.signOut(); + // navigate("/login", { replace: true }); + navigate("/", { replace: true });
5-5: Nit: import spacingConsistent formatting with other files.
-import { toast} from "react-hot-toast"; +import { toast } from "react-hot-toast";frontend/src/components/pages/SignUpPage.tsx (3)
64-67: Grammar: fix mismatch message + spacingPolish the toast copy.
- toast.error("Passwords doesn't match.Try Again"); + toast.error("Passwords don't match. Try again.");
126-154: Add accessibility and autofill hintsImprove UX and a11y.
<InputField icon={User} name="name" + autoComplete="username" + aria-label="Username" placeholder="Username" required /> <InputField icon={Mail} type="email" name="email" + autoComplete="email" + aria-label="Email address" placeholder="Email address" required /> <InputField icon={Lock} type="password" name="password-1" + autoComplete="new-password" + aria-label="Password" placeholder="Password" required /> <InputField icon={Lock} type="password" name="password-2" + autoComplete="new-password" + aria-label="Re-enter password" placeholder="Reenter Password" required />
96-97: EnsureVITE_BASE_URLis whitelisted and add a fallback
- Confirm
import.meta.env.VITE_BASE_URLis listed under Supabase Auth → Redirect URLs.- (Optional) Mirror the fallback used in
ForgotPasswrdPage.tsx:frontend/src/components/pages/SignUpPage.tsx:96-97 - emailRedirectTo: import.meta.env.VITE_BASE_URL, + emailRedirectTo: import.meta.env.VITE_BASE_URL || window.location.origin,frontend/src/App.tsx (3)
38-69: Tighten subscription handling; simplify cleanupUse the canonical destructuring to avoid double “subscription.subscription”.
- const { data: subscription } = supabase.auth.onAuthStateChange( + const { data: { subscription } } = supabase.auth.onAuthStateChange( (event, session) => { console.log("Auth event:", event, session); switch (event) { case "SIGNED_IN": setIsAuthenticated(true); toast.success("Signed in!"); break; @@ } } ); - return () => { - subscription.subscription.unsubscribe(); - }; + return () => subscription.unsubscribe();
29-37: Optional: avoid initial auth flickerConsider an “authReady” state to prevent momentary redirects before getSession resolves.
High-level idea:
- const [authReady, setAuthReady] = useState(false);
- After getSession(), setAuthReady(true);
- Gate routes on authReady to avoid flashing Login.
114-121: Optional: drop onLogin prop and rely on Supabase eventsSince auth state is driven by Supabase, passing onLogin can mask failed logins if misused by the page. Prefer relying solely on SIGNED_IN event.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (11)
.vimrc(1 hunks)frontend/.env example(1 hunks)frontend/package.json(1 hunks)frontend/src/App.tsx(3 hunks)frontend/src/components/landing/LandingPage.tsx(2 hunks)frontend/src/components/pages/ForgotPasswrdPage.tsx(1 hunks)frontend/src/components/pages/LoginPage.tsx(4 hunks)frontend/src/components/pages/ProfilePage.tsx(1 hunks)frontend/src/components/pages/ResetPasswordPage.tsx(1 hunks)frontend/src/components/pages/SignUpPage.tsx(1 hunks)frontend/src/lib/supabaseClient.ts(1 hunks)
🔇 Additional comments (4)
frontend/package.json (2)
21-21: Confirm React Router v7 APIs are fully adopted
Ensure your app uses v7 data‐router patterns:
- Define routes with createBrowserRouter/RouterProvider
- Migrate loaders/actions to return response helpers (json(), redirect())
- Replace useHistory with useNavigate and adopt new hooks (useNavigation, useFetcher, etc.)
- Import core APIs from react-router (not react-router-dom) and opt into any needed “future” flags
13-13: Confirm compatibility with supabase-js updates
Using^2.53.0will install up through v2.57.0, which adds realtime runtime fixes (v2.55.0), enhanced typings, stricter return types, and renames e.g. Gotrue auth methods andstorage.signedUrl. Ensure these changes don’t break your code—or pin to 2.53.x / explicitly bump to ^2.57.0 if you’ve already accounted for them.frontend/src/components/pages/ProfilePage.tsx (1)
4-4: No action needed on this import changeNothing functionally changed here; skipping review for this line.
frontend/src/components/pages/ForgotPasswrdPage.tsx (1)
64-66: Env var choice for redirect
import.meta.env.VITE_BASE_URLis non-standard and easy to misconfigure. Usingwindow.location.originensures an absolute, allowed URL (assuming it’s whitelisted in Supabase auth settings).Confirm that your Supabase Auth redirect URLs include the deployed origin (e.g., production domain and localhost).
There was a problem hiding this 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
♻️ Duplicate comments (2)
frontend/src/components/pages/ResetPasswordPage.tsx (2)
35-43: Resolved: InputField now merges caller className correctly.The earlier issue about losing caller styles is fixed by extracting and merging
className. Nice.
55-106: Resolved: Handles both hash tokens and PKCE code flow.Good addition of
exchangeCodeForSession(code)with URL cleanup; this unblocks password reset across projects.
🧹 Nitpick comments (3)
frontend/src/components/pages/ResetPasswordPage.tsx (3)
50-53: Gate form submission until session is established.Small race: users could submit before
setSession/exchangeCodeForSessioncompletes, makingupdateUserfail with “No session”. Disable submit until the session is ready.const [isLoading, setIsLoading] = useState<boolean>(false); + const [isSessionReady, setIsSessionReady] = useState<boolean>(false); @@ (async () => { try { const { error } = await supabase.auth.setSession({ access_token: accessToken, refresh_token: refreshToken, }); if (error) { toast.error("Error setting session: " + error.message); navigate('/login', { replace: true }); + } else { + setIsSessionReady(true); } @@ (async () => { try { const { error } = await supabase.auth.exchangeCodeForSession(code); if (error) { toast.error("Error exchanging code: " + error.message); navigate('/login', { replace: true }); + } else { + setIsSessionReady(true); } @@ - disabled={isLoading} + disabled={isLoading || !isSessionReady}Also applies to: 68-101, 187-189
115-118: Grammar + strict equality.Use strict compare and correct the message.
- if (password != repassword) { - toast.error("Passwords doesn't match. Try Again"); + if (password !== repassword) { + toast.error("Passwords don't match. Try again."); return; }
167-174: Improve UX/a11y: autocomplete, minlength, and aria-labels for password fields.Helps password managers, native validation, and screen readers.
<InputField icon={Key} type="password" name="password-1" className="mb-7" placeholder="New Password" + autoComplete="new-password" + minLength={8} + aria-label="New password" required /> <InputField icon={Key} type="password" name="password-2" className="mb-7" placeholder="Reenter Password" + autoComplete="new-password" + minLength={8} + aria-label="Confirm new password" required />Also applies to: 175-182
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
frontend/src/components/pages/ForgotPasswrdPage.tsx(1 hunks)frontend/src/components/pages/ResetPasswordPage.tsx(1 hunks)frontend/src/components/pages/SignUpPage.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- frontend/src/components/pages/ForgotPasswrdPage.tsx
- frontend/src/components/pages/SignUpPage.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/components/pages/ResetPasswordPage.tsx (1)
frontend/src/lib/supabaseClient.ts (1)
supabase(12-12)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
frontend/src/components/pages/ForgotPasswrdPage.tsx (1)
64-73: Good: success state only on successful API call; errors surfaced
🧹 Nitpick comments (9)
frontend/src/components/pages/ForgotPasswrdPage.tsx (5)
36-46: InputField ignores caller className; add containerClassName and merge classesMargin class passed at Line 101 is dropped. Merge className and support wrapper spacing.
-const InputField = ({ icon: Icon, ...props }: InputFieldProps) => ( - <div className="relative"> +const InputField = ({ icon: Icon, className, containerClassName, ...props }: InputFieldProps) => ( + <div className={`relative ${containerClassName ?? ''}`}> <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> <Icon className="h-5 w-5 text-gray-400" /> </div> <input {...props} - className="block w-full pl-10 pr-3 py-2 border border-gray-800 rounded-lg bg-gray-900 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent" + className={`block w-full pl-10 pr-3 py-2 border border-gray-800 rounded-lg bg-gray-900 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent ${className ?? ''}`} /> </div> );
18-20: Add containerClassName prop to the InputField typeinterface InputFieldProps extends React.InputHTMLAttributes<HTMLInputElement> { icon: React.ElementType; + containerClassName?: string; }
97-104: Apply wrapper spacing to InputField- <InputField + <InputField icon={Mail} type="email" name="email" - className="mb-7" + containerClassName="mb-7" placeholder="Email address" required />
49-49: Typos: rename component to ForgotPasswordPage-export default function ForgotPasswrdPage() { +export default function ForgotPasswordPage() {Rename the file and update imports/usages accordingly.
16-35: Reduce duplication: extract AuthLayout to a shared component
This layout is repeated across auth pages; centralize as components/auth/AuthLayout.tsx.frontend/src/components/pages/SignUpPage.tsx (4)
64-66: Use strict equality and fix copy- if (password != repassword) { - toast.error("Passwords doesn't match.Try Again"); + if (password !== repassword) { + toast.error("Passwords don't match. Try again.");
59-63: Normalize form inputs (trim email/name)
Avoid accidental whitespace; do not trim passwords.- const email = formData.get('email') as string; - const name = formData.get('name') as string; - const password = formData.get('password-1') as string; - const repassword = formData.get('password-2') as string; + const email = String(formData.get('email') || '').trim(); + const name = String(formData.get('name') || '').trim(); + const password = String(formData.get('password-1') || ''); + const repassword = String(formData.get('password-2') || '');
128-156: Add autocomplete hints for better UX<InputField icon={User} name="name" + autoComplete="username" placeholder="Username" required /> <InputField icon={Mail} type="email" name="email" + autoComplete="email" placeholder="Email address" required /> <InputField icon={Lock} type="password" name="password-1" + autoComplete="new-password" placeholder="Password" required /> <InputField icon={Lock} type="password" name="password-2" + autoComplete="new-password" placeholder="Reenter Password" required />
25-36: Extract AuthLayout/InputField to shared components
These are duplicated across auth pages; move to shared components to keep styling and behavior in one place.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
frontend/src/components/pages/ForgotPasswrdPage.tsx(1 hunks)frontend/src/components/pages/ResetPasswordPage.tsx(1 hunks)frontend/src/components/pages/SignUpPage.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/components/pages/ResetPasswordPage.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/components/pages/ForgotPasswrdPage.tsx (1)
frontend/src/lib/supabaseClient.ts (1)
supabase(12-12)
frontend/src/components/pages/SignUpPage.tsx (2)
frontend/src/lib/supabaseClient.ts (1)
supabase(12-12)backend/app/core/config/settings.py (1)
Settings(8-51)
🔇 Additional comments (2)
frontend/src/components/pages/ForgotPasswrdPage.tsx (1)
64-66: Verify Supabase redirect URL configuration
Ensure VITE_BASE_URL (or window.origin) exactly matches an allowed site URL in Supabase Auth settings and that /reset-password is permitted; otherwise the link may fail at runtime.frontend/src/components/pages/SignUpPage.tsx (1)
90-98: Confirm emailRedirectTo validity
Ensure the value resolves to an absolute URL allowed in Supabase Auth settings (Site URL/Redirect URLs) to prevent broken confirmation links.
smokeyScraper
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Thank you very much for your contribution, @PinJinx!
Cheers!
Closes #115
📝 Description
This PR introduces a basic authentication system for the frontend, leveraging Supabase’s built-in email authentication.
🔧 Changes Made
📷 Screenshots or Visual Changes (if applicable)
Login Screen
SignUp Screen
Post-Signup Screen
🤝 Collaboration
nil
✅ Checklist
Summary by CodeRabbit
New Features
Documentation
Chores