Skip to content

Implement Magic Link Frontend#2987

Open
RandithaK wants to merge 39 commits into
thunder-id:mainfrom
RandithaK:feature/Magic-Link-Frontend
Open

Implement Magic Link Frontend#2987
RandithaK wants to merge 39 commits into
thunder-id:mainfrom
RandithaK:feature/Magic-Link-Frontend

Conversation

@RandithaK
Copy link
Copy Markdown
Contributor

@RandithaK RandithaK commented May 25, 2026

Purpose

This PR implements the end-to-end Magic Link Authentication flow across the frontend, SDKs, and backend. It introduces Magic Link routing and callback handling in the React ecosystem, updates the flow/login executors in the backend and Console UI, and refactors storage handling in the JavaScript SDK.

Approach

  1. SDK Enhancements:
    • Added MagicLinkCallback to the React SDK and MagicLinkRoute to the React-Router SDK to handle the magic link verification process, route configuration, and redirect flows.
    • Refactored StorageManager.ts and SPAUtils.ts in the JavaScript/Browser SDKs to replace custom storage abstractions with standard sessionStorage mechanism.
  2. Backend & Flow Updates:
    • Updated the magic_link_executor.go to include the applicationID in query parameters and enhanced the flow with execution policies.
    • Enabled rendering of email_sent prompts to notify users during the verification process.
  3. Console Login Flow Configurations:
    • Updated Console UI data sets (templates.json, steps.json, executors.json, and widgets.json) to natively support Magic Link flow templates and execution properties.

Related Issues

Related PRs

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
    • Ran Vale and fixed all errors and warnings
  • Tests provided. (Add links if there are any)
    • Unit Tests
    • Integration Tests
  • Breaking changes. (Fill if applicable)
    • Breaking changes section filled.
    • breaking change label added.

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.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Magic Link authentication capability enabling email-based verification flows.
    • Added standalone and combined Magic Link authentication and registration templates.
    • Enhanced authentication flows with new Magic Link generation and verification modes.
  • Improvements

    • Updated authentication state persistence to use browser local storage for improved cross-tab session handling.
    • Extended translation support for Magic Link forms and email verification messaging.
    • Added configuration support for Magic Link endpoint routing.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

📝 Walkthrough

Walkthrough

This PR introduces Magic Link passwordless authentication by refactoring the backend executor, designing multi-step frontend flows for authentication and registration, integrating SDK callback handling, and migrating cross-SDK storage from sessionStorage to localStorage for better persistence across tabs.

Changes

Magic Link Authentication Feature

Layer / File(s) Summary
Backend executor refactor
backend/internal/flow/executor/constants.go, backend/internal/flow/executor/init.go, backend/internal/flow/executor/magic_link_executor.go, backend/internal/flow/executor/magic_link_executor_test.go
Rename MagicLinkAuthExecutor to MagicLinkExecutor, add GetExecutionPolicy to skip challenge validation in verify mode, wire applicationId into magic link generation query params, and expand test coverage for both generate and verify modes including destination attributes and token validation.
Backend configuration
backend/internal/system/config/config.go, backend/internal/system/config/runtimeconfig.go, backend/internal/system/config/runtimeconfig_test.go, backend/internal/authn/magiclink/service.go, backend/internal/authn/magiclink/service_test.go
Add GateClientMagicLinkPath config field with fallback derivation from base path, initialize GateClientMagicLinkURL at runtime with validation/fallback matching login-path pattern, and update service to use the magic link URL.
Frontend templates and models
frontend/apps/console/src/features/flows/data/templates.json, frontend/apps/console/src/features/flows/models/templates.ts, frontend/apps/console/src/features/flows/components/create-flow/SelectFlowTemplate.tsx
Add four new flow templates (Magic Link and Basic+Magic Link variants for authentication and registration), extend TemplateTypes with MAGIC_LINK and BASIC_MAGIC_LINK, and add corresponding icon mappings.
Frontend flow editor UI
frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/ExecutionExtendedProperties.tsx, frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/execution-properties/MagicLinkProperties.tsx, frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/execution-properties/constants.ts, frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/execution-properties/__tests__/MagicLinkProperties.test.tsx
Implement MagicLinkProperties component with mode selector (generate/verify), define MAGIC_LINK_MODES constant, wire into executor-specific properties panel, and add test coverage for mode selection and data updates.
Frontend login flow data
frontend/apps/console/src/features/login-flow/data/executors.json, frontend/apps/console/src/features/login-flow/data/steps.json, frontend/apps/console/src/features/login-flow/data/templates.json, frontend/apps/console/src/features/login-flow/data/widgets.json
Define Magic Link authentication template with email prompt → link generation → send → verify → assertion sequence, add email input step, configure executor steps with modes visible on resource panel, and create multi-step widget with trigger/email/generation/verification flows.
Frontend routes
frontend/apps/gate/src/constants/routes.ts, frontend/apps/gate/src/constants/__tests__/routes.test.ts, frontend/apps/gate/src/config/appRoutes.tsx, frontend/apps/console/vite.config.ts, frontend/apps/gate/vite.config.ts
Add /magiclink route constant, configure MagicLinkRoute in app routes, add route tests, and set Vite aliases for react-router in both console and gate apps.
SDK magic link callback
sdks/react/src/components/auth/Callback/MagicLinkCallback.tsx, sdks/react/src/components/auth/Callback/__tests__/MagicLinkCallback.test.tsx, sdks/react-router/src/components/MagicLinkRoute.tsx, sdks/react-router/src/__tests__/index.test.ts, sdks/react-router/src/index.ts, sdks/react/src/index.ts
Implement MagicLinkCallback component extracting id/token from URL, calling signIn, handling Redirection/Complete/Error flows, provide MagicLinkRoute wrapper for React Router apps with custom navigate support and error handling, export both from SDK entrypoints.
Storage migration to localStorage
sdks/browser/src/utils/SPAUtils.ts, sdks/javascript/src/StorageManager.ts, sdks/react/src/ThunderIDReactClient.ts, sdks/react/src/components/presentation/auth/SignIn/v2/SignIn.tsx, sdks/react/src/contexts/ThunderID/ThunderIDContext.ts, sdks/vue/src/ThunderIDVueClient.ts, sdks/vue/src/components/auth/sign-in/v2/SignIn.ts
Switch PKCE verifier and auth ID (thunderid_auth_id) persistence from sessionStorage to localStorage across browser, React, and Vue SDKs; update StorageManager to special-case PKCE entries in temporary data; remove storage from ThunderIDContext props.
Internationalization and misc
backend/cmd/server/bootstrap/i18n/en-US.json, frontend/packages/i18n/src/locales/en-US.ts, samples/apps/react-sdk-sample/package.json, tests/integration/flow/authentication/magic_link_test.go
Add Magic Link translation strings for signin/signup flows, update sample app to use workspace reference for React SDK, and update integration test to use MagicLinkExecutor constant.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • brionmario
  • DonOmalVindula
  • jeradrutnam
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Implement Magic Link Frontend' is vague and overly broad, not reflecting that this PR encompasses substantial backend, SDK, and full-stack changes beyond just frontend implementation. Revise the title to be more specific and accurate, such as 'Implement end-to-end Magic Link authentication across frontend, SDKs, and backend' or 'Add Magic Link support to frontend, SDKs, and authentication flow'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is well-structured with Purpose, Approach, and Related Issues sections, clearly explaining the Magic Link implementation across SDKs, backend, and Console UI. All major changes are documented.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

frontend/apps/console/src/features/flows/components/create-flow/SelectFlowTemplate.tsx

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

frontend/apps/console/src/features/flows/models/templates.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/ExecutionExtendedProperties.tsx

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

  • 22 others

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 May 25, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Comment thread package.json Outdated
@RandithaK RandithaK force-pushed the feature/Magic-Link-Frontend branch from 48c1002 to 5c19cca Compare May 26, 2026 03:33
Comment thread backend/internal/flow/executor/magic_link_executor.go Outdated
Comment thread backend/internal/flow/executor/magic_link_executor.go Outdated
Comment thread backend/internal/oauth/oauth2/authz/auth_req_store.go Outdated
Comment thread backend/internal/oauth/oauth2/authz/service.go Outdated
Path string `yaml:"path" json:"path"`
LoginPath string `yaml:"login_path" json:"login_path"`
ErrorPath string `yaml:"error_path" json:"error_path"`
MagicLinkPath string `yaml:"magic_link_path" json:"magic_link_path"`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's not term this a magic link specific config, rather have it as a generic callback path for any internal returns. But don't make this misleading with external IDP callback path in the SDK too

Copy link
Copy Markdown
Contributor Author

@RandithaK RandithaK May 26, 2026

Choose a reason for hiding this comment

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

We are having a discussion in
#2736

Contributions are welcome 👍

Config Config `yaml:"config"`
ServerHome string `yaml:"server_home"`
GateClientLoginURL *url.URL
GateClientMagicLinkURL *url.URL
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same as previous comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We are having a discussion in
#2736

Contributions are welcome 👍

Comment thread frontend/apps/console/src/features/flows/data/templates.json Outdated
@RandithaK RandithaK force-pushed the feature/Magic-Link-Frontend branch 2 times, most recently from 4b90eed to e49b2c9 Compare May 27, 2026 05:39
@RandithaK RandithaK force-pushed the feature/Magic-Link-Frontend branch from e49b2c9 to 1b199b2 Compare May 27, 2026 06:08
@RandithaK RandithaK force-pushed the feature/Magic-Link-Frontend branch from 178de30 to 9e4a528 Compare May 27, 2026 08:26
@RandithaK RandithaK marked this pull request as ready for review May 27, 2026 08:32
Copilot AI review requested due to automatic review settings May 27, 2026 08:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds end-to-end Magic Link authentication support across backend, SDKs, router integrations, and console flow templates/config to enable passwordless sign-in/registration via emailed links.

Changes:

  • Introduces Magic Link callback handling in @thunderid/react and a MagicLinkRoute wrapper in @thunderid/react-router.
  • Updates executor naming and flow templates/config (backend + console) to use MagicLinkExecutor with generate/verify modes.
  • Adjusts storage behavior (notably authId and PKCE-related data) and adds runtime config support for a dedicated Magic Link callback path.

Reviewed changes

Copilot reviewed 42 out of 43 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/integration/flow/authentication/magic_link_test.go Updates integration test flow executor name to MagicLinkExecutor.
sdks/vue/src/components/auth/sign-in/v2/SignIn.ts Switches authId persistence from sessionStorage to localStorage.
sdks/vue/src/ThunderIDVueClient.ts Switches thunderid_auth_id lookup/persist to localStorage.
sdks/react/src/index.ts Exports new MagicLinkCallback component.
sdks/react/src/contexts/ThunderID/ThunderIDContext.ts Removes storage from context props/defaults.
sdks/react/src/components/presentation/auth/SignIn/v2/SignIn.tsx Switches thunderid_auth_id persistence from sessionStorage to localStorage.
sdks/react/src/components/auth/Callback/tests/MagicLinkCallback.test.tsx Adds tests for MagicLinkCallback.
sdks/react/src/components/auth/Callback/MagicLinkCallback.tsx Adds Magic Link callback handler component.
sdks/react/src/ThunderIDReactClient.ts Switches thunderid_auth_id lookup/persist to localStorage.
sdks/react-router/src/index.ts Exports new MagicLinkRoute.
sdks/react-router/src/components/MagicLinkRoute.tsx Adds a React Router wrapper around MagicLinkCallback.
sdks/react-router/src/tests/index.test.ts Verifies MagicLinkRoute export.
sdks/javascript/src/StorageManager.ts Stores PKCE verifier-related temporary data in localStorage and manages cleanup.
sdks/browser/src/utils/SPAUtils.ts Switches PKCE get/set/remove from sessionStorage to localStorage.
samples/apps/react-sdk-sample/package.json Uses workspace dependency for @thunderid/react.
frontend/packages/i18n/src/locales/en-US.ts Adds Magic Link translation keys for sign-in/sign-up flows.
frontend/apps/gate/vite.config.ts Adds react-router resolve alias for linked packages.
frontend/apps/gate/src/constants/routes.ts Adds ROUTES.AUTH.MAGIC_LINK.
frontend/apps/gate/src/constants/tests/routes.test.ts Adds coverage for ROUTES.AUTH.MAGIC_LINK.
frontend/apps/gate/src/config/appRoutes.tsx Registers /magiclink route using MagicLinkRoute.
frontend/apps/console/vite.config.ts Adds react-router resolve alias for linked packages.
frontend/apps/console/src/features/login-flow/data/widgets.json Adds Magic Link widget definition.
frontend/apps/console/src/features/login-flow/data/templates.json Adds Magic Link template definition for flow builder.
frontend/apps/console/src/features/login-flow/data/steps.json Adds an Email view step definition used by Magic Link flows.
frontend/apps/console/src/features/login-flow/data/executors.json Adds Magic Link executor steps for generate and verify modes.
frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/execution-properties/constants.ts Adds MAGIC_LINK_MODES options.
frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/execution-properties/tests/MagicLinkProperties.test.tsx Adds tests for Magic Link mode selection UI.
frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/execution-properties/MagicLinkProperties.tsx Adds executor properties UI for selecting Magic Link mode.
frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/ExecutionExtendedProperties.tsx Wires Magic Link executor to use MagicLinkProperties.
frontend/apps/console/src/features/flows/models/templates.ts Adds MAGIC_LINK and BASIC_MAGIC_LINK template types.
frontend/apps/console/src/features/flows/data/templates.json Adds Magic Link templates for authentication/registration (incl. combined basic+magic).
frontend/apps/console/src/features/flows/components/create-flow/SelectFlowTemplate.tsx Adds icons for the new Magic Link templates.
backend/internal/system/config/runtimeconfig_test.go Adds test for invalid magic link path fallback; minor hostname constant reuse.
backend/internal/system/config/runtimeconfig.go Adds GateClientMagicLinkURL and magic link path parsing/fallback.
backend/internal/system/config/config.go Adds MagicLinkPath to gate client config and defaults it; introduces localhost const reuse.
backend/internal/flow/executor/magic_link_executor_test.go Renames executor/test suite and adds applicationId propagation + execution policy test.
backend/internal/flow/executor/magic_link_executor.go Renames executor, adds GetExecutionPolicy, and includes applicationId in generated link params.
backend/internal/flow/executor/init.go Registers executor under ExecutorNameMagicLink.
backend/internal/flow/executor/constants.go Renames executor constant to ExecutorNameMagicLink.
backend/internal/authn/magiclink/service_test.go Updates runtime config and expectation to use /gate/magiclink path.
backend/internal/authn/magiclink/service.go Uses GateClientMagicLinkURL when building magic link URLs.
backend/cmd/server/bootstrap/i18n/en-US.json Adds Magic Link translation keys for server bootstrap i18n.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (7)

sdks/react/src/components/auth/Callback/tests/MagicLinkCallback.test.tsx:1

  • The test sets the callback URL parameter as executionId=..., but MagicLinkCallback reads the execution id from the id query parameter (urlParams.get('id')). As written, this test should fail because executionId will be null in the component and the flow will go through the error redirect path. Update the test URL to use id=exec-1 (or adjust component parsing to accept both id and executionId consistently with how links are generated).
    sdks/react/src/components/auth/Callback/tests/MagicLinkCallback.test.tsx:1
  • The test sets the callback URL parameter as executionId=..., but MagicLinkCallback reads the execution id from the id query parameter (urlParams.get('id')). As written, this test should fail because executionId will be null in the component and the flow will go through the error redirect path. Update the test URL to use id=exec-1 (or adjust component parsing to accept both id and executionId consistently with how links are generated).
    sdks/react/src/components/auth/Callback/MagicLinkCallback.tsx:1
  • The component reads the execution id from the id query parameter, but the error message says “Missing executionId...”, which is misleading and also conflicts with the test expectations. Consider either (mandatory) updating the message to reference id (e.g., “Missing id or token...”), or (optional) supporting both id and executionId for backward compatibility and keeping the message aligned with the accepted parameter names.
    sdks/react/src/components/auth/Callback/MagicLinkCallback.tsx:1
  • The component reads the execution id from the id query parameter, but the error message says “Missing executionId...”, which is misleading and also conflicts with the test expectations. Consider either (mandatory) updating the message to reference id (e.g., “Missing id or token...”), or (optional) supporting both id and executionId for backward compatibility and keeping the message aligned with the accepted parameter names.
    sdks/react/src/components/auth/Callback/MagicLinkCallback.tsx:1
  • crypto.randomUUID() isn’t available in all runtime environments (older browsers, some test runners, or restricted contexts), which can cause a runtime crash in the callback route. Use a safe fallback when crypto.randomUUID is unavailable (e.g., generate a random string with crypto.getRandomValues or a small helper).
    sdks/react/src/contexts/ThunderID/ThunderIDContext.ts:1
  • Removing Pick<ThunderIDReactConfig, 'storage'> from ThunderIDContextProps is a breaking type change for SDK consumers that relied on storage being available via context (and the default context value previously included storage: 'sessionStorage'). If the storage option is being deprecated/removed in favor of a fixed behavior, it should be reflected in public API docs/changelog and ideally accompanied by a clear migration path; otherwise, consider keeping the field (even deprecated) to avoid breaking downstream builds.
    sdks/javascript/src/StorageManager.ts:1
  • Iterating localStorage via Object.keys(localStorage) is not the most robust way to enumerate storage entries across environments; the standard approach is iterating localStorage.length and calling localStorage.key(i). Consider switching to the standard iteration pattern here (and similarly in removeTemporaryData) to avoid missing keys in edge environments and to be consistent with the Storage API.

Comment thread backend/internal/flow/executor/magic_link_executor.go
Comment thread sdks/browser/src/utils/SPAUtils.ts
Comment thread sdks/browser/src/utils/SPAUtils.ts
Comment thread sdks/browser/src/utils/SPAUtils.ts
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.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/internal/authn/magiclink/service.go`:
- Line 199: Update the docs to reflect the new config field and routing change:
document the new GateClientMagicLinkPath config (environment variable name, YAML
key, default value and semantics) and update any guides that reference magic
link callbacks to describe the new URL structure produced by
config.GetServerRuntime().GateClientMagicLinkURL (previously
GateClientLoginURL), plus deployment notes on how to set the new path; ensure
README/docs pages that mention the magic link callback, client endpoint, or
GateClientLoginURL are updated to reference
GateClientMagicLinkURL/GateClientMagicLinkPath and include examples of env/YAML
usage and expected callback URLs.

In `@backend/internal/system/config/config.go`:
- Around line 76-83: Add documentation updates for the new magic link
configuration and executor contract: document the
server.gate_client.magic_link_path config option (field MagicLinkPath in the
config struct) including its purpose, default value pattern
"<gate_client.path>/magiclink" and expected URL format in
docs/content/guides/getting-started/configuration.mdx; update
backend/authentication flow docs to reflect the MagicLinkExecutor rename (was
MagicLinkAuthExecutor), the two execution modes "generate" and "verify" and the
verify execution policy semantics and how flow authors should use them; and
update API/authentication docs to show the magic link callback URL/query
contract now includes applicationId so frontend/backends know how to construct
and validate callback requests (add examples and expected query parameters) in
docs/content/apis.mdx or a guides authentication doc.

In `@frontend/apps/console/src/features/login-flow/data/templates.json`:
- Around line 1593-1641: The template advances directly from the SEND_EMAIL_ID
task (EmailExecutor send) to VERIFY_LINK_ID (MagicLinkExecutor verify) which
skips the intermediate "email_sent" view that must wait for the user/callback
token; add an intermediate node (e.g., EMAIL_SENT_VIEW_ID) representing the
email_sent prompt/view and change the SEND_EMAIL_ID action.onSuccess to point to
EMAIL_SENT_VIEW_ID, then have that view transition (on user action or callback)
to VERIFY_LINK_ID so MagicLinkExecutor.verify only runs after the token/input is
provided; ensure any references like EmailExecutor, MagicLinkExecutor,
SEND_EMAIL_ID, VERIFY_LINK_ID, and AUTH_ASSERT_ID are updated to use the new
EMAIL_SENT_VIEW_ID in the flow.
- Around line 1665-1675: The END node with id "END" currently lacks the
completion executor, so update its node object to include the same executor
wiring used elsewhere: add an executor entry referencing
"LoginCompletionExecutor" (the post-auth completion/redirect handler) so that
when the flow reaches the END node—especially after "AuthAssertExecutor"
succeeds—it triggers the normal completion/redirect path; locate the node with
"id": "END" in templates.json and add the executor configuration consistent with
other templates that wire END through LoginCompletionExecutor.

In `@frontend/apps/console/src/features/login-flow/data/widgets.json`:
- Around line 1231-1259: The three component IDs in the reusable email-sent
widget are hard-coded and will collide when the widget is instantiated multiple
times; replace the fixed ids "image_logo_sent", "display_email_sent_heading",
and "display_email_sent_desc" with generated ID placeholders (e.g. use the
existing {{ID}} pattern used elsewhere in the widget like "{{ID}}_image_logo",
"{{ID}}_heading", "{{ID}}_description") so each instance gets unique ids and
update any internal references to those ids accordingly.

In `@sdks/react-router/src/index.ts`:
- Around line 25-26: Add documentation for the new Magic Link SDK components:
create docs for MagicLinkRoute and MagicLinkCallback under docs/content/sdks/
(or a new guide in docs/content/guides/) that describe each component's purpose,
public props (with types and defaults), usage examples showing how to import and
render MagicLinkRoute and MagicLinkCallback in a React Router app, and
integration steps (routing, expected URL/callback flow). Also include an
end-to-end Magic Link authentication section covering necessary backend
configuration (token/URL generation, callback endpoint), how the frontend
triggers and verifies the link, error handling and redirects, and
cross-references between the React and React Router SDK examples.

In `@sdks/react/src/components/auth/Callback/MagicLinkCallback.tsx`:
- Around line 111-123: The code reads the wrong query param key: change the
urlParams.get call that assigns executionId (currently using 'id') to use
'executionId' so the Magic Link callback recognizes the real parameter; update
the line that sets executionId (where executionId: string | null =
urlParams.get('id')) to urlParams.get('executionId') and keep the existing
clearTokenFromUrl(), error handling, and redirectWithError usage unchanged so
valid callbacks no longer trigger the missing-parameter branch.

In `@sdks/react/src/ThunderIDReactClient.ts`:
- Around line 280-281: The code reads/writes a global localStorage key
'thunderid_auth_id' (see authIdFromStorage and authId usage) which can collide
across tabs/instances; replace this with a namespaced storage helper that
composes the key from client/application id and a per-instance instanceId (e.g.,
`${appId}:${instanceId}:thunderid_auth_id`) and use that helper for all
get/set/remove operations; update the reads (authIdFromStorage), writes, and
cleanup logic (also at the other usage around lines 344-349) to call the shared
helper functions (e.g., getNamespacedAuthId, setNamespacedAuthId,
removeNamespacedAuthId) so each client instance uses its own scoped key and
avoids stale/cross-flow collisions.

In `@sdks/vue/src/ThunderIDVueClient.ts`:
- Around line 310-311: Replace the hardcoded global localStorage key
'thunderid_auth_id' with a scoped key and centralized helper: create a helper
(e.g., getScopedAuthKey(instanceIdOrClientId)) and accessor functions
(getAuthIdFromStorage(), setAuthIdToStorage(id), clearAuthIdFromStorage()) and
use those in ThunderIDVueClient where authIdFromStorage, authIdFromUrl and
authId are computed (and in the other occurrences around the file). The scoped
key should include a unique identifier for the client/instance/application
(e.g., `${baseKey}:${clientId}`) so stored auth IDs don’t bleed across tabs, and
replace direct localStorage.getItem/setItem/removeItem calls with the new helper
functions everywhere in this file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2a4c5136-930b-401d-b8e0-0697a3a70e7e

📥 Commits

Reviewing files that changed from the base of the PR and between 24d4d00 and 9e4a528.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (42)
  • backend/cmd/server/bootstrap/i18n/en-US.json
  • backend/internal/authn/magiclink/service.go
  • backend/internal/authn/magiclink/service_test.go
  • backend/internal/flow/executor/constants.go
  • backend/internal/flow/executor/init.go
  • backend/internal/flow/executor/magic_link_executor.go
  • backend/internal/flow/executor/magic_link_executor_test.go
  • backend/internal/system/config/config.go
  • backend/internal/system/config/runtimeconfig.go
  • backend/internal/system/config/runtimeconfig_test.go
  • frontend/apps/console/src/features/flows/components/create-flow/SelectFlowTemplate.tsx
  • frontend/apps/console/src/features/flows/data/templates.json
  • frontend/apps/console/src/features/flows/models/templates.ts
  • frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/ExecutionExtendedProperties.tsx
  • frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/execution-properties/MagicLinkProperties.tsx
  • frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/execution-properties/__tests__/MagicLinkProperties.test.tsx
  • frontend/apps/console/src/features/login-flow/components/resource-property-panel/extended-properties/execution-properties/constants.ts
  • frontend/apps/console/src/features/login-flow/data/executors.json
  • frontend/apps/console/src/features/login-flow/data/steps.json
  • frontend/apps/console/src/features/login-flow/data/templates.json
  • frontend/apps/console/src/features/login-flow/data/widgets.json
  • frontend/apps/console/vite.config.ts
  • frontend/apps/gate/src/config/appRoutes.tsx
  • frontend/apps/gate/src/constants/__tests__/routes.test.ts
  • frontend/apps/gate/src/constants/routes.ts
  • frontend/apps/gate/vite.config.ts
  • frontend/packages/i18n/src/locales/en-US.ts
  • samples/apps/react-sdk-sample/package.json
  • sdks/browser/src/utils/SPAUtils.ts
  • sdks/javascript/src/StorageManager.ts
  • sdks/react-router/src/__tests__/index.test.ts
  • sdks/react-router/src/components/MagicLinkRoute.tsx
  • sdks/react-router/src/index.ts
  • sdks/react/src/ThunderIDReactClient.ts
  • sdks/react/src/components/auth/Callback/MagicLinkCallback.tsx
  • sdks/react/src/components/auth/Callback/__tests__/MagicLinkCallback.test.tsx
  • sdks/react/src/components/presentation/auth/SignIn/v2/SignIn.tsx
  • sdks/react/src/contexts/ThunderID/ThunderIDContext.ts
  • sdks/react/src/index.ts
  • sdks/vue/src/ThunderIDVueClient.ts
  • sdks/vue/src/components/auth/sign-in/v2/SignIn.ts
  • tests/integration/flow/authentication/magic_link_test.go


if u == nil {
u = config.GetServerRuntime().GateClientLoginURL
u = config.GetServerRuntime().GateClientMagicLinkURL
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 27, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🔴 Documentation required for configuration change.

This change switches the magic link URL fallback from GateClientLoginURL to GateClientMagicLinkURL, introducing a new configuration field GateClientMagicLinkPath. This is both a configuration option change and a user-facing behavior change (magic links now route to a different client endpoint).

Per the coding guidelines, configuration options and user-facing behavior changes require corresponding documentation updates under docs/. Please add:

  • Configuration documentation for GateClientMagicLinkPath (environment variable, YAML key, default value, behavior)
  • Guide updates explaining the magic link callback endpoint and URL structure
  • Any relevant deployment notes about the new path configuration
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/internal/authn/magiclink/service.go` at line 199, Update the docs to
reflect the new config field and routing change: document the new
GateClientMagicLinkPath config (environment variable name, YAML key, default
value and semantics) and update any guides that reference magic link callbacks
to describe the new URL structure produced by
config.GetServerRuntime().GateClientMagicLinkURL (previously
GateClientLoginURL), plus deployment notes on how to set the new path; ensure
README/docs pages that mention the magic link callback, client endpoint, or
GateClientLoginURL are updated to reference
GateClientMagicLinkURL/GateClientMagicLinkPath and include examples of env/YAML
usage and expected callback URLs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai
Documentation would be taken in a separate PR

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.

@RandithaK, understood — I'll defer the documentation requirement to that separate PR.

Would you like me to open a GitHub issue to track the documentation updates needed for this change? That would cover:

  • Configuration docs for GateClientMagicLinkPath (env var, YAML key, default value)
  • Guide updates for the magic link callback endpoint and URL structure
  • Deployment notes for the new path configuration

🧠 Learnings used
Learnt from: KaveeshaPiumini
Repo: thunder-id/thunderid PR: 2835
File: backend/internal/agent/declarative_resource.go:21-39
Timestamp: 2026-05-20T05:39:33.413Z
Learning: Do not treat importing a package named "testing" in production Go files as an anti-pattern in this codebase. For .go files that are not *_test.go, this convention is used to expose *ForTest constructors (e.g., NewAgentExporterForTest, NewApplicationExporterForTest) that are guarded by a call like testing.Testing() and panic when not running under tests. When reviewing, only flag importing "testing" in non-test files if it is not part of this established *ForTest/guard+panic convention.

Comment on lines +76 to 83
Hostname string `yaml:"hostname" json:"hostname"`
Port int `yaml:"port" json:"port"`
Scheme string `yaml:"scheme" json:"scheme"`
Path string `yaml:"path" json:"path"`
LoginPath string `yaml:"login_path" json:"login_path"`
ErrorPath string `yaml:"error_path" json:"error_path"`
MagicLinkPath string `yaml:"magic_link_path" json:"magic_link_path"`
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 27, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🔴 Documentation Required
This PR introduces user-facing changes that are not covered by documentation updates under docs/.
Please update the relevant documentation before merging.

Missing documentation:

  • server.gate_client.magic_link_path configuration option: document purpose/default (<gate_client.path>/magiclink) and expected format in docs/content/guides/getting-started/configuration.mdx.
  • Magic Link executor contract changes (MagicLinkAuthExecutorMagicLinkExecutor, generate/verify modes, verify execution policy semantics): document flow-authoring impact in the relevant backend/authentication flow docs under docs/content/guides/.
  • Magic link callback URL/query contract update (now includes applicationId): document backend/frontend integration expectations in docs/content/apis.mdx or an authentication guide under docs/content/guides/.

As per coding guidelines: "If ANY of the above are detected and the PR does NOT include corresponding updates under docs/, post a single consolidated PR-level comment."

Also applies to: 757-759

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/internal/system/config/config.go` around lines 76 - 83, Add
documentation updates for the new magic link configuration and executor
contract: document the server.gate_client.magic_link_path config option (field
MagicLinkPath in the config struct) including its purpose, default value pattern
"<gate_client.path>/magiclink" and expected URL format in
docs/content/guides/getting-started/configuration.mdx; update
backend/authentication flow docs to reflect the MagicLinkExecutor rename (was
MagicLinkAuthExecutor), the two execution modes "generate" and "verify" and the
verify execution policy semantics and how flow authors should use them; and
update API/authentication docs to show the magic link callback URL/query
contract now includes applicationId so frontend/backends know how to construct
and validate callback requests (add examples and expected query parameters) in
docs/content/apis.mdx or a guides authentication doc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai
Documentation would be taken in a separate PR

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.

@RandithaK, understood! Would you like me to open a follow-up GitHub issue to track the missing documentation items so they don't get lost? I can capture all three items (the magic_link_path config option, the MagicLinkExecutor rename/modes, and the updated magic link callback URL contract) as actionable tasks in the issue.


🧠 Learnings used
Learnt from: KaveeshaPiumini
Repo: thunder-id/thunderid PR: 2835
File: backend/internal/agent/declarative_resource.go:21-39
Timestamp: 2026-05-20T05:39:33.413Z
Learning: Do not treat importing a package named "testing" in production Go files as an anti-pattern in this codebase. For .go files that are not *_test.go, this convention is used to expose *ForTest constructors (e.g., NewAgentExporterForTest, NewApplicationExporterForTest) that are guarded by a call like testing.Testing() and panic when not running under tests. When reviewing, only flag importing "testing" in non-test files if it is not part of this established *ForTest/guard+panic convention.

Comment on lines +1593 to +1641
"onSuccess": "{{SEND_EMAIL_ID}}"
}
}
},
{
"id": "{{SEND_EMAIL_ID}}",
"type": "TASK_EXECUTION",
"size": {
"width": 200,
"height": 100
},
"position": {
"x": 650,
"y": 266
},
"data": {
"action": {
"type": "EXECUTOR",
"executor": {
"name": "EmailExecutor",
"mode": "send"
},
"onSuccess": "{{VERIFY_LINK_ID}}"
},
"properties": {
"emailTemplate": "MAGIC_LINK"
}
}
},
{
"id": "{{VERIFY_LINK_ID}}",
"type": "TASK_EXECUTION",
"size": {
"width": 200,
"height": 100
},
"position": {
"x": 950,
"y": 266
},
"data": {
"action": {
"type": "EXECUTOR",
"executor": {
"name": "MagicLinkExecutor",
"mode": "verify"
},
"onSuccess": "{{AUTH_ASSERT_ID}}"
}
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't advance straight from email send to verification.

After the email is sent, this template immediately runs MagicLinkExecutor in verify mode, but there is no intermediate email_sent prompt/view to wait for the callback token. That makes the standalone template inconsistent with the widget flow and likely leaves verification without the required input.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/apps/console/src/features/login-flow/data/templates.json` around
lines 1593 - 1641, The template advances directly from the SEND_EMAIL_ID task
(EmailExecutor send) to VERIFY_LINK_ID (MagicLinkExecutor verify) which skips
the intermediate "email_sent" view that must wait for the user/callback token;
add an intermediate node (e.g., EMAIL_SENT_VIEW_ID) representing the email_sent
prompt/view and change the SEND_EMAIL_ID action.onSuccess to point to
EMAIL_SENT_VIEW_ID, then have that view transition (on user action or callback)
to VERIFY_LINK_ID so MagicLinkExecutor.verify only runs after the token/input is
provided; ensure any references like EmailExecutor, MagicLinkExecutor,
SEND_EMAIL_ID, VERIFY_LINK_ID, and AUTH_ASSERT_ID are updated to use the new
EMAIL_SENT_VIEW_ID in the flow.

Comment on lines +1665 to +1675
{
"id": "END",
"type": "END",
"size": {
"width": 85,
"height": 34
},
"position": {
"x": 1600,
"y": 300
}
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add the completion executor to the END node.

Every other starter template in this file wires END through LoginCompletionExecutor; this one ends with a bare node. That can skip the normal post-auth completion/redirect path after AuthAssertExecutor succeeds.

Suggested fix
           {
             "id": "END",
             "type": "END",
             "size": {
               "width": 85,
               "height": 34
             },
             "position": {
               "x": 1600,
               "y": 300
-            }
+            },
+            "config": {},
+            "data": {
+              "action": {
+                "type": "EXECUTOR",
+                "executor": {
+                  "name": "LoginCompletionExecutor"
+                }
+              }
+            }
           }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"id": "END",
"type": "END",
"size": {
"width": 85,
"height": 34
},
"position": {
"x": 1600,
"y": 300
}
{
"id": "END",
"type": "END",
"size": {
"width": 85,
"height": 34
},
"position": {
"x": 1600,
"y": 300
},
"config": {},
"data": {
"action": {
"type": "EXECUTOR",
"executor": {
"name": "LoginCompletionExecutor"
}
}
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/apps/console/src/features/login-flow/data/templates.json` around
lines 1665 - 1675, The END node with id "END" currently lacks the completion
executor, so update its node object to include the same executor wiring used
elsewhere: add an executor entry referencing "LoginCompletionExecutor" (the
post-auth completion/redirect handler) so that when the flow reaches the END
node—especially after "AuthAssertExecutor" succeeds—it triggers the normal
completion/redirect path; locate the node with "id": "END" in templates.json and
add the executor configuration consistent with other templates that wire END
through LoginCompletionExecutor.

Comment on lines +1231 to +1259
"components": [
{
"alt": "{{ t(signin:images.app_logo.alt) }}",
"category": "DISPLAY",
"height": "60",
"id": "image_logo_sent",
"resourceType": "ELEMENT",
"src": "{{ meta(application.logoUrl) }}",
"type": "IMAGE",
"width": ""
},
{
"align": "center",
"category": "DISPLAY",
"id": "display_email_sent_heading",
"label": "{{ t(signin:forms.magicLink.emailSent.title) }}",
"resourceType": "ELEMENT",
"type": "TEXT",
"variant": "HEADING_1"
},
{
"align": "center",
"category": "DISPLAY",
"id": "display_email_sent_desc",
"label": "{{ t(signin:forms.magicLink.emailSent.description) }}",
"resourceType": "ELEMENT",
"type": "TEXT",
"variant": "BODY_1"
}
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use generated IDs for the email-sent view elements.

image_logo_sent, display_email_sent_heading, and display_email_sent_desc are fixed IDs inside a reusable widget. If this widget is added more than once, those IDs will collide in the flow graph. Use {{ID}} placeholders here like the rest of the widget.

Suggested fix
-                  "id": "image_logo_sent",
+                  "id": "{{ID}}",
@@
-                  "id": "display_email_sent_heading",
+                  "id": "{{ID}}",
@@
-                  "id": "display_email_sent_desc",
+                  "id": "{{ID}}",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/apps/console/src/features/login-flow/data/widgets.json` around lines
1231 - 1259, The three component IDs in the reusable email-sent widget are
hard-coded and will collide when the widget is instantiated multiple times;
replace the fixed ids "image_logo_sent", "display_email_sent_heading", and
"display_email_sent_desc" with generated ID placeholders (e.g. use the existing
{{ID}} pattern used elsewhere in the widget like "{{ID}}_image_logo",
"{{ID}}_heading", "{{ID}}_description") so each instance gets unique ids and
update any internal references to those ids accordingly.

Comment on lines +25 to +26
export {default as MagicLinkRoute} from './components/MagicLinkRoute';
export * from './components/MagicLinkRoute';
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 27, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🔴 Documentation Required

This PR introduces new SDK components that are not covered by documentation updates under docs/.
Please update the relevant SDK documentation before merging.

Missing documentation:

  • MagicLinkRoute component (React Router SDK): Document the component's purpose, props, usage examples, and integration steps in docs/content/sdks/ or create a Magic Link authentication guide under docs/content/guides/.
  • MagicLinkCallback component (React SDK): Document the component's purpose, props, usage examples, and how it handles the Magic Link verification flow in docs/content/sdks/.
  • Magic Link authentication flow: Provide end-to-end developer guidance covering both backend configuration and frontend integration across the React and React Router SDKs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/react-router/src/index.ts` around lines 25 - 26, Add documentation for
the new Magic Link SDK components: create docs for MagicLinkRoute and
MagicLinkCallback under docs/content/sdks/ (or a new guide in
docs/content/guides/) that describe each component's purpose, public props (with
types and defaults), usage examples showing how to import and render
MagicLinkRoute and MagicLinkCallback in a React Router app, and integration
steps (routing, expected URL/callback flow). Also include an end-to-end Magic
Link authentication section covering necessary backend configuration (token/URL
generation, callback endpoint), how the frontend triggers and verifies the link,
error handling and redirects, and cross-references between the React and React
Router SDK examples.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai
Documentation would be taken in a separate PR

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.

@RandithaK, understood! Deferring the documentation to a separate PR is a reasonable approach.

Would you like me to open a GitHub issue to track the documentation work for the Magic Link SDK components (MagicLinkRoute and MagicLinkCallback), so it doesn't get lost?

Comment on lines +111 to +123
const executionId: string | null = urlParams.get('id');
const token: string | null = urlParams.get('token');
const applicationId: string | null = urlParams.get('applicationId');

clearTokenFromUrl();

if (!executionId || !token) {
const error = new Error('Missing executionId or token in Magic Link URL');
// eslint-disable-next-line no-console
console.error('Magic Link callback error:', error);
redirectWithError(error);
return;
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 27, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Use the correct query parameter for execution ID.

Line 111 reads id, but the callback URL and follow-up navigation in this PR use executionId. This causes valid magic-link callbacks to fail with a missing-parameter error.

💡 Proposed fix
-        const executionId: string | null = urlParams.get('id');
+        const executionId: string | null = urlParams.get('executionId') || urlParams.get('id');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const executionId: string | null = urlParams.get('id');
const token: string | null = urlParams.get('token');
const applicationId: string | null = urlParams.get('applicationId');
clearTokenFromUrl();
if (!executionId || !token) {
const error = new Error('Missing executionId or token in Magic Link URL');
// eslint-disable-next-line no-console
console.error('Magic Link callback error:', error);
redirectWithError(error);
return;
}
const executionId: string | null = urlParams.get('executionId') || urlParams.get('id');
const token: string | null = urlParams.get('token');
const applicationId: string | null = urlParams.get('applicationId');
clearTokenFromUrl();
if (!executionId || !token) {
const error = new Error('Missing executionId or token in Magic Link URL');
// eslint-disable-next-line no-console
console.error('Magic Link callback error:', error);
redirectWithError(error);
return;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/react/src/components/auth/Callback/MagicLinkCallback.tsx` around lines
111 - 123, The code reads the wrong query param key: change the urlParams.get
call that assigns executionId (currently using 'id') to use 'executionId' so the
Magic Link callback recognizes the real parameter; update the line that sets
executionId (where executionId: string | null = urlParams.get('id')) to
urlParams.get('executionId') and keep the existing clearTokenFromUrl(), error
handling, and redirectWithError usage unchanged so valid callbacks no longer
trigger the missing-parameter branch.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai
const executionId: string | null = urlParams.get('id');
Line 111.
I think it's a false positive

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check what query params the backend uses when building the magic link URL
rg -n "queryParams\|executionId\|applicationId\|\"id\"\|'id'" backend/internal/authn/magiclink/service.go -A3 -B3

echo "---"
# Check the magic link executor for queryParams construction
rg -n "queryParams\|executionId\|applicationId\|\"id\"\|GenerateMagicLink" backend/internal/flow/executor/magic_link_executor.go -A5 -B2

Length of output: 658


🏁 Script executed:

#!/bin/bash
# Find the magic link service file
fd "service.go" backend/internal/authn/magiclink/
echo "---"
# Find the magic link executor file
fd "magic_link_executor.go" backend/

echo "=== magic link service ==="
cat backend/internal/authn/magiclink/service.go | grep -n "id\|token\|query\|param\|URL\|url" | head -50

echo "=== magic link executor ==="
cat backend/internal/flow/executor/magic_link_executor.go | grep -n "queryParam\|\"id\"\|executionId\|applicationId\|GenerateMagicLink" | head -50

Length of output: 4322



@RandithaK, you're absolutely right — this is a false positive on my part. The backend magic link executor explicitly sets "id" (not "executionId") in the query parameters:

// backend/internal/flow/executor/magic_link_executor.go
queryParams := map[string]string{
    "id":            ctx.ExecutionID,
    "applicationId": ctx.Application.ID,
}

So urlParams.get('id') on line 111 correctly reads the execution ID from the magic link callback URL. Apologies for the noise!

(∩`-´)⊃━☆゚.*・。゚


✏️ Learnings added
Learnt from: RandithaK
Repo: thunder-id/thunderid PR: 2987
File: sdks/react/src/components/auth/Callback/MagicLinkCallback.tsx:111-123
Timestamp: 2026-05-27T09:18:12.313Z
Learning: In `sdks/react/src/components/auth/Callback/MagicLinkCallback.tsx`, the magic link callback URL uses `id` (not `executionId`) as the query parameter for the execution ID. This is because the backend (`backend/internal/flow/executor/magic_link_executor.go`) explicitly builds the queryParams map with `"id": ctx.ExecutionID`. So `urlParams.get('id')` is the correct way to retrieve the execution ID in the frontend callback.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +280 to 281
const authIdFromStorage: string = localStorage.getItem('thunderid_auth_id') ?? '';
const authId: string = authIdFromUrl || authIdFromStorage;
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope thunderid_auth_id to instance/client to prevent stale and cross-flow collisions.

Using a global localStorage key for auth flow state can mix concurrent flows across tabs/instances and reuse stale authId after browser restarts. Please namespace this key (e.g., include instanceId and client/application identity) and read/write/remove through a shared helper.

Suggested direction
-const authIdFromStorage: string = localStorage.getItem('thunderid_auth_id') ?? '';
+const authKey = `thunderid_auth_id_${this.getInstanceId()}`;
+const authIdFromStorage: string = localStorage.getItem(authKey) ?? '';

-if (authIdFromUrl && !authIdFromStorage) {
-  localStorage.setItem('thunderid_auth_id', authIdFromUrl);
+if (authIdFromUrl && !authIdFromStorage) {
+  localStorage.setItem(authKey, authIdFromUrl);
}

Also applies to: 344-349

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/react/src/ThunderIDReactClient.ts` around lines 280 - 281, The code
reads/writes a global localStorage key 'thunderid_auth_id' (see
authIdFromStorage and authId usage) which can collide across tabs/instances;
replace this with a namespaced storage helper that composes the key from
client/application id and a per-instance instanceId (e.g.,
`${appId}:${instanceId}:thunderid_auth_id`) and use that helper for all
get/set/remove operations; update the reads (authIdFromStorage), writes, and
cleanup logic (also at the other usage around lines 344-349) to call the shared
helper functions (e.g., getNamespacedAuthId, setNamespacedAuthId,
removeNamespacedAuthId) so each client instance uses its own scoped key and
avoids stale/cross-flow collisions.

Comment on lines +310 to 311
const authIdFromStorage: string | null = localStorage.getItem('thunderid_auth_id');
const authId: string = authIdFromUrl || authIdFromStorage || '';
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use a scoped auth-id storage key instead of global thunderid_auth_id.

The new localStorage fallback persists across tabs/sessions, so a global key can bind a flow to the wrong tab/instance and revive stale authId. Scope the key (instance/client/application) and centralize access in one helper.

Also applies to: 375-380

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/vue/src/ThunderIDVueClient.ts` around lines 310 - 311, Replace the
hardcoded global localStorage key 'thunderid_auth_id' with a scoped key and
centralized helper: create a helper (e.g.,
getScopedAuthKey(instanceIdOrClientId)) and accessor functions
(getAuthIdFromStorage(), setAuthIdToStorage(id), clearAuthIdFromStorage()) and
use those in ThunderIDVueClient where authIdFromStorage, authIdFromUrl and
authId are computed (and in the other occurrences around the file). The scoped
key should include a unique identifier for the client/instance/application
(e.g., `${baseKey}:${clientId}`) so stored auth IDs don’t bleed across tabs, and
replace direct localStorage.getItem/setItem/removeItem calls with the new helper
functions everywhere in this file.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants