-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Enhance OAuth2 session handling and add React Router v7 #10355
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
base: main
Are you sure you want to change the base?
Enhance OAuth2 session handling and add React Router v7 #10355
Conversation
📝 WalkthroughWalkthroughChanges modify OAuth2 session handling and client behavior:
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changesNone found. Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ 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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/controllers/api/account.php (1)
1728-1732
: Incorrect class reference in MFA bypass factor
The constantTYPE::EMAIL
is undefined and will cause a fatal error when creating sessions for OAuth2. Replace it with the correctly namespacedType::EMAIL
. A search for other occurrences ofTYPE::
in this file returned only this single instance, so no additional changes are needed.• File:
app/controllers/api/account.php
(around line 1729)
• Replace:- 'factors' => [TYPE::EMAIL, 'oauth2'], // include a special oauth2 factor to bypass MFA checks + 'factors' => [Type::EMAIL, 'oauth2'], // include a special oauth2 factor to bypass MFA checks
🧹 Nitpick comments (7)
public/sdk-web/client.ts (2)
377-384
: New fallback header block is a no-op given the unconditional assignment above; simplify and avoid sending empty headers.Right now, Line 373 always sets X-Fallback-Cookies from localStorage (to a string or ''), so this new conditional never meaningfully changes the header. The intent seems to be "only set the header when we actually have a value" and to force it specifically for account GETs. Let’s do that directly and remove duplication.
Apply this diff to make the header assignment conditional and remove the redundant block:
@@ - if (typeof window !== 'undefined' && window.localStorage) { - headers['X-Fallback-Cookies'] = window.localStorage.getItem('cookieFallback') ?? ''; - } + if (typeof window !== 'undefined' && window.localStorage) { + const fallbackCookies = window.localStorage.getItem('cookieFallback'); + if (fallbackCookies) { + headers['X-Fallback-Cookies'] = fallbackCookies; + } else { + delete headers['X-Fallback-Cookies']; // avoid sending an empty header + } + } - // For OAuth2 flows, ensure we always try to get the session from localStorage if cookies fail - if (typeof window !== 'undefined' && window.localStorage && method === 'GET' && url.pathname.includes('/account')) { - const fallbackCookies = window.localStorage.getItem('cookieFallback'); - if (fallbackCookies && !headers['X-Fallback-Cookies']) { - headers['X-Fallback-Cookies'] = fallbackCookies; - } - } + // Removed redundant special casing for /account GETs; the conditional assignment above already covers it.
430-435
: Consider downgrading the warning to debug or gating it behind an opt-in flag.With fallback cookies now always set on OAuth2 redirects, this console.warn will trigger on every post-login call and can be noisy in production. Consider using console.debug or a feature flag to reduce noise.
app/controllers/api/account.php (1)
1759-1766
: Make SameSite comparison case-insensitive and resilient to surrounding whitespace.Relying on strict equality with 'None' can miss valid configurations such as 'none' or values with incidental whitespace. Normalize before comparison to avoid surprises.
Apply this diff:
- // Determine appropriate SameSite setting for OAuth2 redirects - $sameSite = Config::getParam('cookieSamesite'); - - // For OAuth2 redirects, use 'Lax' instead of 'None' to ensure compatibility with modern browsers and SPA routing - if ($sameSite === 'None') { - $sameSite = 'Lax'; - } + // Determine appropriate SameSite setting for OAuth2 redirects + $sameSite = Config::getParam('cookieSamesite'); + // For OAuth2 redirects, use 'Lax' instead of 'None' to ensure compatibility with modern browsers and SPA routing + if (\is_string($sameSite) && \strcasecmp(\trim($sameSite), 'None') === 0) { + $sameSite = 'Lax'; + }Also applies to: 1769-1769
docs/OAuth2-React-Router-v7-Fix.md (4)
5-24
: Tighten the problem statement and root-cause bullets.Minor grammar and clarity improvements; also clarify that Lax cookies aren’t sent on cross-site XHR, which is why the fallback is needed.
Apply this diff:
-Users experiencing 401 Unauthorized errors after Google OAuth sign-in when using React Router v7 with Appwrite. The error occurs because session cookies are not properly established after OAuth2 redirect. +Users experienced 401 Unauthorized errors after Google OAuth sign-in when using React Router v7 with Appwrite. The error occurred because session cookies were not consistently established after the OAuth2 redirect in SPA flows. @@ -The issue is caused by: +Primary contributing factors: -1. **SameSite Cookie Attribute**: Cookies set with `SameSite=None` requiring HTTPS in production -2. **Cookie Fallback Mechanism**: X-Fallback-Cookies header not always being set -3. **React Router v7 Navigation**: Stricter cookie handling during client-side navigation +1. **SameSite cookie semantics**: `SameSite=None` requires Secure (HTTPS) and is often blocked in dev; `SameSite=Lax` is not sent on cross-site XHR/fetch, which many SPAs rely on. +2. **Cookie fallback**: The `X-Fallback-Cookies` header was not always set on OAuth2 redirects, so the SDK could not recover the session from localStorage immediately. +3. **React Router v7 navigation**: Client-side navigation can complete the redirect without a full page load, narrowing the cases where cookies are sent automatically.
29-39
: Clarify scope: fallback cookies are now always set for OAuth2 redirects; SDK condition applies broadly, not just account.get().Small wording tweaks to avoid over-promising and to be precise about the scenarios.
-Updated the OAuth2 session creation to: +Updated the OAuth2 session creation path to: @@ -- Always set fallback cookies for OAuth2 sessions regardless of domain verification status +- Always set fallback cookies for OAuth2 redirect responses, regardless of domain verification status @@ -Enhanced the client to: -- Always include fallback cookies from localStorage for account-related requests -- Better handling of OAuth2 session establishment +Enhanced the Web SDK client to: +- Include fallback cookies from localStorage when present (so the session is usable immediately after redirect) +- Improve reliability of OAuth2 session establishment in SPAs
96-106
: Production vs. development nuance around SameSite=Lax and cross-site XHR.Make it explicit that if your app and API are on different sites, cookies with Lax will not accompany XHR/fetch; the SDK’s fallback handles this, but readers should understand the trade-off.
-#### Production (HTTPS) -- Ensure your domain is properly configured in Appwrite console -- Use proper SSL certificates -- The fix should work seamlessly with `SameSite=Lax` cookies +#### Production (HTTPS) +- Ensure your domain is properly configured in the Appwrite console and use valid SSL certificates. +- Note: If your app runs on a different site than your Appwrite API, `SameSite=Lax` cookies will not be sent on cross-site XHR/fetch. The Web SDK’s fallback (`X-Fallback-Cookies` + localStorage) addresses this; keep it enabled unless you can serve API and app from the same site.
117-131
: Add a short “Security considerations” section.The doc should briefly call out the sensitivity of the fallback header/localStorage and logging guidance.
Proposed insertion after “Additional Notes”:
+## Security considerations + +- `X-Fallback-Cookies` contains an encoded session and should be treated as sensitive. Ensure reverse proxies and application logs do not record this header. +- The SDK stores the fallback in `localStorage` to bridge OAuth2 redirects. Protect against XSS (e.g., strong CSP, avoid dangerouslySetInnerHTML) because XSS could read localStorage. +- Once you can serve your SPA and API from the same site (or verify your custom domain), you may rely on same-site cookies and reduce dependency on the fallback mechanism.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
docs/examples/react-router-v7-oauth2.tsx
is excluded by!docs/examples/**
📒 Files selected for processing (3)
app/controllers/api/account.php
(2 hunks)docs/OAuth2-React-Router-v7-Fix.md
(1 hunks)public/sdk-web/client.ts
(1 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/OAuth2-React-Router-v7-Fix.md
[grammar] ~21-~21: There might be a mistake here.
Context: ...Site=None` requiring HTTPS in production 2. Cookie Fallback Mechanism: X-Fallback-...
(QB_NEW_EN)
[grammar] ~22-~22: There might be a mistake here.
Context: ...back-Cookies header not always being set 3. React Router v7 Navigation: Stricter c...
(QB_NEW_EN)
[grammar] ~29-~29: There might be a mistake here.
Context: ... Updated the OAuth2 session creation to: - Always set fallback cookies for OAuth2 s...
(QB_NEW_EN)
[grammar] ~36-~36: There might be a mistake here.
Context: ...web/client.ts`) Enhanced the client to: - Always include fallback cookies from loc...
(QB_NEW_EN)
[grammar] ~98-~98: There might be a mistake here.
Context: ... Considerations #### Development (HTTP) - Ensure your Appwrite instance is configu...
(QB_NEW_EN)
[grammar] ~102-~102: There might be a mistake here.
Context: ...lly for OAuth2) #### Production (HTTPS) - Ensure your domain is properly configure...
(QB_NEW_EN)
[grammar] ~103-~103: There might be a mistake here.
Context: ...sure your domain is properly configured in Appwrite console - Use proper SSL certi...
(QB_NEW_EN)
[grammar] ~109-~109: There might be a mistake here.
Context: ...` cookies ## Testing the Fix 1. Set up OAuth2 provider (Google) in Appwrite ...
(QB_NEW_EN)
[grammar] ~109-~109: There might be a mistake here.
Context: ... 1. Set up OAuth2 provider (Google) in Appwrite console 2. **Implement OAuth2 ...
(QB_NEW_EN)
[grammar] ~111-~111: There might be a mistake here.
Context: ...eact Router v7 app 3. Test the flow: - Click login with Google - Complete OA...
(QB_NEW_EN)
🔇 Additional comments (2)
app/controllers/api/account.php (2)
1742-1744
: Good call: Always returning X-Fallback-Cookies for OAuth2 redirect closes the “no cookies after SPA navigation” gap.This aligns with the target scenario (React Router v7) and makes the session immediately retrievable by the Web SDK after the redirect. No functional concerns here.
1742-1769
: Ensure sensitive header isn’t logged or cached anywhere in the pipeline.X-Fallback-Cookies carries an encoded session. While headers later add Cache-Control/Pragma before redirect, double-check server logs, reverse proxies, and middleware do not log or cache this header.
Proposed checklist:
- Confirm access logs sanitize/omit X-Fallback-Cookies.
- Confirm any reverse proxy (e.g., Nginx, Cloudflare) does not cache or log this header.
- If needed, add explicit header suppression in logging config.
I can draft Nginx/Envoy snippets to drop/sanitize this header on egress if you want.
Security Scan Results for PRDocker Image Scan Results
Source Code Scan Results🎉 No vulnerabilities found! |
What does this PR do?
This PR fixes the 401 Unauthorized error that occurs after Google OAuth sign-in when using Appwrite with React Router v7. The issue manifested as users being treated as guests despite successful OAuth2 authentication, with the error message "User (role: guests) missing scope (account)".
Key Changes:
SameSite=Lax
instead ofSameSite=None
for OAuth2 redirects to ensure better browser compatibility with modern SPAsThis fix addresses the root cause while maintaining full backward compatibility and improving the overall OAuth2 authentication experience across all environments.
Test Plan
Manual Testing:
account.createOAuth2Session('google', successUrl, failureUrl)
account.get()
immediately after redirect - should return user data without 401 errorAutomated Testing:
Before Fix:
account.get()
after OAuth2 redirect returns 401 UnauthorizedAfter Fix:
account.get()
after OAuth2 redirect returns authenticated user dataRelated PRs and Issues
Checklist
docs/OAuth2-React-Router-v7-Fix.md
docs/examples/react-router-v7-oauth2.tsx