Skip to content

feat: optional Google sign-in - #192

Merged
ayoub3bidi merged 8 commits into
developfrom
feature/supabase-auth-google
Jun 28, 2026
Merged

feat: optional Google sign-in#192
ayoub3bidi merged 8 commits into
developfrom
feature/supabase-auth-google

Conversation

@ayoub3bidi

@ayoub3bidi ayoub3bidi commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Contribution workflow

Description

Adds optional user accounts for Bayan Flow v0.5.0: Google OIDC sign-in via Supabase Auth, session persistence, sign-out, and minimal profile plumbing. Unauthenticated users keep full access to all visualizations and existing panels — no feature gating in this PR.

Sign-in uses a popup OAuth flow (Google account picker in a small window, /auth/callback completes the session and closes the popup). From the landing page, successful sign-in redirects to /app. On /app, the control is a compact G icon to the right of the theme toggle, with a styled tooltip on hover.

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📚 Documentation update
  • 🎨 Style/UI improvement
  • ⚡ Performance improvement
  • 🧪 Test addition or improvement
  • 🔧 Chore (maintenance, dependencies, etc.)

Related Issues

Fixes #

Changes Made

Auth stack

Profile UI

Database (Supabase)

  • supabase/migrations/ — mirrored SQL applied to project bayan-flow (qketsapzqpzmccljfjcm, eu-central-1):
    • profiles table (plan default free, provider, timestamps)
    • handle_new_user trigger on auth.users insert
    • RLS: authenticated users SELECT own row only; client cannot write plan
    • display_name, avatar_url columns + backfill from Google metadata

CSP, CI, and hosting

Docs and i18n

  • AGENTS.md — auth architecture contracts
  • public/auth.md — popup flow, callback redirect URL requirement
  • src/content/legal/privacy.en.js — optional Google sign-in, profile data
  • Auth strings in en / fr / ar: sign_in_google, sign_out, account_menu_label, completing_sign_in, etc.

Tests

  • New: authService, AuthProvider, UserMenu, UserAvatar, resolveUserAvatar, Tooltip, supabaseMock
  • Updated: LandingPage, PrivacyPolicy, cspHeaders, framerMotionMock (useReducedMotion)

Algorithm Details (if applicable)

N/A — no algorithm changes.

Testing

  • All existing tests pass (pnpm test:run) — 1598 tests
  • New tests added for new functionality
  • Manual testing completed (local Google OAuth + profile row verified)
  • Cross-browser testing (if UI changes) — recommended on staging after deploy

Test Results

Test Files  127 passed (127)
     Tests  1598 passed (1598)
pnpm lint     — pass
pnpm build    — pass (auth CSP directives validated on dist/_headers)

Manual verification

Prerequisites

  1. Copy .env.example.env.local with Supabase URL + anon key
  2. Supabase Redirect URLs include {origin}/auth/callback (e.g. http://localhost:5173/auth/callback, https://dev.bayanflow.com/auth/callback)
  3. GitHub Actions secrets VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY set for Cloudflare deploy workflows

Landing (/)

  1. Top-right: “Sign in with Google” → popup opens (not full-page redirect)
  2. Choose Google account → popup closes → app navigates to /app
  3. Avatar / account menu shows display name, email, Google photo (or DiceBear fallback)

App (/app)

  1. Header order: GitHub → Language → Theme → G icon / avatar (compact)
  2. Hover G icon → styled tooltip “Sign in with Google” (no scale animation)
  3. Account menu → Sign out clears session

Without env vars

  1. Auth controls hidden (isSupabaseConfigured() false); app behaves as before

RTL (ar)

  1. Header auth control placement uses logical end alignment; verify visually on staging

Screenshots/GIFs

Before After
No sign-in; header ends at theme toggle Compact G icon + tooltip on /app; full button on landing
Popup Google OAuth; account avatar dropdown after sign-in

Add screenshots from local/staging QA before merge if desired.

Code Quality

  • Code follows the project's coding standards
  • ESLint passes (pnpm lint)
  • Prettier formatting applied (pnpm format)
  • No console errors or warnings (auth flow verified locally)
  • Code is properly documented with JSDoc (if applicable)

Performance Impact

  • Performance impact assessed and acceptable
  • Supabase client + Dicebear add to main bundle; auth code is lazy only where possible; chunk size warning unchanged from baseline

Accessibility

  • Keyboard navigation works correctly (tooltip shows on focus; account menu keyboard-dismissible)
  • Screen reader compatibility maintained (aria-label, role="menu", role="tooltip", aria-describedby)
  • Color contrast meets WCAG guidelines (tooltip uses high-contrast pill)
  • Focus indicators are visible

Breaking Changes

  • None — auth is optional; missing Supabase env vars disable auth UI without affecting existing features

Checklist

  • I have completed the Contribution workflow checklist at the top of this template
  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published — GitHub secrets + Supabase redirect URLs must be configured before staging OAuth works

Additional Notes

Explicitly out of scope (future releases)

  • Feature gating, favorites, GitHub OAuth, Lemon Squeezy / plan webhooks, signup abuse hooks

Deploy / ops checklist for reviewer

  1. Confirm GitHub secrets on repo: VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY
  2. Confirm Supabase redirect URLs for dev + prod origins (/auth/callback)
  3. Google Cloud OAuth consent screen: app name/logo (user still sees Supabase redirect host unless custom auth domain is added later)
  4. Apply supabase/migrations/ if deploying to a fresh Supabase project

OAuth branding note

Google consent screen shows *.supabase.co as the redirect host — expected with Supabase as OIDC broker; improve via Google consent branding; optional Supabase custom auth domain later.


Reviewer Guidelines:

  • Check that all tests pass
  • Verify code follows project standards
  • Test sign-in on staging with secrets configured
  • Confirm no service-role key in client bundle or CI logs
  • Ensure privacy/auth docs match behavior

Summary by CodeRabbit

  • New Features
    • Added optional Google sign-in with One Tap, including an account menu and redirect after sign-in.
    • Added user avatar display with automatic fallback generation when profile images aren’t available.
  • Bug Fixes
    • Updated security headers (CSP and permissions) to properly allow Google and Supabase authentication and avatar assets.
  • Documentation
    • Updated auth and privacy documentation to reflect optional sign-in and what data is stored.

@ayoub3bidi ayoub3bidi self-assigned this Jun 27, 2026
@netlify

netlify Bot commented Jun 27, 2026

Copy link
Copy Markdown

Deploy Preview for dev-bayanflow ready!

Name Link
🔨 Latest commit 6d327da
🔍 Latest deploy log https://app.netlify.com/projects/dev-bayanflow/deploys/6a4109d782df260008a4865e
😎 Deploy Preview https://deploy-preview-192--dev-bayanflow.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot added documentation Improvements or additions to documentation style Improve styling, design, and animation ci Workflows dependencies config tests labels Jun 27, 2026
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds optional Google sign-in via Supabase OIDC across schema, services, React auth state, UI, CSP/build configuration, and matching tests/docs.

Changes

Google OIDC Auth via Supabase

Layer / File(s) Summary
Profiles schema, trigger, and RLS
supabase/migrations/20260627160007_create_profiles_auth_trigger_rls.sql, supabase/migrations/20260627160026_revoke_handle_new_user_rpc_execute.sql, supabase/migrations/20260627170000_profiles_display_name_avatar_url.sql
Creates public.profiles with plan, provider, display_name, and avatar_url, adds the trigger on auth.users, enables RLS, revokes direct function access, and backfills the new profile fields.
Supabase client, GIS wrapper, and auth/profile services
src/lib/supabaseClient.js, src/lib/googleIdentity.js, src/services/authService.js, src/services/profileService.js, src/services/entitlementService.js, src/services/googleTokenExchange.js
Adds the cached Supabase client, Google Identity Services helpers, signInWithIdToken-based auth flow, profile lookup and entitlement helpers, and Google auth-code exchange.
Auth context contract and provider
src/contexts/AuthContextDefinition.js, src/contexts/AuthProvider.jsx, src/hooks/useAuth.js, src/main.jsx, src/pages/GoogleAuthCallback.jsx
Defines the auth context shape, implements provider hydration and auth-state updates, exposes useAuth, wraps the app in the provider, and adds the Google callback route/page.
Avatar, tooltip, and auth menu UI
src/utils/resolveUserAvatar.js, src/components/UserAvatar.jsx, src/components/ui/Tooltip.jsx, src/components/UserMenu.jsx, src/components/GoogleOneTap.jsx, src/components/Header.jsx, src/pages/LandingPage.jsx, src/i18n/locales/*/translation.json
Adds avatar resolution and fallback rendering, tooltip behavior, the auth dropdown and One Tap components, header/landing wiring, and auth UI translations.
CSP, build env, and repo guidance
public/_headers, netlify.toml, scripts/cspHeaders.js, vite.config.js, .github/workflows/deploy-cloudflare.yml, .github/workflows/preview-cloudflare.yml, .env.example, AGENTS.md, package.json, README.md, public/auth.md
Updates CSP rules and validation for Google/Supabase origins, injects build-time env vars, adds DiceBear/Supabase dependencies, and refreshes auth/setup documentation.
Mocks, component tests, service tests, and policy/docs checks
src/test/supabaseMock.js, src/test/setup.js, src/test/framerMotionMock.jsx, vitest.config.js, src/contexts/AuthProvider.test.jsx, src/components/UserMenu.test.jsx, src/components/UserAvatar.test.jsx, src/components/GoogleOneTap.test.jsx, src/components/ui/Tooltip.test.jsx, src/utils/resolveUserAvatar.test.js, src/services/authService.test.js, src/services/googleTokenExchange.test.js, src/lib/googleIdentity.test.js, src/lib/supabaseClient.test.js, src/security/cspHeaders.test.js, src/content/legal/privacy.en.js, src/content/legal/privacy.en.test.js, src/pages/GoogleAuthCallback.test.jsx, src/pages/LandingPage.test.jsx, src/pages/PrivacyPolicy.test.jsx, src/constants/pyodideCdn.test.js
Adds Supabase and Framer mocks, sets test env, and expands coverage for auth, avatar, tooltip, CSP, privacy, and token-exchange behavior.

Possibly related PRs

  • ayoub3bidi/bayan-flow#191: Shares the CSP validation and header-update path, including scripts/cspHeaders.js, public/_headers, and netlify.toml.

Poem

🐇 I hop through sign-in gates at dawn,
With DiceBear ears and tokens drawn.
Supabase hums, “You’re known, you’re free,”
And one small menu smiles at me.
🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding optional Google sign-in via Supabase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/supabase-auth-google

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

@socket-security

socket-security Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​dicebear/​styles@​10.2.08210010093100
Added@​supabase/​supabase-js@​2.108.28910010096100
Added@​dicebear/​core@​10.3.01001009196100

View full report

@codecov

codecov Bot commented Jun 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.63812% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/components/ui/Tooltip.jsx 97.64% 2 Missing ⚠️
src/contexts/AuthProvider.jsx 99.04% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

Preview for Bayan Flow Staging ready!

Name Link
🔨 Latest commit 6d327da
🔍 Latest deploy log https://github.com/ayoub3bidi/bayan-flow/actions/runs/28321212903
😎 Deploy Preview https://pr-192-bayan-flow-staging.ayoub3bidi.workers.dev
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

Preview alias pr-192 on the staging worker. Updates automatically with new commits.

@ayoub3bidi
ayoub3bidi force-pushed the feature/supabase-auth-google branch from 3a05616 to 159e91a Compare June 27, 2026 17:19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (2)
src/pages/LandingPage.test.jsx (1)

52-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the mocked UserMenu is rendered on the landing page.

Right now this stub prevents the suite from catching a regression where the landing-page sign-in entry disappears entirely. Add a simple getByTestId('user-menu') assertion in the render checks so the PR’s new auth entrypoint stays covered.

🤖 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 `@src/pages/LandingPage.test.jsx` around lines 52 - 54, The LandingPage test
currently mocks UserMenu but never verifies it is actually rendered, leaving the
sign-in entrypoint untested. In LandingPage.test.jsx, update the render
assertions to include a getByTestId('user-menu') check against the mocked
UserMenu so the landing page coverage explicitly confirms the auth entrypoint
remains present.
src/main.jsx (1)

40-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse AUTH_CALLBACK_PATH for this route.

authService.getOAuthCallbackUrl() already builds the OAuth redirect from AUTH_CALLBACK_PATH. Import the same constant here so the router and redirect cannot drift apart later.

Suggested fix
 import { ThemeProvider } from './contexts/ThemeContext.jsx';
 import { AuthProvider } from './contexts/AuthProvider.jsx';
+import { AUTH_CALLBACK_PATH } from './services/authService.js';
@@
-            <Route path="/auth/callback" element={<AuthCallback />} />
+            <Route path={AUTH_CALLBACK_PATH} element={<AuthCallback />} />
🤖 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 `@src/main.jsx` at line 40, The auth callback route is hardcoded instead of
reusing the shared path constant. Update the router in main.jsx to import and
use AUTH_CALLBACK_PATH for the AuthCallback route so it stays aligned with
authService.getOAuthCallbackUrl() and cannot drift from the OAuth redirect path.
🤖 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 `@AGENTS.md`:
- Around line 9-16: Update AGENTS.md to remove the time-sensitive auth
rollout/status snapshot or rewrite it to reflect the current post-PR contract.
The current “auth not implemented yet” / “no Supabase project exists” notes are
stale and conflict with this patch, so edit the relevant bullet(s) in AGENTS.md
to keep only durable repo rules and reference the existing auth-related guidance
consistently.

In `@scripts/cspHeaders.js`:
- Around line 87-105: The auth CSP check in assertAuthCspDirectives is
hardcoding a single Supabase host instead of using the configured backend URL.
Update the connect-src validation to derive the required Supabase origin from
the same VITE_SUPABASE_URL used by src/lib/supabaseClient.js (and ensure the
build workflows pass the correct environment value for staging/production), then
validate against that computed origin rather than a fixed string.

In `@src/components/ui/Tooltip.jsx`:
- Around line 39-62: The Tooltip component in `show`, `hide`, and
`clearShowTimeout` leaves a pending hover timer active if the component unmounts
before the delay completes, which can trigger a stale `setVisible(true)` call.
Add an unmount cleanup for `showTimeoutRef` in the `Tooltip` component (for
example via the existing React effect cleanup) so any scheduled timeout is
cleared when the component is removed.

In `@src/components/UserMenu.jsx`:
- Around line 120-132: The landing sign-in CTA in UserMenu should stay fully
visible on small screens instead of collapsing to icon-only. Update the sign-in
button markup in the UserMenu component so the text label tied to
t('auth.sign_in_google') is no longer hidden by the responsive classes on
mobile, while keeping the existing motion.button behavior and Google icon
intact.

In `@src/content/legal/privacy.en.js`:
- Line 32: The privacy copy in privacy.en.js currently hard-codes that the
Supabase PostgreSQL profile row is hosted in the EU, which may be inaccurate
until the project is provisioned. Update the wording in the optional Google
sign-in paragraph to be region-neutral, or change it to the actual Supabase
region once known, so the statement matches the deployed setup. Locate the text
in the privacy policy entry and adjust only that hosting/location claim.

In `@src/contexts/AuthProvider.jsx`:
- Around line 53-64: `refreshProfile` in `AuthProvider` can commit an
out-of-date `profileRow` after `getProfile()` resolves, so a slower fetch for an
old user may overwrite the current session. Update the `refreshProfile` flow,
the hydrate path, and the `onAuthStateChange()` callback so each request is tied
to the latest active user/request token and only calls `setProfileRow` when the
fetched row still matches that latest request. Keep the existing null/reset
handling for signed-out users and stale/error cases.

In `@src/services/authService.js`:
- Around line 77-105: The popup callbacks in authService’s onMessage listener
and pollTimer currently await getSession() without handling rejections, which
can leave the popup promise pending and hide the real error. Update both
callbacks to catch any getSession() failure and route it to fail(error)
immediately, while keeping the existing success path that calls finish() when a
session is returned.

In `@src/test/supabaseMock.js`:
- Around line 46-55: The Supabase mock reset helper only clears call history for
several mocks, so prior return-value stubs can leak into later tests. Update
resetSupabaseMocks to use mockReset on the auth and from mocks in supabaseMock,
then reapply the default implementations for isSupabaseConfigured,
getSupabaseClient, and the Supabase auth/client helpers so tests like
AuthProvider.test.jsx start from a clean baseline.

In `@supabase/migrations/20260627170000_profiles_display_name_avatar_url.sql`:
- Around line 33-46: The current backfill only updates existing public.profiles
rows, so users without a profile row are still missed. Update the migration to
also insert missing profile records for any auth.users entries that do not yet
have a matching public.profiles row, then apply the existing display_name and
avatar_url backfill logic; use the public.profiles and auth.users tables in the
migration so src/services/profileService.js can find a row for every user.

---

Nitpick comments:
In `@src/main.jsx`:
- Line 40: The auth callback route is hardcoded instead of reusing the shared
path constant. Update the router in main.jsx to import and use
AUTH_CALLBACK_PATH for the AuthCallback route so it stays aligned with
authService.getOAuthCallbackUrl() and cannot drift from the OAuth redirect path.

In `@src/pages/LandingPage.test.jsx`:
- Around line 52-54: The LandingPage test currently mocks UserMenu but never
verifies it is actually rendered, leaving the sign-in entrypoint untested. In
LandingPage.test.jsx, update the render assertions to include a
getByTestId('user-menu') check against the mocked UserMenu so the landing page
coverage explicitly confirms the auth entrypoint remains present.
🪄 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 Plus

Run ID: 25c4017a-0fc0-42ae-a659-d3e382f000b0

📥 Commits

Reviewing files that changed from the base of the PR and between 7fafb95 and 3a05616.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (45)
  • .env.example
  • .github/workflows/deploy-cloudflare.yml
  • .github/workflows/preview-cloudflare.yml
  • AGENTS.md
  • netlify.toml
  • package.json
  • public/_headers
  • public/auth.md
  • scripts/cspHeaders.js
  • src/components/Header.jsx
  • src/components/UserAvatar.jsx
  • src/components/UserAvatar.test.jsx
  • src/components/UserMenu.jsx
  • src/components/UserMenu.test.jsx
  • src/components/ui/Tooltip.jsx
  • src/components/ui/Tooltip.test.jsx
  • src/content/legal/privacy.en.js
  • src/content/legal/privacy.en.test.js
  • src/contexts/AuthContextDefinition.js
  • src/contexts/AuthProvider.jsx
  • src/contexts/AuthProvider.test.jsx
  • src/hooks/useAuth.js
  • src/i18n/locales/ar/translation.json
  • src/i18n/locales/en/translation.json
  • src/i18n/locales/fr/translation.json
  • src/lib/supabaseClient.js
  • src/main.jsx
  • src/pages/AuthCallback.jsx
  • src/pages/LandingPage.jsx
  • src/pages/LandingPage.test.jsx
  • src/pages/PrivacyPolicy.test.jsx
  • src/security/cspHeaders.test.js
  • src/services/authService.js
  • src/services/authService.test.js
  • src/services/entitlementService.js
  • src/services/profileService.js
  • src/test/framerMotionMock.jsx
  • src/test/setup.js
  • src/test/supabaseMock.js
  • src/utils/resolveUserAvatar.js
  • src/utils/resolveUserAvatar.test.js
  • supabase/migrations/20260627160007_create_profiles_auth_trigger_rls.sql
  • supabase/migrations/20260627160026_revoke_handle_new_user_rpc_execute.sql
  • supabase/migrations/20260627170000_profiles_display_name_avatar_url.sql
  • vite.config.js

Comment thread AGENTS.md Outdated
Comment thread scripts/cspHeaders.js
Comment thread src/components/ui/Tooltip.jsx
Comment thread src/components/UserMenu.jsx
Comment thread src/content/legal/privacy.en.js Outdated
Comment thread src/contexts/AuthProvider.jsx
Comment thread src/services/authService.js Outdated
Comment thread src/test/supabaseMock.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@src/components/UserAvatar.jsx`:
- Around line 27-37: The avatar fallback logic in UserAvatar currently only
switches on error, so a missing profile.avatarSrc can leave the image blank.
Update the image source handling around useEffect, imageSrc, and usedFallback so
that the DiceBear fallback (fallbackSrc from generateAvatarDataUri) is used
whenever profile.avatarSrc is empty/undefined, not just inside onError. Keep the
state reset in sync when profile.avatarSrc changes so the component immediately
renders the fallback for missing avatars.

In `@src/pages/AuthCallback.jsx`:
- Around line 9-10: The AuthCallback page currently calls Supabase directly and
only handles the success path of getSession, which can leave the popup stuck if
the promise rejects. Update AuthCallback to use authService.getSession() instead
of importing getSupabaseClient, and add rejection handling in the callback flow
so failures still notify the opener and close or redirect appropriately. Keep
the session logic inside the auth service layer and preserve the existing
AUTH_COMPLETE_MESSAGE behavior via the AuthCallback component’s callback
handling.

In `@src/utils/resolveUserAvatar.js`:
- Around line 34-35: The remote avatar URL validator currently allows insecure
http:// values in isHttpUrl, which should be restricted to https:// only. Update
the URL check used by resolveUserAvatar and any related call sites in the avatar
resolution flow to accept only secure https URLs, keeping the existing
string/trim validation intact. Make sure any logic that depends on this helper
(including the referenced avatar resolution paths) continues to reject non-HTTPS
remote avatars.
🪄 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 Plus

Run ID: a6ad3f0e-e21f-4bff-95fe-6e8a4f105668

📥 Commits

Reviewing files that changed from the base of the PR and between 3a05616 and 159e91a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (45)
  • .env.example
  • .github/workflows/deploy-cloudflare.yml
  • .github/workflows/preview-cloudflare.yml
  • AGENTS.md
  • netlify.toml
  • package.json
  • public/_headers
  • public/auth.md
  • scripts/cspHeaders.js
  • src/components/Header.jsx
  • src/components/UserAvatar.jsx
  • src/components/UserAvatar.test.jsx
  • src/components/UserMenu.jsx
  • src/components/UserMenu.test.jsx
  • src/components/ui/Tooltip.jsx
  • src/components/ui/Tooltip.test.jsx
  • src/content/legal/privacy.en.js
  • src/content/legal/privacy.en.test.js
  • src/contexts/AuthContextDefinition.js
  • src/contexts/AuthProvider.jsx
  • src/contexts/AuthProvider.test.jsx
  • src/hooks/useAuth.js
  • src/i18n/locales/ar/translation.json
  • src/i18n/locales/en/translation.json
  • src/i18n/locales/fr/translation.json
  • src/lib/supabaseClient.js
  • src/main.jsx
  • src/pages/AuthCallback.jsx
  • src/pages/LandingPage.jsx
  • src/pages/LandingPage.test.jsx
  • src/pages/PrivacyPolicy.test.jsx
  • src/security/cspHeaders.test.js
  • src/services/authService.js
  • src/services/authService.test.js
  • src/services/entitlementService.js
  • src/services/profileService.js
  • src/test/framerMotionMock.jsx
  • src/test/setup.js
  • src/test/supabaseMock.js
  • src/utils/resolveUserAvatar.js
  • src/utils/resolveUserAvatar.test.js
  • supabase/migrations/20260627160007_create_profiles_auth_trigger_rls.sql
  • supabase/migrations/20260627160026_revoke_handle_new_user_rpc_execute.sql
  • supabase/migrations/20260627170000_profiles_display_name_avatar_url.sql
  • vite.config.js
✅ Files skipped from review due to trivial changes (9)
  • src/pages/PrivacyPolicy.test.jsx
  • src/components/UserAvatar.test.jsx
  • src/pages/LandingPage.test.jsx
  • src/utils/resolveUserAvatar.test.js
  • src/services/profileService.js
  • .github/workflows/preview-cloudflare.yml
  • src/i18n/locales/ar/translation.json
  • src/i18n/locales/en/translation.json
  • AGENTS.md
🚧 Files skipped from review as they are similar to previous changes (26)
  • public/_headers
  • src/services/authService.test.js
  • src/test/framerMotionMock.jsx
  • src/components/ui/Tooltip.test.jsx
  • src/hooks/useAuth.js
  • src/main.jsx
  • src/pages/LandingPage.jsx
  • netlify.toml
  • src/security/cspHeaders.test.js
  • package.json
  • .github/workflows/deploy-cloudflare.yml
  • scripts/cspHeaders.js
  • supabase/migrations/20260627160026_revoke_handle_new_user_rpc_execute.sql
  • src/components/UserMenu.test.jsx
  • src/contexts/AuthContextDefinition.js
  • supabase/migrations/20260627160007_create_profiles_auth_trigger_rls.sql
  • src/contexts/AuthProvider.test.jsx
  • src/i18n/locales/fr/translation.json
  • src/content/legal/privacy.en.test.js
  • vite.config.js
  • public/auth.md
  • src/test/setup.js
  • src/content/legal/privacy.en.js
  • supabase/migrations/20260627170000_profiles_display_name_avatar_url.sql
  • src/components/Header.jsx
  • src/test/supabaseMock.js

Comment thread src/components/UserAvatar.jsx
Comment thread src/pages/AuthCallback.jsx Outdated
Comment on lines +9 to +10
import { getSupabaseClient } from '@/lib/supabaseClient';
import { AUTH_COMPLETE_MESSAGE } from '@/services/authService';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Catch callback session lookup failures and keep this page on the auth service layer.

Line 24 only handles the resolved path of supabase.auth.getSession(). If that promise rejects, the popup never notifies the opener or closes, so the user can get stranded on /auth/callback. Using authService.getSession() here fixes the repo contract at the same time.

Suggested fix
 import { useEffect } from 'react';
 import { useTranslation } from 'react-i18next';
-import { getSupabaseClient } from '`@/lib/supabaseClient`';
-import { AUTH_COMPLETE_MESSAGE } from '`@/services/authService`';
+import {
+  AUTH_COMPLETE_MESSAGE,
+  getSession,
+} from '`@/services/authService`';

 function AuthCallback() {
   const { t } = useTranslation();

   useEffect(() => {
-    const supabase = getSupabaseClient();
-    if (!supabase) {
-      window.close();
-      return undefined;
-    }
-
     let isMounted = true;
-
-    supabase.auth.getSession().then(({ data: { session }, error }) => {
-      if (!isMounted) {
-        return;
-      }
-
-      if (error) {
-        console.error('Auth callback failed:', error);
-      }
-
-      if (session && window.opener && !window.opener.closed) {
-        window.opener.postMessage(
-          { type: AUTH_COMPLETE_MESSAGE },
-          window.location.origin
-        );
-      }
-
-      window.close();
-    });
+    const completeAuth = async () => {
+      try {
+        const session = await getSession();
+        if (!isMounted) {
+          return;
+        }
+
+        if (session && window.opener && !window.opener.closed) {
+          window.opener.postMessage(
+            { type: AUTH_COMPLETE_MESSAGE },
+            window.location.origin
+          );
+        }
+      } catch (error) {
+        if (isMounted) {
+          console.error('Auth callback failed:', error);
+        }
+      } finally {
+        if (isMounted) {
+          window.close();
+        }
+      }
+    };
+
+    void completeAuth();

     return () => {
       isMounted = false;
     };
   }, []);

As per coding guidelines, "Use src/services/authService.js and src/services/profileService.js as the auth service layer; components must use AuthContext/useAuth and never import Supabase directly." Based on learnings, "Use getSession() and onAuthStateChange() for session handling."

Also applies to: 15-41

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 7-10: src/pages/AuthCallback.jsx#L7-L10
Added lines #L7 - L10 were not covered by tests

🤖 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 `@src/pages/AuthCallback.jsx` around lines 9 - 10, The AuthCallback page
currently calls Supabase directly and only handles the success path of
getSession, which can leave the popup stuck if the promise rejects. Update
AuthCallback to use authService.getSession() instead of importing
getSupabaseClient, and add rejection handling in the callback flow so failures
still notify the opener and close or redirect appropriately. Keep the session
logic inside the auth service layer and preserve the existing
AUTH_COMPLETE_MESSAGE behavior via the AuthCallback component’s callback
handling.

Sources: Coding guidelines, Learnings

Comment thread src/utils/resolveUserAvatar.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 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 `@src/components/GoogleOneTap.jsx`:
- Around line 35-43: Guard the redirect in GoogleOneTap.jsx by re-checking the
effect lifetime after the awaited authService.signInWithGoogleIdToken call
inside initOneTap’s onCredential handler. The issue is that navigation can still
happen after cleanup if the sign-in finishes after the component unmounts or the
effect is invalidated; fix it by verifying isMounted again before calling
navigate('/app') in the onCredential callback.

In `@src/lib/googleIdentity.js`:
- Around line 187-214: The hidden Google sign-in host created in
openGoogleSignInButton is not being removed after the sign-in flow completes,
which leaves stale offscreen DOM nodes behind on repeated attempts. Update the
popup/completion handling around openGoogleSignInButton and the related success,
timeout, and reject paths to always clean up the appended host element. Make
sure any early failure path still removes the host before rejecting, and
preserve the existing click/render behavior while ensuring the hidden node is
detached after use.

In `@src/services/authService.js`:
- Around line 93-100: The sign-in flow in signInWithGoogle only guards on
isGoogleAuthConfigured(), so it can still open the Google popup when Supabase
auth is not fully configured and then fail later. Update signInWithGoogle to
gate the popup on the full auth configuration check used by the rest of the auth
flow, and keep the early throw before requestGoogleSignInPopup is called so the
popup is never launched unless both Google and Supabase auth are ready.
- Around line 29-40: The JWT payload decoding in parseGoogleIdTokenClaims
currently uses atob() directly, which can corrupt non-ASCII Google profile names
before they reach sync logic in authService. Update parseGoogleIdTokenClaims to
decode the base64 payload as UTF-8 before JSON parsing, and keep the existing
fallback behavior for invalid tokens so full_name/name are stored correctly in
the Google sign-in flow.

In `@src/services/authService.test.js`:
- Around line 92-94: The isAuthConfigured test only covers the happy path and
does not exercise the false branches implied by its name. Update
authService.test.js around the isAuthConfigured assertion to also verify cases
where Supabase is present without Google client ID and where Google client ID is
present without Supabase, so the authService.isAuthConfigured() behavior is
validated for both required inputs and not just the combined success case.
- Around line 51-54: Prettier is flagging the JWT payload setup in
authService.test.js, so reformat the payload construction to match the project’s
style. Update the btoa(JSON.stringify(...)) chain in the authService test setup
by breaking it into the preferred multiline layout (or equivalent formatted
form) while keeping the same logic and variable name payload.

In `@src/services/googleTokenExchange.js`:
- Around line 34-40: The token exchange flow in googleTokenExchange.js parses
the response body before checking the HTTP status, which can turn non-JSON error
responses into parse failures. Update the token exchange logic around the
response handling to inspect response.ok first, and only call response.json()
when the body is expected to be JSON; for failures, safely read the error text
or otherwise guard parsing before using payload.error_description in the token
exchange error path.

In `@vitest.config.js`:
- Around line 19-22: The test.env block in vitest.config.js is not formatted to
match Prettier, causing CI to fail. Reformat the env object in the vitest config
so it matches the project’s Prettier style, keeping the
VITE_GOOGLE_WEB_CLIENT_ID entry under the test.env configuration.
🪄 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 Plus

Run ID: 24f2e144-1579-4552-9780-9ffa7026fe02

📥 Commits

Reviewing files that changed from the base of the PR and between 8d2b187 and 40edf07.

📒 Files selected for processing (24)
  • .env.example
  • .github/workflows/deploy-cloudflare.yml
  • .github/workflows/preview-cloudflare.yml
  • AGENTS.md
  • netlify.toml
  • public/_headers
  • public/auth.md
  • scripts/cspHeaders.js
  • src/components/GoogleOneTap.jsx
  • src/components/GoogleOneTap.test.jsx
  • src/components/UserAvatar.jsx
  • src/constants/pyodideCdn.test.js
  • src/contexts/AuthProvider.jsx
  • src/lib/googleIdentity.js
  • src/lib/googleIdentity.test.js
  • src/lib/supabaseClient.js
  • src/main.jsx
  • src/pages/GoogleAuthCallback.jsx
  • src/services/authService.js
  • src/services/authService.test.js
  • src/services/googleTokenExchange.js
  • src/services/googleTokenExchange.test.js
  • src/test/supabaseMock.js
  • vitest.config.js
✅ Files skipped from review due to trivial changes (4)
  • src/services/googleTokenExchange.test.js
  • .env.example
  • AGENTS.md
  • public/auth.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/lib/supabaseClient.js
  • public/_headers
  • src/components/UserAvatar.jsx
  • src/contexts/AuthProvider.jsx

Comment thread src/components/GoogleOneTap.jsx
Comment thread src/lib/googleIdentity.js
Comment thread src/services/authService.js
Comment thread src/services/authService.js
Comment thread src/services/authService.test.js Outdated
Comment thread src/services/authService.test.js
Comment thread src/services/googleTokenExchange.js Outdated
Comment thread vitest.config.js
…d Supabase URL in CSP

- Add test files for supabaseClient, GoogleAuthCallback, entitlementService (new)
- Expand tests for AuthProvider, GoogleOneTap, UserMenu, Tooltip,
  authService, resolveUserAvatar (edge cases, error paths, state branches)
- Replace hardcoded Supabase project URL in cspHeaders.js assertion
  with dynamic env read via process.env.VITE_SUPABASE_URL
- Fix formatting in vitest.config.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/cspHeaders.js (1)

85-113: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don't make auth CSP validation unconditionally depend on VITE_SUPABASE_URL.

These helpers are exercised against committed header files, and the current test suite calls them without build env injected. As written, getSupabaseOrigin() throws before assertAuthCspDirectives() can validate anything, which is exactly why the CSP tests are red in CI. Please make the expected Supabase origin injectable from the caller/test setup, or otherwise avoid hard-failing this validator when the env is absent outside the build 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 `@scripts/cspHeaders.js` around lines 85 - 113, The auth CSP validator
currently hard-depends on VITE_SUPABASE_URL via getSupabaseOrigin(), which makes
assertAuthCspDirectives() fail before it can validate committed headers in
tests. Update the helper flow so the expected Supabase origin is injectable from
the caller/test setup (or otherwise only required in the build path), and adjust
assertAuthCspDirectives() to use that injected value instead of unconditionally
reading process.env. Keep the existing validation logic and error messages, but
ensure getSupabaseOrigin() is no longer a mandatory runtime dependency for
CI/header-file checks.

Sources: Linters/SAST tools, Pipeline failures

🤖 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 `@src/components/ui/Tooltip.test.jsx`:
- Around line 46-58: The “hides tooltip on mouse leave” test is using
focus/blur, so it duplicates the keyboard-dismiss path and never covers the
mouse-leave behavior. Update the Tooltip test to use the same rendered setup but
trigger hover/mouse enter to show the tooltip and then fire mouse leave on the
button (using the Tooltip component and its button child) so the test verifies
the actual onMouseLeave path instead of blur.

In `@src/components/UserMenu.test.jsx`:
- Around line 110-131: The sign-in error test in UserMenu.test.jsx only verifies
that signInWithGoogle is called, so it does not catch an incorrect redirect
after a rejected login. Update the handleSignIn error-path coverage by asserting
that no navigation to /app occurs when signInWithGoogle rejects, using the
existing render(<UserMenu variant="landing" />) setup and the signInWithGoogle
mock. Keep the current rejection case, but add an expectation on the
router/navigation mock so this test fails if handleSignIn still redirects on
error.

In `@src/lib/supabaseClient.test.js`:
- Around line 13-52: The supabaseClient tests are importing the module without
stubbing the required Supabase env vars, so isSupabaseConfigured() stays false
and getSupabaseClient() never creates the client. Update the supabaseClient test
setup to stub VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY before importing
supabaseClient.js, and isolate each test with vi.resetModules() plus
vi.unstubAllEnvs() so the cached singleton doesn’t leak between cases. Use the
existing getSupabaseClient and isSupabaseConfigured test blocks as the place to
apply this setup.

---

Outside diff comments:
In `@scripts/cspHeaders.js`:
- Around line 85-113: The auth CSP validator currently hard-depends on
VITE_SUPABASE_URL via getSupabaseOrigin(), which makes assertAuthCspDirectives()
fail before it can validate committed headers in tests. Update the helper flow
so the expected Supabase origin is injectable from the caller/test setup (or
otherwise only required in the build path), and adjust assertAuthCspDirectives()
to use that injected value instead of unconditionally reading process.env. Keep
the existing validation logic and error messages, but ensure getSupabaseOrigin()
is no longer a mandatory runtime dependency for CI/header-file checks.
🪄 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 Plus

Run ID: 3edd6582-ae30-4fe3-8da3-13b708028f5f

📥 Commits

Reviewing files that changed from the base of the PR and between 40edf07 and ff1017e.

📒 Files selected for processing (11)
  • scripts/cspHeaders.js
  • src/components/GoogleOneTap.test.jsx
  • src/components/UserMenu.test.jsx
  • src/components/ui/Tooltip.test.jsx
  • src/contexts/AuthProvider.test.jsx
  • src/lib/supabaseClient.test.js
  • src/pages/GoogleAuthCallback.test.jsx
  • src/services/authService.test.js
  • src/services/entitlementService.test.js
  • src/utils/resolveUserAvatar.test.js
  • vitest.config.js
🚧 Files skipped from review as they are similar to previous changes (3)
  • vitest.config.js
  • src/services/authService.test.js
  • src/utils/resolveUserAvatar.test.js

Comment thread src/components/ui/Tooltip.test.jsx
Comment thread src/components/UserMenu.test.jsx
Comment thread src/lib/supabaseClient.test.js
- getSupabaseOrigin() returns null (instead of throwing) when
  VITE_SUPABASE_URL is not set, allowing CI build to proceed
- assertAuthCspDirectives skips the Supabase origin check when
  getSupabaseOrigin() returns null; other validations remain enforced
- This unblocks the CI workflow's Build job which doesn't have access
  to Supabase secrets, while deploy/preview workflows with secrets
  still validate CSP fully
- Tooltip.test.jsx: replace focus/blur with mouseEnter/mouseLeave in
  'hides tooltip on mouse leave' test
- UserMenu.test.jsx: assert no navigation occurs on sign-in error
- LandingPage.test.jsx: assert mocked UserMenu is rendered
- googleTokenExchange.js: check response.ok before parsing JSON body
  to avoid masking HTTP errors
- resolveUserAvatar.js: restrict isHttpUrl to https:// only
- authService.js: decode JWT payload as UTF-8 via TextDecoder for
  correct handling of non-ASCII characters in Google profile names
…t files

Addresses all 16 remaining CodeRabbit issues from PR #192:

Auth & services:
- main.jsx: import AUTH_CALLBACK_PATH constant instead of hardcoded path
- authService.js: signInWithGoogle checks isAuthConfigured() (Supabase + Google)
- AuthProvider.jsx: stale refreshProfile fix with request token
- GoogleAuthCallback.jsx: use getSession() with error handling
- googleIdentity.js: clean up sign-in host element after use in finish()
- GoogleOneTap.jsx: re-check isMounted before navigating after await

Components:
- UserAvatar.jsx: DiceBear fallback used immediately when avatarSrc empty
- UserMenu.jsx: sign-in label visible on mobile (remove hidden sm:inline)
- Tooltip.jsx: useEffect cleanup clears pending showTimeoutRef on unmount

Data/content:
- privacy.en.js: remove hardcoded "hosted in the EU"
- Migration: INSERT missing profile rows before backfill UPDATE

Tests:
- supabaseMock.js: mockClear -> mockReset with re-applied defaults
- supabaseClient.test.js: env stubs with vi.stubEnv + resetModules
- authService.test.js: add isAuthConfigured Supabase-not-configured branch
- GoogleAuthCallback.test.jsx: update tests for getSession() behavior
@ayoub3bidi
ayoub3bidi merged commit 94e5e20 into develop Jun 28, 2026
16 checks passed
@ayoub3bidi
ayoub3bidi deleted the feature/supabase-auth-google branch June 28, 2026 12:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci Workflows config dependencies documentation Improvements or additions to documentation style Improve styling, design, and animation tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant