@supertokens-plugins/rownd-nodejs
v0.6.1
Published
Rownd User Migration Plugin for SuperTokens
Readme
SuperTokens Rownd User Migration Plugin
This plugin facilitates the migration of users and sessions from Rownd to SuperTokens.
Installation
npm install @supertokens-plugins/rownd-nodejsQuick Start
Backend Configuration
Initialize the plugin in your SuperTokens backend configuration.
[!IMPORTANT] This plugin always requires the
SessionandUserMetadatarecipes. EnablePasswordlessfor email/phone,ThirdPartyfor Google/Apple/guest/anonymous users,EmailVerificationfor verified email profile updates, andAccountLinkingwhen migrated Rownd users may have multiple supported login methods.
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import UserMetadata from "supertokens-node/recipe/usermetadata";
import RowndMigrationPlugin from "@supertokens-plugins/rownd-nodejs";
SuperTokens.init({
appInfo: {
// your app info
},
recipeList: [
Session.init(),
UserMetadata.init(),
// your other recipes
],
experimental: {
plugins: [
RowndMigrationPlugin.init({
rowndAppKey: process.env.ROWND_APP_KEY,
rowndAppSecret: process.env.ROWND_APP_SECRET,
enableDebugLogs: process.env.ENABLE_DEBUG_LOGS === "true",
}),
],
},
});Disabling Rownd User Migration
After migration is complete, disable Rownd user and session migration to run the compatibility endpoints without Rownd credentials:
RowndMigrationPlugin.init({
disableRowndUserMigration: true,
});This prevents the Rownd API client and the /plugin/rownd/migrate and
/plugin/migrate-session routes from being initialized. Other compatibility
endpoints remain enabled. Passwordless and email-verification links continue to
use the Rownd Hub with an internal dummy app key when rowndAppKey is omitted.
The plugin logs a warning during initialization while migration is disabled.
Without disableRowndUserMigration: true, both rowndAppKey and
rowndAppSecret are required.
Session Claim Fields
Schema fields can be copied into the SuperTokens access-token payload by setting include_in_session_claims: true. Use session_claim_name when the claim name should differ from the Rownd data field name.
RowndMigrationPlugin.init({
rowndAppKey: process.env.ROWND_APP_KEY,
rowndAppSecret: process.env.ROWND_APP_SECRET,
schema: {
employee_id: {
display_name: "Employee ID",
type: "string",
user_visible: false,
include_in_session_claims: true,
session_claim_name: "employee_id_claim",
},
},
});Client Link Domains
Set clientDomains to rewrite account links to different frontend URL bases. Values must be absolute URL bases, including custom schemes for native deep links. The plugin selects mobile for mobile_app display context and browser otherwise. Consumers can pass rownd_client_domain to select any custom key.
RowndMigrationPlugin.init({
rowndAppKey: process.env.ROWND_APP_KEY,
rowndAppSecret: process.env.ROWND_APP_SECRET,
clientDomains: {
browser: "https://app.example.com",
mobile: "customDomain://",
browser_local: "http://localhost:3000",
},
});Passwordless Confirmation Bypass
Use createMagicLinkWithConfirmationBypass when your backend needs to create a passwordless magic link that can be opened on a different device without showing the SuperTokens cross-device confirmation prompt.
This is intended for trusted server-side flows only.
First, configure the exact post-login paths that may use the bypass:
const rowndPluginConfig = {
rowndAppKey: process.env.ROWND_APP_KEY,
rowndAppSecret: process.env.ROWND_APP_SECRET,
clientDomains: {
browser: "https://app.example.com",
},
crossDeviceConfirmationBypass: {
allowedRedirectPaths: ["/profile", "/settings/security"],
},
};
const superTokensConfig = {
// your app info and recipe list
experimental: {
plugins: [RowndMigrationPlugin.init(rowndPluginConfig)],
},
};
SuperTokens.init(superTokensConfig);Then call the helper from your backend:
import { createMagicLinkWithConfirmationBypass } from "@supertokens-plugins/rownd-nodejs";
const magicLink = await createMagicLinkWithConfirmationBypass({
email: "[email protected]",
tenantId: "tenant-a",
clientDomain: "browser",
redirectToPath: "/profile",
displayContext: "browser",
});redirectToPath is required and must match crossDeviceConfirmationBypass.allowedRedirectPaths exactly after normalization. Absolute URLs are accepted only when their origin matches the resolved clientDomain; they are normalized back to a relative path before being added to the magic link.
clientDomain must be a configured clientDomains key, not a raw domain. Omit it to use the SuperTokens website domain.
Pass exactly one of email or phoneNumber. tenantId defaults to public. The helper returns the rewritten magic link with bypassDeviceConfirmation=true.
Before skipping the cross-device confirmation prompt, the frontend should validate the callback against the plugin. Routes are mounted under your SuperTokens apiBasePath, which defaults to /auth.
- POST
{apiBasePath}/plugin/passwordless-cross-device-confirmation/validate - Default:
POST /auth/plugin/passwordless-cross-device-confirmation/validate - Body:
{ "clientDomain": "browser", "redirectToPath": "/profile", "appVariantId": "optional_variant" } - Success response:
{ "status": "OK", "bypass": true }
If validation fails, the frontend should show the normal cross-device confirmation prompt.
API Endpoints
Routes are mounted under your SuperTokens apiBasePath, which defaults to /auth. The migration endpoint is the main Rownd-to-SuperTokens session handoff endpoint; the plugin also exposes Rownd-compatible app config, guest, user, metadata, field, and sign-out endpoints under {apiBasePath}/plugin/rownd/....
Unauthenticated migration and guest routes accept an optional tenantId query parameter. It defaults to public. SuperTokens Core validates the tenant when the operation runs. Authenticated identity-field and sign-out operations use the tenant from the current session; custom Rownd metadata remains global to the SuperTokens user.
[!IMPORTANT] Rownd users with multiple supported login methods are rejected unless SuperTokens account linking is enabled in the target environment.
Migrate
- POST
{apiBasePath}/plugin/rownd/migrate - Default:
POST /auth/plugin/rownd/migrate - Non-public tenant:
POST /auth/plugin/rownd/migrate?tenantId=tenant-a - Headers:
Authorization: Bearer <Rownd_JWT>. Header-token clients should also sendrid: session,fdi-version: 1.18, andst-auth-mode: header. - Description: Validates the Rownd JWT, imports new users with their Rownd profile data, ensures the selected login method is associated with the requested SuperTokens tenant, and then creates a new SuperTokens session in that tenant. Header-token clients must receive
st-access-token,st-refresh-token, andfront-tokenresponse headers.
Guest
- POST
{apiBasePath}/plugin/rownd/guest - Default:
POST /auth/plugin/rownd/guest - Non-public tenant:
POST /auth/plugin/rownd/guest?tenantId=tenant-a - Description: Creates a guest or instant user and session in the requested tenant.
Debug Logging
Set enableDebugLogs: true in the plugin config to enable debug logging.
Telemetry
Telemetry is optional. If telemetry is omitted from the plugin config, no telemetry is emitted.
The plugin emits exactly one telemetry event per /migrate call result.
Event shape
Each event includes endpoint outcome data only (not step-by-step events), including:
outcome:successorerrordurationMstenantId(when available)rowndUserId(when available)superTokensUserId(when available)- for errors:
error.messageanderror.name
[!NOTE] Telemetry failures never fail migration endpoints. Errors in telemetry reporting are swallowed.
Provider: OpenTelemetry
RowndMigrationPlugin.init({
rowndAppKey: process.env.ROWND_APP_KEY,
rowndAppSecret: process.env.ROWND_APP_SECRET,
telemetry: {
provider: "opentelemetry",
},
});[!IMPORTANT] This plugin uses
@opentelemetry/apionly. You still need to initialize OpenTelemetry SDK/exporters in your app for spans to be exported.
Provider: Axiom
RowndMigrationPlugin.init({
rowndAppKey: process.env.ROWND_APP_KEY,
rowndAppSecret: process.env.ROWND_APP_SECRET,
telemetry: {
provider: "axiom",
token: process.env.AXIOM_TOKEN!,
dataset: process.env.AXIOM_DATASET!,
// optional, defaults to https://api.axiom.co/v1/datasets
// url: "https://api.axiom.co/v1/datasets",
},
});Provider: Custom
RowndMigrationPlugin.init({
rowndAppKey: process.env.ROWND_APP_KEY,
rowndAppSecret: process.env.ROWND_APP_SECRET,
telemetry: {
provider: "custom",
factory: () => ({
recordEvent: async (event) => {
// send to your telemetry backend
},
}),
},
});Bulk Import Script
The package includes a bulk migration script for importing Rownd users into SuperTokens.
Set supertokens.tenantId in the generated configuration to associate every imported login method with a non-public tenant. It defaults to public when omitted. Resuming from a checkpoint with a different tenant is rejected.
The script runs from a YAML config file generated from the included template.
Usage
- Generate a local config file.
- Edit the config with your Rownd and SuperTokens credentials.
- Run the migration.
npx rownd-nodejs init-config --output ./rownd-bulk-migrate.yaml
npx rownd-nodejs bulk-migrate --config ./rownd-bulk-migrate.yamlFor repo-local development, use npm run cli -- bulk-migrate --config ./rownd-bulk-migrate.yaml from packages/rownd-nodejs.
The script:
- fetches users from Rownd page by page
- validates the Rownd payload shape with
zod - maps users with
mapRowndUserToSuperTokens - imports them into SuperTokens in bounded batches
- writes a checkpoint file so the run can resume later
Config File
All runtime config is read from the YAML file passed with --config.
There is no environment variable parsing.
