Skip to content

Make /portal/sso/embed best-effort to handle Cloudflare challenges#210

Merged
matin merged 2 commits intomainfrom
fix-embed-403
Mar 18, 2026
Merged

Make /portal/sso/embed best-effort to handle Cloudflare challenges#210
matin merged 2 commits intomainfrom
fix-embed-403

Conversation

@matin
Copy link
Copy Markdown
Owner

@matin matin commented Mar 18, 2026

Summary

Some users get a 403 Cloudflare managed challenge ("Just a moment...") on the /portal/sso/embed step added in 0.7.3. This happens because Cloudflare's bot detection requires JavaScript execution, which bare requests can't do.

Making the embed step best-effort unblocks login for these users. Without the LB cookie, preauthorized may occasionally hit a different backend, but that's better than login failing 100%.

Diagnosed via logfire session 5ad71f1d12204642cf-mitigated: challenge in the 403 response.

Test plan

  • 163 tests pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved SSO/authentication resilience: the login flow now tolerates certain provider responses and continues to obtain tokens even if intermediate verification steps fail, reducing login interruptions.
  • Chores

    • Version bumped from 0.7.3 to 0.7.4.

Some users get 403 from Cloudflare on the embed step. Since it only
sets a load balancer cookie, failure shouldn't block login.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 18, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d62d4809-a639-4dd8-9ae7-f4b9045f441d

📥 Commits

Reviewing files that changed from the base of the PR and between afb2adf and ba32a6d.

📒 Files selected for processing (1)
  • src/garth/sso.py

Walkthrough

Adds a browser-like header set SSO_HEADERS and uses it for SSO requests; the initial SSO sign-in request and the best-effort GET to /portal/sso/embed now merge these headers (with different Sec-Fetch-Site values). The Cloudflare LB cookie setup is changed to be best-effort (GarthException is swallowed). Version bumped to 0.7.4.

Changes

Cohort / File(s) Summary
SSO flow & headers
src/garth/sso.py
Introduce SSO_HEADERS: dict[str, str]; merge SSO_HEADERS into headers for initial sign-in (adds "Sec-Fetch-Site": "none"); replace direct Cloudflare cookie setup with a best-effort GET to "/portal/sso/embed" using merged SSO_HEADERS + "Sec-Fetch-Site": "same-origin" and a referrer; swallow GarthException on failure so token retrieval continues.
Version bump
src/garth/version.py
Incremented __version__ from "0.7.3" to "0.7.4".

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

🚥 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 directly describes the main change: making the /portal/sso/embed step best-effort to handle Cloudflare 403 challenges. This matches the core objective of preventing Cloudflare challenges from blocking the login flow.
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 fix-embed-403
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (868af84) to head (ba32a6d).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main      #210      +/-   ##
===========================================
+ Coverage   99.97%   100.00%   +0.02%     
===========================================
  Files          68        68              
  Lines        3558      3560       +2     
===========================================
+ Hits         3557      3560       +3     
+ Misses          1         0       -1     
Flag Coverage Δ
unittests 100.00% <100.00%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (1)
src/garth/sso.py (1)

222-227: Consider narrowing the exception handling or adding logging.

The bare except GarthException: pass catches all HTTP errors (including 500s, network failures, auth issues), not just the 403 Cloudflare challenge mentioned in the comment. While the step is intentionally best-effort, silently swallowing all exceptions reduces observability for diagnosing unexpected failures.

Options to consider:

  1. Check for specific 403 status and only suppress that
  2. Add minimal logging (e.g., logger.debug) so failures are traceable
♻️ Option 1: Narrow to 403 only
     # Sets Cloudflare LB cookie for backend pinning — best-effort
     # (Cloudflare may challenge with 403 for bot detection)
     try:
         client.get("sso", "/portal/sso/embed")
-    except GarthException:
-        pass
+    except GarthHTTPError as e:
+        if e.error.response is not None and e.error.response.status_code == 403:
+            pass  # Expected Cloudflare challenge
+        else:
+            raise
♻️ Option 2: Log failures for observability
+import logging
+
+logger = logging.getLogger(__name__)
+
 # In _complete_login:
     try:
         client.get("sso", "/portal/sso/embed")
-    except GarthException:
-        pass
+    except GarthException as e:
+        logger.debug("Cloudflare LB cookie setup failed (non-blocking): %s", e)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/garth/sso.py` around lines 222 - 227, The try/except around
client.get("sso", "/portal/sso/embed") in sso.py currently swallows all
GarthException instances; narrow handling to only suppress Cloudflare 403s
and/or record failures: inspect the raised GarthException from client.get and if
its HTTP status is 403 only, suppress it; otherwise call the module logger
(e.g., logger.debug or logger.warning) with the exception details and re-raise
or handle appropriately so unexpected HTTP 5xx/network/auth errors are visible;
update the exception block referencing client.get and GarthException and use the
existing logger variable (or create one) to emit minimal observability.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/garth/sso.py`:
- Around line 222-227: The try/except around client.get("sso",
"/portal/sso/embed") in sso.py currently swallows all GarthException instances;
narrow handling to only suppress Cloudflare 403s and/or record failures: inspect
the raised GarthException from client.get and if its HTTP status is 403 only,
suppress it; otherwise call the module logger (e.g., logger.debug or
logger.warning) with the exception details and re-raise or handle appropriately
so unexpected HTTP 5xx/network/auth errors are visible; update the exception
block referencing client.get and GarthException and use the existing logger
variable (or create one) to emit minimal observability.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 35a110fe-9c66-44d9-997e-6b644adab6d9

📥 Commits

Reviewing files that changed from the base of the PR and between 868af84 and afb2adf.

📒 Files selected for processing (2)
  • src/garth/sso.py
  • src/garth/version.py

Cloudflare challenges requests that look like bots. The sign-in page
and /portal/sso/embed are web pages (not APIs) and need browser-like
headers (User-Agent, Sec-Fetch-*, Accept) to avoid 403 challenges.
API endpoints (preauthorized, exchange) keep the GCM-iOS user agent.

Also: pragma no cover on the embed except branch (only hit when
Cloudflare challenges despite correct headers).

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@matin matin merged commit c319358 into main Mar 18, 2026
25 of 26 checks passed
@matin matin deleted the fix-embed-403 branch March 18, 2026 10:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant