SDK Integration Guide
Configure the SuperTokens Rownd backend plugin and frontend SDKs.
Configure the SuperTokens Rownd backend plugin and frontend SDKs to migrate users, create SuperTokens sessions, and keep using Rownd-style APIs.
Overview
This tutorial configures the backend and client SDKs used during the Rownd migration. By the end, your backend exposes Rownd-compatible plugin routes, your frontend or mobile app uses the SuperTokens Rownd-compatible Hub, and OAuth/OIDC clients can be migrated if your Rownd app uses them.
Before you start
These instructions assume that you have already created an account in the SuperTokens SaaS Dashboard and have deployed a SuperTokens Core service. After you have done that, select the relevant Managed deployment, enable Account Linking from Features, and copy the core connection information from Overview.
Steps
1. Configure the backend SDK
1.1 Install the SuperTokens SDK and Rownd plugin
Install the base SuperTokens backend SDK together with the Rownd migration plugin. The SuperTokens SDK adds the auth middleware, recipe APIs, and session handling. The Rownd plugin adds the Rownd-compatible migration, Hub, profile, and OAuth compatibility routes.
1.1 Install the SuperTokens SDK and Rownd plugin
Install the base SuperTokens Python SDK together with the Rownd migration plugin from PyPI.
npm install supertokens-node @supertokens-plugins/rownd-nodejsyarn add supertokens-node @supertokens-plugins/rownd-nodejspnpm add supertokens-node @supertokens-plugins/rownd-nodejsbun add supertokens-node @supertokens-plugins/rownd-nodejspip install supertokens-python supertokens-rownduv add supertokens-python supertokens-rownd1.2 Initialize SuperTokens
Initialize the recipes that map to your Rownd auth methods, then add the Rownd plugin under experimental.plugins.
The setup has four parts:
supertokens: connects the backend SDK to SuperTokens Core.appInfo: defines the public API and website domains used by SuperTokens and the Rownd Hub.recipeList: enables the SuperTokens recipes used to replace Rownd auth behavior.experimental.plugins: mounts the Rownd migration plugin routes underapiBasePath.
1.2 Initialize SuperTokens
Python plugin configuration must include api_base_path, api_domain, website_domain, and app_name explicitly. Keep these values in sync with InputAppInfo.
The setup has four parts:
supertokens_config: connects the backend SDK to SuperTokens Core.app_info: defines the public API and website domains used by SuperTokens and the Rownd Hub.recipe_list: enables the SuperTokens recipes used to replace Rownd auth behavior.experimental.plugins: mounts the Rownd migration plugin routes underapi_base_path.
import SuperTokens from "supertokens-node";
import AccountLinking from "supertokens-node/recipe/accountlinking";
import EmailVerification from "supertokens-node/recipe/emailverification";
import OAuth2Provider from "supertokens-node/recipe/oauth2provider";
import Passwordless from "supertokens-node/recipe/passwordless";
import Session from "supertokens-node/recipe/session";
import ThirdParty from "supertokens-node/recipe/thirdparty";
import UserMetadata from "supertokens-node/recipe/usermetadata";
import RowndMigrationPlugin from "@supertokens-plugins/rownd-nodejs";
SuperTokens.init({
supertokens: {
connectionURI: process.env.SUPERTOKENS_CONNECTION_URI!,
apiKey: process.env.SUPERTOKENS_API_KEY,
},
appInfo: {
appName: "My App",
apiDomain: "<API_DOMAIN>",
websiteDomain: process.env.WEBSITE_DOMAIN!,
apiBasePath: "<API_BASE_PATH>",
},
recipeList: [
AccountLinking.init({}),
Session.init(),
OAuth2Provider.init(),
UserMetadata.init(),
Passwordless.init({
contactMethod: "EMAIL_OR_PHONE",
flowType: "MAGIC_LINK",
}),
EmailVerification.init({ mode: "OPTIONAL" }),
ThirdParty.init({
signInAndUpFeature: {
providers: [
{
config: {
thirdPartyId: "google",
clients: [
{
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
],
},
},
{
config: {
thirdPartyId: "apple",
clients: [
{
// Browser/Hub Apple login uses the Apple Services ID.
clientType: "web",
clientId: process.env.APPLE_WEB_CLIENT_ID!,
clientSecret: process.env.APPLE_CLIENT_SECRET!,
},
{
// Native iOS Apple login returns authorization codes for the app bundle ID.
clientType: "ios",
clientId: process.env.APPLE_IOS_BUNDLE_ID!,
clientSecret: process.env.APPLE_CLIENT_SECRET!,
},
],
},
},
],
},
}),
],
experimental: {
plugins: [
RowndMigrationPlugin.init({
rowndAppKey: process.env.ROWND_APP_KEY!,
rowndAppSecret: process.env.ROWND_APP_SECRET!,
enableDebugLogs: process.env.ROWND_ENABLE_DEBUG_LOGS === "true",
clientDomains: {
browser: process.env.WEBSITE_DOMAIN!,
browser_local: "http://localhost:3000",
mobile: "https://my-app.rownd-hub.supertokens.com",
},
appConfig: {
id: process.env.ROWND_APP_KEY!,
name: "My App",
signInMethods: [
{ method: "email" },
{ method: "phone" },
{ method: "google", clientId: process.env.GOOGLE_CLIENT_ID },
{
method: "apple",
clientId: process.env.APPLE_WEB_CLIENT_ID,
// These map Rownd platforms to the SuperTokens Apple clients above.
webClientType: "web",
iosClientType: "ios",
},
{ method: "anonymous", type: "guest", displayName: "Continue as guest" },
],
profile: {
accountInformation: {
methods: {
email: { enabled: true },
phone: { enabled: true },
google: { enabled: true },
apple: { enabled: true },
},
},
personalInformation: { enabled: true },
preferences: { enabled: true },
signOutButton: { enabled: true },
deleteAccountButton: { enabled: true },
},
},
}),
],
},
});from supertokens_python import (
InputAppInfo,
SupertokensConfig,
SupertokensExperimentalConfig,
init,
)
from supertokens_python.recipe import (
accountlinking,
emailverification,
oauth2provider,
passwordless,
session,
thirdparty,
usermetadata,
)
from supertokens_python.recipe.thirdparty import ProviderClientConfig, ProviderConfig, ProviderInput
from supertokens_rownd import init as rownd_init
from supertokens_rownd.types import RowndPluginConfig
API_BASE_PATH = "<API_BASE_PATH>"
API_DOMAIN = "<API_DOMAIN>"
WEBSITE_DOMAIN = "https://app.example.com"
init(
app_info=InputAppInfo(
app_name="My App",
api_domain=API_DOMAIN,
website_domain=WEBSITE_DOMAIN,
api_base_path=API_BASE_PATH,
),
framework="fastapi",
mode="asgi",
supertokens_config=SupertokensConfig(
connection_uri="<SUPERTOKENS_CONNECTION_URI>",
api_key="<SUPERTOKENS_API_KEY>",
),
recipe_list=[
accountlinking.init(),
session.init(),
oauth2provider.init(),
usermetadata.init(),
passwordless.init(
contact_config=passwordless.ContactEmailOrPhoneConfig(),
flow_type="MAGIC_LINK",
),
emailverification.init(mode="OPTIONAL"),
thirdparty.init(
sign_in_and_up_feature=thirdparty.SignInAndUpFeature(
providers=[
ProviderInput(
config=ProviderConfig(
third_party_id="google",
clients=[
ProviderClientConfig(
client_id="<GOOGLE_CLIENT_ID>",
client_secret="<GOOGLE_CLIENT_SECRET>",
)
],
)
),
ProviderInput(
config=ProviderConfig(
third_party_id="apple",
clients=[
# Browser/Hub Apple login uses the Apple Services ID.
ProviderClientConfig(
client_type="web",
client_id="<APPLE_WEB_CLIENT_ID>",
client_secret="<APPLE_CLIENT_SECRET>",
),
# Native iOS Apple login returns authorization codes for the app bundle ID.
ProviderClientConfig(
client_type="ios",
client_id="<APPLE_IOS_BUNDLE_ID>",
client_secret="<APPLE_CLIENT_SECRET>",
),
],
)
)
]
)
),
],
experimental=SupertokensExperimentalConfig(
plugins=[
rownd_init(
RowndPluginConfig(
rownd_app_key="<ROWND_APP_KEY>",
rownd_app_secret="<ROWND_APP_SECRET>",
api_base_path=API_BASE_PATH,
api_domain=API_DOMAIN,
website_domain=WEBSITE_DOMAIN,
app_name="My App",
client_domains={
"browser": WEBSITE_DOMAIN,
"browser_local": "http://localhost:3000",
"mobile": "https://my-app.rownd-hub.supertokens.com",
},
app_config={
"id": "<ROWND_APP_KEY>",
"name": "My App",
"signInMethods": [
{"method": "email"},
{"method": "phone"},
{"method": "google", "clientId": "<GOOGLE_CLIENT_ID>"},
{
"method": "apple",
"clientId": "<APPLE_WEB_CLIENT_ID>",
# These map Rownd platforms to the SuperTokens Apple clients above.
"webClientType": "web",
"iosClientType": "ios",
},
{"method": "anonymous", "type": "guest", "displayName": "Continue as guest"},
],
},
)
)
]
),
)1.3 Add CORS and middleware
Install SuperTokens middleware after CORS handling.
1.3 Add CORS and middleware
For FastAPI, use the get_middleware() and get_all_cors_headers() functions as shown below.
import express from "express";
import cors from "cors";
import supertokens from "supertokens-node";
import { middleware } from "supertokens-node/framework/express";
const app = express();
app.use(
cors({
origin: process.env.WEBSITE_DOMAIN,
allowedHeaders: ["content-type", ...supertokens.getAllCORSHeaders()],
credentials: true,
}),
);
// IMPORTANT: CORS should be before this line.
app.use(middleware());
// ...your API routesfrom fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
from supertokens_python import get_all_cors_headers
from supertokens_python.framework.fastapi import get_middleware
app = FastAPI()
app.add_middleware(get_middleware())
# TODO: Add APIs
app.add_middleware(
CORSMiddleware,
allow_origins=[
"https://app.example.com"
],
allow_credentials=True,
allow_methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
allow_headers=["Content-Type"] + get_all_cors_headers(),
)
# TODO: start server1.4 Configure client domains
By default, magic links are constructed using the websiteDomain that you pass in the SDK configuration.
To test locally or to route the user to a mobile deep linked domain you can use the clientDomains plugin option.
1.4 Configure client domains
By default, magic links are constructed using the website_domain that you pass in the SDK configuration.
To test locally or to route the user to a mobile deep linked domain you can use the client_domains plugin option.
RowndMigrationPlugin.init({
rowndAppKey: process.env.ROWND_APP_KEY!,
rowndAppSecret: process.env.ROWND_APP_SECRET!,
clientDomains: {
browser: "https://app.example.com",
browser_local: "http://localhost:3000",
mobile: "https://my-app.rownd-hub.supertokens.com",
},
});RowndPluginConfig(
rownd_app_key="<ROWND_APP_KEY>",
rownd_app_secret="<ROWND_APP_SECRET>",
client_domains={
"browser": "https://app.example.com",
"browser_local": "http://localhost:3000",
"mobile": "https://my-app.rownd-hub.supertokens.com",
},
)The client sends a clientDomain key, not a URL. The plugin looks up the key in clientDomains and rewrites links to that base URL.
If no explicit key is sent:
- Mobile Hub flows use
clientDomains.mobile. - Browser Hub flows use
clientDomains.browser. - If the selected key is missing, the plugin keeps the link on the Hub URL and only rewrites the path.
The client sends a clientDomain key, not a URL. The plugin looks up the key in client_domains and rewrites links to that base URL.
If no explicit key is sent:
- Mobile Hub flows use
client_domains["mobile"]. - Browser Hub flows use
client_domains["browser"]. - If the selected key is missing, the plugin keeps the link on the Hub URL and only rewrites the path.
1.5 Configure Apple login for iOS (optional)
If your iOS app uses native Sign in with Apple, configure Apple as multiple SuperTokens clients. Browser and Hub Apple login use the Apple Services ID, but native iOS Apple login returns authorization codes for your app bundle ID. The iOS bundle ID must therefore be configured as a separate Apple client in the SuperTokens backend SDK.
The Rownd plugin maps the Apple sign-in method to those SuperTokens client types. The plugin setting is iosClientType, which becomes ios_client_type in the Rownd app config. The iOS SDK reads that value and sends it as clientType when it exchanges the Apple authorization code with /signinup.
ThirdParty.init({
signInAndUpFeature: {
providers: [
{
config: {
thirdPartyId: "apple",
clients: [
{
// Browser/Hub Apple login uses the Apple Services ID.
clientType: "web",
clientId: process.env.APPLE_WEB_CLIENT_ID!,
clientSecret: process.env.APPLE_CLIENT_SECRET!,
},
{
// Native iOS Apple login returns authorization codes for the app bundle ID.
clientType: "ios",
clientId: process.env.APPLE_IOS_BUNDLE_ID!,
clientSecret: process.env.APPLE_CLIENT_SECRET!,
},
],
},
},
],
},
});
RowndMigrationPlugin.init({
rowndAppKey: process.env.ROWND_APP_KEY!,
rowndAppSecret: process.env.ROWND_APP_SECRET!,
appConfig: {
signInMethods: [
{
method: "apple",
clientId: process.env.APPLE_WEB_CLIENT_ID,
// These map Rownd platforms to the SuperTokens Apple clients above.
webClientType: "web",
iosClientType: "ios",
},
],
},
});thirdparty.init(
sign_in_and_up_feature=thirdparty.SignInAndUpFeature(
providers=[
ProviderInput(
config=ProviderConfig(
third_party_id="apple",
clients=[
# Browser/Hub Apple login uses the Apple Services ID.
ProviderClientConfig(
client_type="web",
client_id="<APPLE_WEB_CLIENT_ID>",
client_secret="<APPLE_CLIENT_SECRET>",
),
# Native iOS Apple login returns authorization codes for the app bundle ID.
ProviderClientConfig(
client_type="ios",
client_id="<APPLE_IOS_BUNDLE_ID>",
client_secret="<APPLE_CLIENT_SECRET>",
),
],
)
)
]
)
)
RowndPluginConfig(
rownd_app_key="<ROWND_APP_KEY>",
rownd_app_secret="<ROWND_APP_SECRET>",
app_config={
"signInMethods": [
{
"method": "apple",
"clientId": "<APPLE_WEB_CLIENT_ID>",
# These map Rownd platforms to the SuperTokens Apple clients above.
"webClientType": "web",
"iosClientType": "ios",
}
],
},
)If Android uses a separate Apple client, add another SuperTokens Apple client with clientType: "android" and set androidClientType: "android" on the Rownd Apple sign-in method.
2. Configure the frontend SDK
After the backend plugin is deployed and reachable, configure each client application to use the SuperTokens Rownd-compatible Hub.
Every client needs the same values configured on the backend:
appKey: the Rownd app key used by the backend plugin.apiDomain: the public backend origin that hosts SuperTokens and the Rownd plugin routes.apiBasePath: the SuperTokens API base path, for example<API_BASE_PATH>.clientDomain: optional key from the backendclientDomainsmap.
2.1 Install the React SDK
2.1 Load the hosted Hub script
Use this option when you do not use a package-based frontend framework.
2.1 Add the Android SDK
The Android SDK is published through JitPack.
2.1 Add the iOS SDK
In Xcode, add this Swift Package dependency:
2.1 Install the Flutter SDK
Add the SuperTokens Rownd Flutter package to pubspec.yaml:
2.1 Install the React Native SDK
npm install @supertokens/rownd-reactyarn add @supertokens/rownd-reactpnpm add @supertokens/rownd-reactbun add @supertokens/rownd-react<script>
window._rphConfig = window._rphConfig || [];
window._rphConfig.push(["setClientDomain", "browser"]);
</script>
<script
async
src="https://rownd-hub.supertokens.com/static/scripts/rph.js?appKey=<ROWND_APP_KEY>&apiDomain=<API_DOMAIN>&apiBasePath=<API_BASE_PATH>"
></script>
<script
type="module"
async
src="https://rownd-hub.supertokens.com/static/scripts/rph.mjs?appKey=<ROWND_APP_KEY>&apiDomain=<API_DOMAIN>&apiBasePath=<API_BASE_PATH>"
></script>dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url "https://jitpack.io" }
}
}https://github.com/supertokens/supertokens-rownd-ios.gitdependencies:
supertokens_rownd_flutter: ^0.1.0
provider: ^6.1.2npm install @supertokens/rownd-react-nativeyarn add @supertokens/rownd-react-nativepnpm add @supertokens/rownd-react-nativebun add @supertokens/rownd-react-native2.2 Add the provider
Replace imports from @rownd/react with @supertokens/rownd-react, then add RowndProvider near the root of your application.
The script URL supports these query parameters:
| Parameter | Required | Description |
|---|---|---|
appKey |
Yes | Rownd app key used by the backend plugin. |
apiDomain |
Yes | Public backend origin that hosts the plugin routes. |
apiBasePath |
No | SuperTokens API base path. Defaults to <API_BASE_PATH>. |
appVariantId |
No | Rownd app variant or sub-brand ID. |
clientDomain |
No | Key from the backend plugin clientDomains map. |
displayContext |
No | Usually browser for direct web integrations. |
2.2 Use runtime config
Use window._rphConfig for optional settings that are easier to set in JavaScript than in the script URL.
Select the Rownd package product and add it to your app target.
If you use CocoaPods instead, install the RowndSupertokens pod. The pod exposes the same Swift module, so app code still imports Rownd.
2.2 Configure Rownd
Then fetch dependencies:
React Native apps must use React Native 0.61 or newer. Native builds also need Android minSdkVersion 26 or newer and iOS deployment target 14.0 or newer.
2.2 Expo setup
For Expo apps, add the plugin, a URL scheme, and native platform versions to app.json. Use a development build or prebuild so native URL scheme and platform configuration is generated.
import React from "react";
import ReactDOM from "react-dom/client";
import { RowndProvider } from "@supertokens/rownd-react";
import { App } from "./App";
ReactDOM.createRoot(document.getElementById("root")!).render(
<RowndProvider
appKey="<ROWND_APP_KEY>"
clientDomain="browser_local"
supertokens={{
appInfo: {
appName: "My App",
apiDomain: "<API_DOMAIN>",
apiBasePath: "<API_BASE_PATH>",
},
}}
>
<App />
</RowndProvider>,
);<script>
window._rphConfig = window._rphConfig || [];
window._rphConfig.push(["setPostLoginRedirect", "/profile"]);
window._rphConfig.push(["setPostSignOutRedirect", "/"]);
window._rphConfig.push(["setClientDomain", "browser_local"]);
</script>dependencies {
implementation 'com.github.supertokens:supertokens-rownd-android:0.1.1'
}import Rownd
import UIKit
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
) -> Bool {
Task {
await Rownd.configure(
launchOptions: launchOptions,
appKey: "<ROWND_APP_KEY>",
supertokens: RowndSuperTokensConfig(
appName: "My App",
apiDomain: "<API_DOMAIN>",
apiBasePath: "<API_BASE_PATH>"
)
)
}
return true
}flutter pub get{
"expo": {
"scheme": "rowndsupertokens",
"plugins": [
"@supertokens/rownd-react-native",
[
"expo-build-properties",
{
"android": {
"minSdkVersion": 26
},
"ios": {
"deploymentTarget": "14.0"
}
}
]
]
}
}Do not manually include the Hub script in your HTML when using the React SDK. The provider injects the Hub script for you.
2.3 Use Rownd-compatible APIs
Add _rphConfig entries before the Hub script loads.
The SDK requires compileSdk 35 or newer, Kotlin Gradle plugin 2.1.0 or newer, and minSdk 26 or newer.
2.2 Add configuration values
2.3 Configure links
Add an Associated Domains entitlement for the Hub domain used by the app.
2.2 Configure Rownd
Import the Flutter package and configure it before using any Rownd APIs.
Install the Expo build properties plugin before running prebuild:
import { RequireSignIn, SignedIn, SignedOut, useRownd } from "@supertokens/rownd-react";
export function AuthControls() {
const { requestSignIn, signOut, user } = useRownd();
return (
<div>
<SignedOut>
<button onClick={() => requestSignIn({ method: "email" })}>Email</button>
<button onClick={() => requestSignIn({ method: "phone" })}>Phone</button>
<button onClick={() => requestSignIn({ method: "google" })}>Google</button>
<button onClick={() => requestSignIn({ method: "apple" })}>Apple</button>
<button onClick={() => requestSignIn({ method: "anonymous" })}>Guest</button>
</SignedOut>
<SignedIn>
<p>{user.data?.email || user.data?.phone_number || user.data?.user_id}</p>
<button onClick={() => signOut()}>Sign out</button>
</SignedIn>
<RequireSignIn>
<p>Protected content</p>
</RequireSignIn>
</div>
);
}android {
defaultConfig {
manifestPlaceholders = [rowndDeepLinkScheme: "rowndsupertokens"]
buildConfigField "String", "ROWND_APP_KEY", '"<ROWND_APP_KEY>"'
buildConfigField "String", "ROWND_API_DOMAIN", '"<API_DOMAIN>"'
buildConfigField "String", "ROWND_API_BASE_PATH", '"<API_BASE_PATH>"'
buildConfigField "String", "ROWND_DEEP_LINK_SCHEME", '"rowndsupertokens"'
}
}<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:my-app.rownd-hub.supertokens.com</string>
</array>import 'package:supertokens_rownd_flutter/rownd.dart';
import 'package:supertokens_rownd_flutter/rownd_platform_interface.dart';
final rowndPlugin = RowndPlugin();
void configureRownd() {
rowndPlugin.configure(RowndConfig(
appKey: '<ROWND_APP_KEY>',
supertokens: RowndSuperTokensConfig(
appInfo: RowndSuperTokensAppInfo(
appName: 'My Flutter App',
apiDomain: '<API_DOMAIN>',
apiBasePath: '<API_BASE_PATH>',
),
),
));
}npx expo install expo-build-propertiesrequestSignIn() supports Rownd-style options such as identifier, auto_sign_in, init_data, post_login_redirect, include_user_data, redirect, intent, group_to_join, prevent_closing, method, and method_options.
2.3 Configure deep links
Add one custom-scheme fallback filter and one verified HTTPS App Link filter.
Register the custom URL scheme fallback.
The Flutter package is published as supertokens_rownd_flutter. Existing Rownd-style APIs remain available through RowndPlugin, but the package import and SuperTokens config are required for the migrated SDK.
2.3 Use Rownd-compatible APIs
The SDK exposes Rownd state through a ChangeNotifier. Provide rowndPlugin.state() to your widget tree and use the plugin methods for sign-in, sign-out, account management, user profile calls, and access tokens.
2.3 Add the provider
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="${rowndDeepLinkScheme}" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="my-app.rownd-hub.supertokens.com" />
</intent-filter><key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>rowndsupertokens</string>
</array>
</dict>
</array>import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:supertokens_rownd_flutter/rownd.dart';
import 'package:supertokens_rownd_flutter/rownd_platform_interface.dart';
import 'package:supertokens_rownd_flutter/state/global_state.dart';
final rowndPlugin = RowndPlugin();
void main() {
WidgetsFlutterBinding.ensureInitialized();
rowndPlugin.configure(RowndConfig(
appKey: '<ROWND_APP_KEY>',
supertokens: RowndSuperTokensConfig(
appInfo: RowndSuperTokensAppInfo(
appName: 'My Flutter App',
apiDomain: '<API_DOMAIN>',
apiBasePath: '<API_BASE_PATH>',
),
),
));
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => rowndPlugin.state()),
Provider<RowndPlugin>.value(value: rowndPlugin),
],
child: const MaterialApp(home: AuthControls()),
);
}
}
class AuthControls extends StatelessWidget {
const AuthControls({super.key});
@override
Widget build(BuildContext context) {
return Consumer<GlobalStateNotifier>(
builder: (_, rownd, __) {
final isAuthenticated = rownd.state.auth?.isAuthenticated ?? false;
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
final plugin = context.read<RowndPlugin>();
if (isAuthenticated) {
plugin.signOut();
} else {
plugin.requestSignIn();
}
},
child: Text(isAuthenticated ? 'Sign out' : 'Sign in'),
),
),
);
},
);
}
}import { RowndProvider } from "@supertokens/rownd-react-native";
export default function Root() {
return (
<RowndProvider
config={{
appKey: "<ROWND_APP_KEY>",
supertokens: {
appInfo: {
appName: "My App",
apiDomain: "<API_DOMAIN>",
apiBasePath: "<API_BASE_PATH>",
},
},
deepLinkScheme: "rowndsupertokens",
}}
>
<App />
</RowndProvider>
);
}The HTTPS App Link domain should match clientDomains.mobile on the backend.
2.4 Initialize Rownd
Forward custom URL scheme links and Universal Links to Rownd.
requestSignIn() accepts an optional RowndSignInOptions object. The migrated Flutter SDK currently exposes postSignInRedirect as the sign-in option.
2.4 Configure Android
Flutter Android apps need JitPack because the native SuperTokens Rownd Android SDK is resolved from JitPack.
The React Native provider accepts appKey, supertokens.appInfo, deepLinkScheme, and optional hubUrlOverride. Use hubUrlOverride only for staging or local Hub testing. React Native does not send a clientDomain prop; mobile Hub flows use the backend clientDomains.mobile default.
2.4 Register native links
For bare React Native iOS apps, install pods after adding the package:
import android.app.Application
import io.rownd.android.Rownd
import io.rownd.android.RowndConfigureOptions
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Rownd.configure(
this,
RowndConfigureOptions(
appKey = BuildConfig.ROWND_APP_KEY,
apiDomain = BuildConfig.ROWND_API_DOMAIN,
apiBasePath = BuildConfig.ROWND_API_BASE_PATH,
deepLinkScheme = BuildConfig.ROWND_DEEP_LINK_SCHEME,
)
)
}
}func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
return Rownd.handleSmartLink(url: url)
}
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else {
return false
}
return Rownd.handleSmartLink(url: url)
}dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url "https://jitpack.io" }
}
}cd ios && pod install2.5 Call protected APIs
Rownd manages the SuperTokens session after sign-in. For OkHttp, add the SuperTokens interceptor to clients that call protected backend APIs.
The Universal Link domain should match clientDomains.mobile on the backend.
Set Android platform versions and Kotlin metadata support:
Register the same scheme in Info.plist and forward URL opens to React Native Linking. The React Native Rownd provider listens for Linking events and passes matching links to the native SDK.
import com.supertokens.session.SuperTokensInterceptor
import okhttp3.OkHttpClient
val client = OkHttpClient.Builder()
.addInterceptor(SuperTokensInterceptor())
.build()android {
compileSdk 35
defaultConfig {
minSdk 26
targetSdk 35
}
}<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>rowndsupertokens</string>
</array>
</dict>
</array>Use Kotlin Gradle plugin 2.1.0 or newer. Also make your main activity extend FlutterFragmentActivity instead of FlutterActivity:
Objective-C app delegate:
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity : FlutterFragmentActivity()#import <React/RCTLinkingManager.h>
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}2.5 Configure iOS
The Flutter plugin depends on the RowndSupertokens CocoaPod. The pod exposes the Swift module as Rownd, so Flutter apps do not need app-level Swift import changes.
Install pods after adding the package:
Swift app delegate:
cd ios && pod installimport React
override func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
return RCTLinkingManager.application(app, open: url, options: options)
}If an existing lockfile pins an older lottie-ios version, update pods:
For Android, register the scheme on the activity that hosts React Native and use singleTask. The scheme must match config.deepLinkScheme; singleTask is required so links opened while the app is running are delivered to the existing React Native activity.
cd ios && pod update lottie-ios --repo-update<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="rowndsupertokens" />
</intent-filter>
</activity>2.6 Configure mobile links
Configure the same native link handling described in the Android and iOS tabs for Flutter’s Android and iOS host apps. The HTTPS App Link or Universal Link domain should match clientDomains.mobile on the backend.
If your bare React Native Android app uses Google Sign-In, initialize the Rownd package from MainActivity before calling auth APIs:
import android.os.Bundle
import com.facebook.react.ReactActivity
import com.reactnativerowndplugin.RowndPluginPackage
class MainActivity : ReactActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
RowndPluginPackage.preInit(this)
}
}import { Pressable, Text, View } from "react-native";
import { useRownd } from "@supertokens/rownd-react-native";
export function AuthControls() {
const { is_authenticated, requestSignIn, signOut, user, getAccessToken } = useRownd();
async function callProtectedApi() {
const accessToken = await getAccessToken();
// Use accessToken in the Authorization header for your protected API call.
}
if (is_authenticated) {
return (
<View>
<Text>Welcome {user.data?.email ?? user.data?.first_name}</Text>
<Pressable onPress={callProtectedApi}>
<Text>Get access token</Text>
</Pressable>
<Pressable onPress={() => signOut()}>
<Text>Sign out</Text>
</Pressable>
</View>
);
}
return (
<Pressable onPress={() => requestSignIn()}>
<Text>Sign in</Text>
</Pressable>
);
}requestSignIn() accepts Rownd-style options such as method, postSignInRedirect, and intent. The guest method is treated as anonymous. On Android, forcing email or phone currently opens the Hub default flow rather than bypassing the method selector.
3. Validate client flows
Test the following flows to validate your client integration:
- Existing users can authenticate
- User logins and sign ups are migrated to SuperTokens
- Existing Rownd sessions migrate without forcing users to sign in again.
- Deep links work as expected on mobile
4. Migrate OAuth/OIDC clients (optional)
If your Rownd application acts as an OAuth/OIDC provider, update clients to use SuperTokens discovery and endpoints after the SuperTokens team migrates your Rownd OAuth clients into SuperTokens Core.
4.1 Replace the discovery URL
Replace the Rownd discovery URL:
https://api.rownd.io/oidc/{rowndAppId}/.well-known/openid-configuration
with your SuperTokens discovery URL:
<API_DOMAIN>/<API_BASE_PATH>/.well-known/openid-configuration
4.2 Replace hardcoded endpoints
If a client hardcodes endpoints, update them like this:
| Rownd endpoint | SuperTokens endpoint |
|---|---|
/oidc/{appId}/.well-known/openid-configuration |
<API_BASE_PATH>/.well-known/openid-configuration |
/oidc/{appId}/auth |
<API_BASE_PATH>/oauth/auth |
/oidc/{appId}/token |
<API_BASE_PATH>/oauth/token |
/oidc/{appId}/me |
<API_BASE_PATH>/oauth/userinfo |
/oidc/{appId}/jwks |
<API_BASE_PATH>/jwt/jwks.json |
/oidc/{appId}/token/introspection |
<API_BASE_PATH>/oauth/introspect |
/oidc/{appId}/token/revocation |
<API_BASE_PATH>/oauth/revoke |
/oidc/{appId}/session/end |
<API_BASE_PATH>/oauth/end_session |
Replace <API_BASE_PATH> with your configured backend API base path.
4.3 Confirm client IDs and tokens
Continue using the OAuth credential client_id and client_secret that Rownd issued and the SuperTokens team migrated. Do not use the Rownd OIDC client configuration id as the OAuth client_id.
Existing Rownd-issued OAuth tokens are not SuperTokens-issued tokens. After cutover, users should complete a new authorization flow against SuperTokens unless a separate token migration path is explicitly enabled for your project.
4.4 Validate OAuth
Check discovery and JWKS:
curl <API_DOMAIN>/<API_BASE_PATH>/.well-known/openid-configuration
curl <API_DOMAIN>/<API_BASE_PATH>/jwt/jwks.json
Start an authorization-code flow:
<API_DOMAIN>/<API_BASE_PATH>/oauth/auth?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=code&scope=openid%20profile%20email%20phone%20offline_access
Exchange the code:
curl -X POST <API_DOMAIN>/<API_BASE_PATH>/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "CLIENT_ID:CLIENT_SECRET" \
-d "grant_type=authorization_code" \
-d "code=AUTH_CODE" \
-d "redirect_uri=REDIRECT_URI"
Fetch userinfo:
curl <API_DOMAIN>/<API_BASE_PATH>/oauth/userinfo \
-H "Authorization: Bearer ACCESS_TOKEN"