Skip to content

fix(OAuth): flagsmith login never completes for a new signup - #8281

Open
khvn26 wants to merge 3 commits into
mainfrom
fix/cli-login-signup-flow
Open

fix(OAuth): flagsmith login never completes for a new signup#8281
khvn26 wants to merge 3 commits into
mainfrom
fix/cli-login-signup-flow

Conversation

@khvn26

@khvn26 khvn26 commented Aug 13, 2026

Copy link
Copy Markdown
Member

Thanks for submitting a PR! Please check the boxes below:

  • I have read the Contributing Guide.
  • I have added information to docs/ if required so people know about the feature.
  • I have filled in the "Changes" section below.
  • I have filled in the "How did you test this code" section below.

Changes

Closes #8165.

In this PR, we:

  1. Route OAuth-driven signups to onboarding.
  2. Redirect to OAuth consent screen once onboarding (both control and single-page) has provisioned the organisation.
  3. Stop two things moving the browser out from under that request.
  4. Serve the consent screen's scope descriptions from the API.

How did you test this code?

Added unit tests.

Manually, against a local API and dashboard, with flagsmith-cli as the client and scope=admin-api. Open /oauth/authorize/?client_id=flagsmith-cli&response_type=code&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&code_challenge=…&code_challenge_method=S256&scope=admin-api&state=x while logged out, then sign up.

Will test the Google path in production.

khvn26 added 2 commits August 13, 2026 12:50
A client that opens the consent screen while the user is logged out has
its request stashed in the redirect cookie. `App.onLogin` restored that
cookie only after its "no organisation yet" branch, which returns early
to route new users into onboarding - so a signup dropped the request and
left the client waiting until it timed out.

Onboarding now hands the request on instead of losing it. It provisions
the organisation the client needs, then answers the request itself: from
the create-organisation page once the organisation exists, and from the
single-page flow once its workspace is ready, never on load, since
bootstrapping is a chain of creates that must not be interrupted
half-done.

Two things had to stop moving the browser out from under that. Creating
an organisation re-fires `onLogin` while the organisation list is still
refreshing, whose onboarding branch would then yank the user off a
request they were about to authorise; it now leaves the consent screen
alone. And a page load stashed the loaded path over a waiting request,
so a reload mid-onboarding - which its own Try again performs - lost it.

Alongside, on the same path: identity-provider signups now get the same
onboarding entry decision as `register` makes, rather than falling
through to the legacy create-organisation page; the redirect is
re-encoded when re-embedded, so a redirect carrying its own query is no
longer truncated at its first `&`; and the cookie holding it expires in
an hour instead of thirty days, so an abandoned login cannot hijack a
later one.

beep boop
The consent screen described the `mcp` scope from a map hardcoded in the
page, so what a scope grants was written a repository apart from the
policy that decides who may hold it, and `admin-api` - the scope the CLI
asks for - got only the registry's one-line label.

Describe scopes in `oauth2_metadata` beside that policy and return
`{label, grants}` per scope, letting the page render whatever it is
given. A scope with no grants written falls back to its label, so adding
one cannot produce an empty permissions box.

Both lists now state that the token acts with the user's own permissions:
neither scope is enforced per request, so the previous wording implied a
boundary that does not exist.

beep boop
@khvn26
khvn26 requested review from a team as code owners August 13, 2026 11:55
@khvn26
khvn26 requested review from kyle-ssg and removed request for a team August 13, 2026 11:55
@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
flagsmith-frontend-preview Ready Ready Preview Aug 13, 2026 12:16pm
flagsmith-frontend-staging Ready Ready Preview Aug 13, 2026 12:16pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview Aug 13, 2026 12:16pm

Request Review

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Docker builds report

Image Build Status Security report
ghcr.io/flagsmith/flagsmith-api-test:pr-8281 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-e2e:pr-8281 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-api:pr-8281 Finished ✅ Results
ghcr.io/flagsmith/flagsmith:pr-8281 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-private-cloud:pr-8281 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-frontend:pr-8281 Finished ✅ Results

@github-actions github-actions Bot added the fix label Aug 13, 2026
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4aa02737-71c6-47e8-98ec-3f12fafacb97

📥 Commits

Reviewing files that changed from the base of the PR and between 9f38cdd and ebb86f1.

📒 Files selected for processing (1)
  • frontend/web/project/api.ts

📝 Walkthrough

Walkthrough

The API now returns structured OAuth scope descriptions with labels and grants. The frontend uses this metadata on the consent page. Pending authorisation redirects persist through login, organisation creation, and onboarding. Redirect values are encoded, and redirect cookies expire after one hour. OAuth sign-up handling identifies new users without invitations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Mergeability Score: 🔵 Low · up to ebb86

This PR routes new OAuth signups through onboarding and resumes authorization after organization setup. It is mergeable with owner awareness that the changed TypeScript signatures still need to be aligned with the repository’s named-union-type requirement.


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.

@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

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

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #8281   +/-   ##
=======================================
  Coverage   98.75%   98.75%           
=======================================
  Files        1569     1571    +2     
  Lines       62827    62855   +28     
=======================================
+ Hits        62042    62070   +28     
  Misses        785      785           

☔ View full report in Codecov by Harness.
📢 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@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: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4f78302d-242d-4790-9a8b-b40d8a88f70d

📥 Commits

Reviewing files that changed from the base of the PR and between dc99d65 and 9f38cdd.

📒 Files selected for processing (17)
  • api/oauth2_metadata/constants.py
  • api/oauth2_metadata/mappers.py
  • api/oauth2_metadata/types.py
  • api/oauth2_metadata/views.py
  • api/tests/unit/oauth2_metadata/test_authorize_view.py
  • api/tests/unit/oauth2_metadata/test_mappers.py
  • frontend/common/stores/account-store.js
  • frontend/common/types/responses.ts
  • frontend/common/utils/__tests__/pendingAuthorisation.test.ts
  • frontend/common/utils/pendingAuthorisation.ts
  • frontend/web/components/App.js
  • frontend/web/components/pages/CreateOrganisationPage.tsx
  • frontend/web/components/pages/OAuthAuthorizePage.tsx
  • frontend/web/components/pages/home-page/HomePage.tsx
  • frontend/web/components/pages/onboarding/OnboardingFlow/OnboardingFlow.tsx
  • frontend/web/main.js
  • frontend/web/project/api.ts

Comment thread frontend/common/utils/pendingAuthorisation.ts
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor
✅ private-cloud · depot-ubuntu-latest-16 — run #19298 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  32.6 seconds
commit  ebb86f1
info  🔄 Run: #19298 (attempt 1)

🗂️ Previous results
✅ oss · depot-ubuntu-latest-arm-16 — run #19298 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  46.9 seconds
commit  ebb86f1
info  🔄 Run: #19298 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #19298 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  40.1 seconds
commit  ebb86f1
info  🔄 Run: #19298 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-arm-16 — run #19298 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  23 passed
skipped  1 skipped

Details

stats  24 tests across 18 suites
duration  1 minute, 26 seconds
commit  ebb86f1
info  🔄 Run: #19298 (attempt 1)

Skipped tests

firefox › tests/onboarding-tests.pw.ts › Onboarding › New user connects via the single-page onboarding flow @oss

✅ private-cloud · depot-ubuntu-latest-arm-16 — run #19296 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  1 minute, 5 seconds
commit  9f38cdd
info  🔄 Run: #19296 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-16 — run #19296 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  47.2 seconds
commit  9f38cdd
info  🔄 Run: #19296 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #19296 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  46.6 seconds
commit  9f38cdd
info  🔄 Run: #19296 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #19296 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  41.6 seconds
commit  9f38cdd
info  🔄 Run: #19296 (attempt 1)

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Visual Regression

19 screenshots compared. See report for details.
View full report

It carries what the two accepted forms mean, which a comment was doing.

beep boop

@emyller emyller left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Backend changes look good, bar one test tweak.

Comment on lines +10 to +25
SCOPE_GRANTS: dict[str, tuple[str, ...]] = {
SCOPE_MCP: (
"Manage feature flags, toggle states, and update values",
"Create and manage audience targeting segments",
"View and configure environments",
"View and update project settings",
"Create and review change requests",
"View organisation details, roles, and groups",
"Act with your own permissions, in every organisation you belong to",
),
SCOPE_ADMIN_API: (
"Act with your own permissions, in every organisation you belong to",
"Manage feature flags, segments, environments and projects you can access",
"Manage organisation settings, members and roles you can access",
),
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
SCOPE_GRANTS: dict[str, tuple[str, ...]] = {
SCOPE_MCP: (
"Manage feature flags, toggle states, and update values",
"Create and manage audience targeting segments",
"View and configure environments",
"View and update project settings",
"Create and review change requests",
"View organisation details, roles, and groups",
"Act with your own permissions, in every organisation you belong to",
),
SCOPE_ADMIN_API: (
"Act with your own permissions, in every organisation you belong to",
"Manage feature flags, segments, environments and projects you can access",
"Manage organisation settings, members and roles you can access",
),
}
SCOPE_GRANTS: dict[str, frozenset[str, ...]] = {
SCOPE_MCP: frozenset([
"Manage feature flags, toggle states, and update values",
"Create and manage audience targeting segments",
"View and configure environments",
"View and update project settings",
"Create and review change requests",
"View organisation details, roles, and groups",
"Act with your own permissions, in every organisation you belong to",
]),
SCOPE_ADMIN_API: frozenset([
"Act with your own permissions, in every organisation you belong to",
"Manage feature flags, segments, environments and projects you can access",
"Manage organisation settings, members and roles you can access",
]),
}

nit: The immutability of tuples does not justify using them as immutable collections. Please, if you agree, prefer to use tuples when they could be interchanged with their verbose brother, NamedTuple.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's hard to justify frozenset here considering we don't need O(1) membership checks here; and using a mutable type like list makes things geniunely worse in my opinion.

I'm sorry, I'd rather you come to terms with the fact that tuple[str, ...] is a legitimate type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm sorry, I'd rather you come to terms with the fact that tuple[str, ...] is a legitimate type.

I continue to disagree, and I'd be glad to argument, though this isn't worth it. I will try not to pick these from now on.

Comment thread api/tests/unit/oauth2_metadata/test_authorize_view.py
@khvn26
khvn26 requested a review from emyller August 13, 2026 16:10

@emyller emyller left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved from backend standpoint.

@talissoncosta talissoncosta left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good on FE side! 💯

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

Labels

api Issue related to the REST API fix front-end Issue related to the React Front End Dashboard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flagsmith login experience is broken for signups

4 participants