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

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: executeEmbeddedSignInFlowV2 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 (e.g. @thunderid/express, which initiates this V2 flow server-side) 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. The V1 executeEmbeddedSignInFlow targets /oauth2/authn (a legacy, non-Thunder endpoint) and is out of scope.
  • 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 executeEmbeddedSignInFlowV2 (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.

A browser SPA initiating a sign-in flow directly via POST /flow/execute
passes a public applicationId, which the server cannot use to authenticate
the client. Browser SPAs must use the redirect-based authorization_code +
PKCE flow, which has a true equivalent in the OAuth /authorize handler.

- executeEmbeddedSignInFlowV2 now throws a ThunderIDRuntimeError when a new
  sign-in flow is initiated (applicationId + flowType) in a browser context.
  The react, vue and browser SDKs reach this via the shared core, so all are
  covered. Server-side (confidential client) initiation is still allowed
  (non-browser), and browser-side continuation with an executionId — the
  hosted Gate path — is unaffected.
- Scoped to sign-in only: /authorize initiates authentication only, so
  registration and recovery have no redirect-based equivalent and are
  left unchanged.
- Add JSDoc guidance on the react and vue embedded SignIn components and a
  note in the JavaScript SDK README; update tests to assert the browser
  block, server-side allowance, and browser continuation.

Refs thunder-id/thunderid#3217
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