Skip to content

SuperTokens lazy user migration#41

Merged
mhamann merged 3 commits into
rownd:mainfrom
bcbogdan:feat/supertokens-migrate
Apr 22, 2026
Merged

SuperTokens lazy user migration#41
mhamann merged 3 commits into
rownd:mainfrom
bcbogdan:feat/supertokens-migrate

Conversation

@bcbogdan
Copy link
Copy Markdown
Contributor

@bcbogdan bcbogdan commented Apr 21, 2026

Summary

This change introduces the SuperTokens lazy migration flow to new sign ups .
The migration endpoint only runs when a user has just created a new Rownd account. Existing users who simply sign in should not trigger the migration flow again.

How it works

  1. The React provider listens for the Hub sign_in_completed event.
  2. When that event includes user_type: "new_user", the provider sets an internal flag indicating that this auth flow was a sign up.
  3. A follow-up effect waits for a valid Rownd access token and SuperTokens app config.
  4. Once both are available, the SDK calls the SuperTokens migration endpoint exactly once for that new user.

Summary by Sourcery

Add SuperTokens user migration support triggered on new Rownd sign-ups and provide an example app demonstrating the integration.

New Features:

  • Trigger a SuperTokens migration endpoint once after a successful Rownd sign-up when SuperTokens app info is configured.
  • Expose a new SuperTokens configuration option on the React Rownd provider for enabling migration behavior.
  • Add a full SuperTokens migration example app with backend and frontend to illustrate the integration flow.

Enhancements:

  • Wire the React provider to listen for sign-in completion events and gate migration calls to new-user sign-ups only.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 21, 2026

Reviewer's Guide

Implements a lazy SuperTokens migration flow that only runs after new Rownd sign-ups, exposes SuperTokens config on the React provider, and adds a full backend/frontend example app demonstrating the migration integration.

Sequence diagram for lazy SuperTokens migration on new Rownd sign-up

sequenceDiagram
    actor User
    participant FrontendApp
    participant ReactRowndProvider
    participant HubEvents
    participant SuperTokensBackend

    User->>FrontendApp: Clicks requestSignIn
    FrontendApp->>ReactRowndProvider: requestSignIn()
    ReactRowndProvider->>HubEvents: Start Rownd auth flow

    HubEvents-->>ReactRowndProvider: sign_in_completed (user_type new_user)
    ReactRowndProvider->>ReactRowndProvider: shouldSyncToSuperTokens.current = true

    Note over ReactRowndProvider: Later, when hubState.access_token and props.supertokens.appInfo are set

    ReactRowndProvider->>ReactRowndProvider: useEffect(access_token, appInfo)
    ReactRowndProvider->>ReactRowndProvider: Check accessToken && appInfo && shouldSyncToSuperTokens.current
    ReactRowndProvider->>ReactRowndProvider: shouldSyncToSuperTokens.current = false
    ReactRowndProvider->>SuperTokensBackend: POST /auth/plugin/rownd/migrate\nAuthorization: Bearer accessToken
    SuperTokensBackend-->>ReactRowndProvider: 2xx or error

    ReactRowndProvider-->>FrontendApp: Rownd context updated
    FrontendApp-->>User: Shows authenticated UI
Loading

Class diagram for updated ReactRowndProvider and SuperTokens config

classDiagram
    class RowndProviderProps {
      string apiVersion
      SuperTokensConfig supertokens
    }

    class SuperTokensConfig {
      AppInfo appInfo
    }

    class AppInfo {
      string appName
      string apiDomain
      string apiBasePath
    }

    class ReactRowndProvider {
      RowndProviderProps props
      any hubState
      boolean shouldSyncToSuperTokens_current
      stateListener(state, api)
      useEffectAuthState()
      useEffectSubscribeSignInCompleted()
      useEffectSyncUserToSuperTokens()
    }

    class SuperTokensSync {
      +syncUserToSuperTokens(accessToken, appName, apiDomain, apiBasePath) Promise~void~
    }

    RowndProviderProps --> SuperTokensConfig : optional supertokens
    SuperTokensConfig --> AppInfo : has appInfo
    ReactRowndProvider --> RowndProviderProps : uses props
    ReactRowndProvider ..> SuperTokensSync : calls
Loading

Flow diagram for syncUserToSuperTokens migration helper

flowchart TD
    A[Start syncUserToSuperTokens] --> B[Build base URL from apiDomain and apiBasePath]
    B --> C[Set Authorization header with Bearer accessToken]
    C --> D[fetch POST base/plugin/rownd/migrate\nwith credentials include]
    D --> E{res.ok?}
    E -- Yes --> F[Log migrate success]
    E -- No --> G[console.warn migrate failed with status]
    F --> H[End]
    G --> H[End]
    D --> I[Network or runtime error]
    I --> J[console.warn migrate failed non-fatal]
    J --> H
Loading

File-Level Changes

Change Details Files
Wire SuperTokens lazy migration into ReactRowndProvider so it triggers once after new-user sign-up when access token and app config are available.
  • Add syncUserToSuperTokens utility and import it into the React provider
  • Track whether the current auth flow is a new-user sign-up via sign_in_completed hub event listener and a ref flag
  • Add an effect that waits for both a Rownd access token and SuperTokens appInfo before invoking the migration endpoint, ensuring it only runs once per sign-up
  • Minor refactor of stateListener definition and InternalRowndProvider JSX formatting
src/context/ReactRowndProvider.tsx
src/utils/supertokens-sync.ts
Extend Rownd provider props to accept SuperTokens app configuration needed for migration calls.
  • Add optional supertokens.appInfo prop definition to RowndProviderProps with appName, apiDomain, and apiBasePath fields
  • Document that providing SuperTokens appInfo enables automatic migration after sign-up
src/context/RowndContext.tsx
Add a complete SuperTokens migration example application (backend + frontend) to demonstrate and test the new migration flow.
  • Create an Express-based SuperTokens backend configured with Passwordless, Session, UserMetadata, AccountLinking, and the Rownd plugin, including CORS and logging
  • Add a minimal React frontend using supertokens-rownd-react to sign in, display user and access token, and sign out
  • Configure TypeScript, Vite, and package.json for both backend and frontend example projects, plus auxiliary config and placeholder files
examples/supertokens-migration/backend/src/index.ts
examples/supertokens-migration/backend/package.json
examples/supertokens-migration/backend/tsconfig.json
examples/supertokens-migration/backend/.env.example
examples/supertokens-migration/backend/.npmrc
examples/supertokens-migration/frontend/src/App.tsx
examples/supertokens-migration/frontend/src/main.tsx
examples/supertokens-migration/frontend/index.html
examples/supertokens-migration/frontend/package.json
examples/supertokens-migration/frontend/package-lock.json
examples/supertokens-migration/frontend/tsconfig.json
examples/supertokens-migration/frontend/tsconfig.node.json
examples/supertokens-migration/frontend/vite.config.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@bcbogdan bcbogdan changed the title Feat/supertokens migrate SuperTokens lazy user migration Apr 21, 2026
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In syncUserToSuperTokens, the success log runs even when res.ok is false; consider moving the success log into the if (res.ok) branch (or returning early on non-OK) so logs accurately reflect the outcome.
  • The call to syncUserToSuperTokens(accessToken, appInfo).catch(() => {}); in ReactRowndProvider silently swallows errors even though syncUserToSuperTokens already handles its own errors with a try/catch, so you can drop the outer .catch or surface errors in a more visible way if needed.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `syncUserToSuperTokens`, the success log runs even when `res.ok` is false; consider moving the success log into the `if (res.ok)` branch (or returning early on non-OK) so logs accurately reflect the outcome.
- The call to `syncUserToSuperTokens(accessToken, appInfo).catch(() => {});` in `ReactRowndProvider` silently swallows errors even though `syncUserToSuperTokens` already handles its own errors with a try/catch, so you can drop the outer `.catch` or surface errors in a more visible way if needed.

## Individual Comments

### Comment 1
<location path="src/utils/supertokens-sync.ts" line_range="14-17" />
<code_context>
+      headers,
+      credentials: 'include',
+    });
+    if (!res.ok) {
+      console.warn(`[Rownd->ST] migrate failed with status: ${res.status}`);
+    }
+    console.log('[Rownd->ST] migrate success');
+  } catch (e) {
+    console.warn('[Rownd->ST] migrate failed (non-fatal):', e);
</code_context>
<issue_to_address>
**suggestion:** Success logging is emitted even when the response is not OK, which can be misleading.

Because `migrate success` is logged regardless of `res.ok`, failures appear as successes in the logs. Please either return early when `!res.ok` or move the success log into an `if (res.ok)` block so logs accurately reflect the outcome.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/utils/supertokens-sync.ts Outdated
@bcbogdan bcbogdan force-pushed the feat/supertokens-migrate branch from 4e09735 to fcbf3ae Compare April 21, 2026 18:39
Comment thread src/context/ReactRowndProvider.tsx Outdated
};
}, [hubState.events]);

useEffect(() => {
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.

This has an ordering race. Mutating a ref doesn't trigger a re-render, so this effect only runs when access_token or props.supertokens changes after the flag is set.

If Hub updates access_token first and then emits sign_in_completed (likely as state settles before events fire), the effect runs while the flag is still false, then the event sets the flag and nothing re-triggers the effect → migration silently never runs.

Two fixes:

  1. Call syncUserToSuperTokens directly from the sign_in_completed handler, reading access_token via a ref that mirrors hubState.access_token. Mirrors the iOS/Android impls and removes the multi-hop dance.
  2. Make shouldSyncToSuperTokens a useState instead of a useRef, so flipping it re-runs the effect.

Option 1 is smaller and more obviously correct.

@@ -0,0 +1,30 @@
import { useRownd } from 'supertokens-rownd-react';
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.

This should probably be @rownd/react, right?

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.

Yep. Copied it directly from the internal repo and forgot to change it.

@bcbogdan bcbogdan force-pushed the feat/supertokens-migrate branch from 2ed716d to 4434043 Compare April 22, 2026 18:14
@mhamann mhamann merged commit 0c1b004 into rownd:main Apr 22, 2026
2 checks passed
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.

2 participants