uflowid

Flutter SDK for UFlow ID (id.uflow.uz) + UFlow Cloud (cloud.uflow.uz) — email OTP, OAuth with API key / client_id, SSO across com.modder.* apps, and black-white theme matching Next.js.

This is v2.0.0 - major update:

  • Cloud OAuth: Get client_id + apiKey (uf_live_...) from https://cloud.uflow.uz/dashboard, then OAuth flow
  • SSO: One UFlow account works across all com.modder.* apps, plus optional device-local instant SSO via sharedUserId
  • Black-white theme: No more RGB gradients, only UFlowTheme.light() / dark() matching globals.css (bg #f7f8fa light, #000000 dark, CTA blue)

Old package uflow_idflutter still works (same code), but uflowid is short name.

Install

dependencies:
  uflowid: ^2.0.0
import 'package:uflowid/uflowid.dart';

Quick start (Cloud OAuth + SSO)

  1. Create app in Cloud Dashboard: https://cloud.uflow.uz/dashboard -> New App -> get client_id (app id) and create API key uf_live_...
  2. Initialize in main():
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await UflowId.initialize(
    supabaseUrl: 'https://YOUR-PROJECT.supabase.co',
    supabaseAnonKey: 'YOUR-ANON-KEY',
    cloudBaseUrl: 'https://cloud.uflow.uz',
    apiBase: 'https://id.uflow.uz',
    clientId: 'your-app-id-from-dashboard',
    apiKey: 'uf_live_...', // confidential clients, or null for PKCE public clients
    redirectUri: 'uz.uflow.widgets://oauth', // must match dashboard redirect_uris
  );
  // Try silent SSO from previous app
  await UflowSSO.instance.trySilentSignIn();
  runApp(MyApp());
}
  1. OAuth login:
// Start login - get authorize_url
final login = await UflowIdAuth.instance.startCloudLogin(
  redirectUri: 'uz.uflow.widgets://oauth',
  scopes: ['user:read'],
  usePKCE: true, // mobile = true (no apiKey exposure)
);

// Open in browser
import 'package:url_launcher/url_launcher.dart';
await launchUrl(Uri.parse(login.authorizeUrl), mode: LaunchMode.externalApplication);

// In your deep link handler (app_links or uni_links):
// uz.uflow.widgets://oauth?code=AbC123&state=...
final tokens = await UflowIdAuth.instance.handleOAuthCallback(
  callbackUrl: 'uz.uflow.widgets://oauth?code=AbC123...',
);
// tokens.accessToken is uf_at_... -> stored automatically
  1. Use Cloud APIs:
final me = await UflowCloud.instance.me(); // who is this token?
final user = await UflowCloud.instance.getUser(); // full profile

final active = await UflowIdSubscriptions.instance.isActive('widgets_full');

SSO among com.modder.* apps

One account everywhere:

  • Supabase auth.users is same table on uflow.uz, cloud.uflow.uz, id.uflow.uz -> signing in once, same user_id everywhere
  • Cloud grants stored server-side (cloud_user_grants) -> if user authorized uz.uflow.widgets, and later opens com.modder.otherapp, you can check hasPreviousGrant()
  • Browser SSO: cloud.uflow.uz uses cookie domain .uflow.uz, so session on cloud is valid on id. Mobile OAuth via system browser will be 1-tap if already signed in.

Device-local instant SSO (optional):

Add to all your com.modder.* apps' android/app/src/main/AndroidManifest.xml:

<manifest android:sharedUserId="uz.uflow.shared" ...>

And sign all with same keystore. Then tokens stored under uflow_access_token can be shared via native:

val ctx = createPackageContext("uz.uflow.widgets", 0)
val prefs = ctx.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
val token = prefs.getString("flutter.uflow_access_token", null)

Or use deep link export/import:

// App A (has token)
final link = await UflowSSO.instance.exportSSOLink(); // com.modder.sso://import?access_token=...
// Share via Intent to App B

// App B
await UflowSSO.instance.importSSOLink(link);

Email OTP (legacy, still works)

await UflowIdAuth.instance.sendOtp('user@example.com');
await UflowIdAuth.instance.verifyOtp('user@example.com', '123456');

Theme - black-white like Next.js

Old RGB gradient removed. New:

MaterialApp(
  theme: UFlowTheme.light(),
  darkTheme: UFlowTheme.dark(),
  themeMode: ThemeMode.system, // follows .dark class like Next.js
  home: MyPage(),
)

// Widgets:
UFlowCard(child: Text('card matching .card in globals.css'))
UFlowButton(label: 'Primary', onPressed: () {}, variant: UFlowButtonVariant.primary) // .btn-primary
UFlowButton(label: 'Ghost', variant: UFlowButtonVariant.ghost)
UFlowBadge(label: 'Active', variant: UFlowBadgeVariant.green)

Colors from cloud_uflow/app/globals.css:

  • Light bg #f7f8fa, surface #ffffff, border #e3e6ec, text #1c1c1e, CTA #1544e0
  • Dark bg #000000, surface #0a0a0c, border #26262b, text #ffffff, CTA #2f6bff

API Reference

Class Description
UflowId initialize(), isSignedIn, accessToken, clientId, apiKey, debugInfo
UflowIdAuth sendOtp, verifyOtp, startCloudLogin, handleOAuthCallback, exchangeCode, signOut
UflowCloud login, me, getUser, contact
UflowOAuth exchangeCode, refresh, parseCallback, loadTokens, clearTokens
UflowSSO isSignedIn, trySilentSignIn, exportSSOLink, importSSOLink, hasPreviousGrant, clear
UflowIdSubscriptions isActive, get, activate

Libraries

uflow_idflutter
uflowid
UFlow ID Flutter SDK - v2.0.0 Black-white theme like id.uflow.uz / cloud.uflow.uz + Cloud OAuth + SSO