Skip to content

[WIP] Block direct sign-in flow initiation from browser SPAs#3

Open
Malith-19 wants to merge 1 commit into
thunder-id:mainfrom
Malith-19:block-spa-signin-initiation
Open

[WIP] Block direct sign-in flow initiation from browser SPAs#3
Malith-19 wants to merge 1 commit into
thunder-id:mainfrom
Malith-19:block-spa-signin-initiation

Conversation

@Malith-19

@Malith-19 Malith-19 commented Jun 23, 2026

Copy link
Copy Markdown
Member

Purpose

Block browser SPAs from initiating a sign-in flow directly via POST /flow/execute, and require the redirect-based OAuth2 authorization_code + PKCE flow instead.

A browser SPA initiating a flow passes a public applicationId, which the server cannot use to authenticate the client. This is the SDK-side item of the secured-by-default model agreed in the design discussion. Since the SDKs have not had a release yet, this removes the capability rather than deprecating it. The config-time backend guard (a public-client SPA must use the authorization_code grant) is tracked separately under thunder-id/thunderid#3216.

Approach

  • Hard block at the shared core: executeEmbeddedSignInFlow throws a ThunderIDRuntimeError when a new sign-in flow is initiated (applicationId + flowType) in a browser context. @thunderid/browser, @thunderid/react, and @thunderid/vue all reach this function through the core, so a single guard covers every browser SPA SDK.
  • Non-SPAs still work: the guard is keyed on the runtime environment (window/document). Server-side / confidential-client usage runs in Node, so it may still initiate the flow. The hosted Gate UI is also unaffected — it only continues a redirect-initiated flow via executionId, never initiates.
  • Scoped to sign-in only — deliberately. Thunder's /authorize handler initiates authentication only, so registration and recovery have no redirect-based equivalent and are left unchanged.
  • Developer guidance: JSDoc on the react <SignIn> and vue SignIn components, plus a note in the JavaScript SDK README.

Note: client-side detection is an SDK-level fail-fast for honest developers, not a security boundary — the real enforcement is the backend guard (thunder-id/thunderid#3216).

Related Issues

Related PRs

Checklist


⚠️ Breaking Changes

🔧 Summary of Breaking Changes

Browser SPAs can no longer initiate a sign-in flow directly via executeEmbeddedSignInFlow (the embedded <SignIn /> component or useThunderID().signIn({ applicationId }) used standalone). Doing so now throws a ThunderIDRuntimeError.

💥 Impact

Browser SPA apps that drove sign-in with the app-native (embedded) flow. Server-side clients, and the hosted sign-in (Gate) continuation path, are not affected. The SDKs have not been released, so no published consumers are impacted.

🔄 Migration Guide

Configure the application for the authorization_code grant with a registered redirect_uri, and sign in via the redirect-based flow — for example @thunderid/browser's signIn() or @thunderid/react's <SignInButton />. See Register an application.

Security checks

  • Followed secure coding standards in WSO2 Secure Coding Guidelines
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

Note

[WIP] — opened for testing and fine-tuning before review.

Summary by CodeRabbit

  • New Features

    • Clarified supported sign-in paths for browser apps, including the recommended redirect-based login flow.
    • Added guidance in developer-facing docs and component notes for React and Vue integrations.
  • Bug Fixes

    • Prevented browser-based apps from starting a new embedded sign-in flow directly, with a clear runtime error and guidance to use the supported flow instead.
    • Kept existing flow continuation and server-side sign-in behavior working as expected.
  • Tests

    • Added coverage for browser, server, and flow-continuation scenarios.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Malith-19, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d55d06ef-785a-411c-8e4c-5329d3e90cb4

📥 Commits

Reviewing files that changed from the base of the PR and between 6160a7d and 5c251cf.

📒 Files selected for processing (5)
  • packages/javascript/README.md
  • packages/javascript/src/api/__tests__/executeEmbeddedSignInFlow.test.ts
  • packages/javascript/src/api/executeEmbeddedSignInFlow.ts
  • packages/react/src/components/presentation/auth/SignIn/SignIn.tsx
  • packages/vue/src/components/auth/sign-in/SignIn.ts
📝 Walkthrough

Walkthrough

executeEmbeddedSignInFlow gains a browser detection helper (isBrowser()) and a runtime guard that throws ThunderIDRuntimeError when a browser SPA attempts to initiate a new embedded sign-in flow directly. Tests cover browser block, server allow, and browser continuation. README, React SignIn, and Vue SignIn JSDoc are updated to document this restriction.

Changes

Browser SPA sign-in guard

Layer / File(s) Summary
Runtime guard and isBrowser helper
packages/javascript/src/api/executeEmbeddedSignInFlow.ts
Imports ThunderIDRuntimeError, adds isBrowser() via window.document detection, and throws with a specific error code when isNewFlowStart is true in a browser context.
Browser/server environment tests
packages/javascript/src/api/__tests__/executeEmbeddedSignInFlow.test.ts
Adds afterEach cleanup of globalThis.window and three cases: blocked browser initiation (zero fetch calls), allowed server initiation, and allowed browser continuation via executionId.
Documentation
packages/javascript/README.md, packages/react/src/components/presentation/auth/SignIn/SignIn.tsx, packages/vue/src/components/auth/sign-in/SignIn.ts
Adds README section and @remarks JSDoc blocks in React and Vue SignIn components documenting the unsupported SPA direct initiation and pointing to redirect-based OAuth2 PKCE flow.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 Hop, hop, the browser said,
"Can I start sign-in flows ahead?"
The SDK shook its furry head —
Use PKCE redirects instead!
The guard is set, the error's thrown,
No sneaky SPA flows alone. 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: blocking direct browser SPA sign-in flow initiation.
Description check ✅ Passed The description follows the template and includes purpose, approach, related items, checklist, security checks, and breaking changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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

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.

@Malith-19 Malith-19 force-pushed the block-spa-signin-initiation branch from 6160a7d to 5c251cf Compare June 30, 2026 12:01
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