Skip to main content

AuthCentral Flutter SDK — Integration Reference

A compact reference for integrating the AuthCentral SDK (authcentral_sdk) into a host Flutter application. Every code block is copy-pasteable. Prefer the snippets here over inventing APIs — every public symbol shown below is part of the current SDK surface.

0. Use this page with an LLM assistant

This reference is also published as a Claude Skill file — a single Markdown document your AI assistant (Claude Code, Claude Desktop, Cursor, etc.) can load as context while integrating the SDK. Pointing the assistant at it is usually the fastest way to get correct, SDK-aware code generation.

Download: authcentral-sdk.md (right-click → Save As, or curl below).

Install as a Claude Code skill

# Project-level (available only inside this repo)
mkdir -p .claude/skills/authcentral-sdk
curl -L <DOCS_URL>/skills/authcentral-sdk.md \
-o .claude/skills/authcentral-sdk/SKILL.md

# User-level (available in every project on your machine)
mkdir -p ~/.claude/skills/authcentral-sdk
curl -L <DOCS_URL>/skills/authcentral-sdk.md \
-o ~/.claude/skills/authcentral-sdk/SKILL.md

Claude Code picks up the skill on next launch; trigger it by asking "integrate AuthCentral SDK …".

Use with other LLM tools

  • Cursor / Windsurf / generic chat UIs: download the file and attach it to the conversation (drag-and-drop or @file reference).
  • Raw prompt context: paste the file contents into your system prompt. The file's own front-matter description field tells the LLM when to use it, so most agentic tools will pick it up automatically once indexed.

The downloadable file carries Claude skill frontmatter (name / description) so agentic tools pick it up automatically; the sections below are the source of truth.

1. What the SDK does

  • Provides a drop-in authentication screen (SsoSdk.authScreen()) with full internal routing: login, signup, email/phone verification, backup, success.
  • Exposes a reactive token store (SsoSdk.instance.tokenStore) and auth controller (SsoSdk.instance.authController) for headless use.
  • Supports email/password, Google, Apple, X (Twitter), Telegram and ETH/SOL/TON wallets.
  • Ships three built-in themes (Chatoshi, VIP, Hero) in dark + light variants; fully custom themes are supported.
  • 17 languages with auto-detection from device locale and per-string overrides.

Distribution: GitLab git dependency. Platforms: iOS 14+, Android API 23+. Requires: Flutter ≥ 3.41.0, Dart ≥ 3.11.0.

2. Golden-path quick start

Minimal working integration — copy, replace the three placeholders, ship.

# pubspec.yaml
dependencies:
authcentral_sdk:
git:
url: <gitlab-repo-url>
path: authCentralSDK
// lib/main.dart
import 'package:authcentral_sdk/authcentral_sdk.dart';
import 'package:flutter/material.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();

await SsoSdk.initialize(SsoConfig(
baseUrl: 'https://api.authcentral-1.hero-dev.xyz',
origin: SsoOrigin.heroDev,
appName: 'MyApp',
theme: SsoResolvedTheme.heroDark(),
));

runApp(const MyApp());
}

// Somewhere in your UI:
Future<void> openAuth(BuildContext context) async {
const appToken = String.fromEnvironment('APP_TOKEN');
final authToken = await SsoSdk.instance.getAccessToken(appToken);

await Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => SsoSdk.authScreen(
enabledProviders: const [
SsoProvider.emailPassword,
SsoProvider.google,
SsoProvider.apple,
],
authToken: authToken,
onSuccess: (tokens) => Navigator.of(context).pop(),
onCancel: () => Navigator.of(context).pop(),
),
),
);
}

// Reactive read:
StreamBuilder<SsoTokens?>(
stream: SsoSdk.instance.tokenStore.tokensStream,
initialData: SsoSdk.instance.tokenStore.currentTokens,
builder: (ctx, snap) => snap.data != null ? HomePage() : LoginPage(),
);

Two-token model. appToken (app-private) is passed to getAccessToken() to mint an authToken. The authToken is passed to authScreen(). After successful auth, SsoTokens (authToken + refreshToken) are stored in SsoSdk.instance.tokenStore and streamed to you.

3. Installation

3.1 Dependency

dependencies:
authcentral_sdk:
git:
url: <gitlab-repo-url>
path: authCentralSDK

Run: flutter pub get.

3.2 iOS (ios/Runner/Info.plist)

Google Sign-In — add the reversed client ID:

<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.YOUR_CLIENT_ID</string>
</array>
</dict>
</array>

Deep links (wallets, X) — add your app scheme to the same CFBundleURLTypes array (alongside Google, not instead):

<dict>
<key>CFBundleURLSchemes</key>
<array><string>heroapp</string></array>
</dict>

3.3 Android (android/app/src/main/AndroidManifest.xml)

<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="heroapp" />
</intent-filter>

For Google Sign-In on Android also drop google-services.json into android/app/.

3.4 App secrets

Keep APP_TOKEN out of source. Use --dart-define-from-file=.env and String.fromEnvironment('APP_TOKEN').

4. Initialization — SsoConfig

SsoSdk.initialize() must be called once before any other SDK call.

await SsoSdk.initialize(SsoConfig(
// Required
baseUrl: 'https://api.authcentral-1.hero-dev.xyz',
origin: SsoOrigin.heroDev,
appName: 'MyApp',

// Branding
logo: 'assets/icons/my_logo.svg', // asset path or https:// URL

// Theme (default: SsoResolvedTheme.chatoshiDark())
theme: SsoResolvedTheme.heroDark(),

// Localization (default: auto-detect from device)
locale: const Locale('en'),
textOverrides: {'login_title': 'Welcome back!'},

// Deep links for wallets / X / Telegram
appScheme: 'heroapp',

// Wallet provider configuration (required if SsoProvider.wallet is used)
walletConfig: const SsoWalletConfig(
walletConnectProjectId: 'your-project-id',
tonConnectManifestUrl: 'https://example.com/tonconnect-manifest.json',
),

// Analytics
analytics: SsoAnalyticsConfig(
onEvent: (event) => FirebaseAnalytics.instance
.logEvent(name: event.name, parameters: event.properties),
onError: (error) => Sentry.captureException(error),
suppressedEvents: {'sso_screen_viewed'},
),

// Global callbacks (fire on every successful auth, across screens)
onAuthSuccess: (tokens) => print('Authenticated'),
onAuthFailure: () => print('Auth failed'),

// Environment / networking / security
environment: SsoEnvironment.production,
debug: false,
connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 30),
allowInsecureDevices: false,
));

4.1 Parameter reference

ParameterTypeRequiredDefaultNotes
baseUrlStringyesAuthCentral backend URL
originSsoOriginyesSent as Origin header (see below)
appNameStringyesShown in auth screens
logoString?nonullAsset path or URL
themeSsoResolvedTheme?nochatoshiDarkSee §8
localeLocale?nonull → autoSee §9
textOverridesMap<String,String>?nonullPer-key string overrides
appSchemeString?nonullDeep-link scheme (wallets, X)
walletConfigSsoWalletConfig?nonullRequired for wallets
analyticsSsoAnalyticsConfig?nonullEvent + error forwarding
onAuthSuccessFunction(SsoTokens)?nonullGlobal success hook
onAuthFailureFunction()?nonullGlobal failure hook
environmentSsoEnvironmentnoproduction
debugboolnofalseVerbose logs
connectTimeoutDurationno30s
receiveTimeoutDurationno30s
allowInsecureDevicesboolnofalseRooted/jailbroken
tokenStorageAdapterTokenStorageAdapter?nonullOverride secure storage
httpInterceptorsList<dynamic>?nonullExtra Dio interceptors

4.2 SsoOrigin values

EnumURLUse for
SsoOrigin.heroProdhttps://auth.hero.ioHero production
SsoOrigin.heroDevhttps://authcentral-1.hero-dev.xyzHero dev
SsoOrigin.chatoshiProdhttps://auth.chatoshi.aiChatoshi production
SsoOrigin.chatoshiDevhttps://auth.chatoshi.devChatoshi dev
SsoOrigin.vipProdhttps://auth.vip.aiVIP production
SsoOrigin.vipDevhttps://authcentral-1.dev-vip.netVIP dev

4.3 Teardown

SsoSdk.dispose(); // releases all SDK resources; re-init to use again

5. Opening the auth screen

Two presentation modes — same parameters (plus sheet-specific ones).

5.1 Full-page (Navigator.push)

Navigator.of(context).push(MaterialPageRoute(
builder: (_) => SsoSdk.authScreen(
enabledProviders: const [
SsoProvider.emailPassword,
SsoProvider.google,
SsoProvider.apple,
SsoProvider.telegram,
SsoProvider.x,
SsoProvider.wallet,
],
enabledWallets: const [
SsoWalletType.metaMask,
SsoWalletType.trustWallet,
SsoWalletType.walletConnect,
SsoWalletType.phantom,
SsoWalletType.tonKeeper,
],
authToken: authToken,
theme: SsoResolvedTheme.heroDark(), // overrides init-level theme
locale: 'en', // overrides init-level locale
onSuccess: (tokens) => Navigator.of(context).pop(),
onError: (err) => debugPrint('code=${err.code} msg=${err.message}'),
onCancel: () => Navigator.of(context).pop(),
termsUrl: 'https://example.com/terms',
privacyUrl: 'https://example.com/privacy',
),
));

5.2 Bottom sheet

await SsoSdk.showAuthBottomSheet(
context: context,
enabledProviders: const [SsoProvider.emailPassword, SsoProvider.google],
authToken: authToken,
onSuccess: (tokens) {/* sheet auto-dismisses */},
onCancel: () {/* swipe / scrim tap */},
// Sheet-specific:
heightFactor: 0.9, // fraction of screen height
topRadius: 20.0, // logical pixels
isDismissible: true, // tap scrim dismisses
enableDrag: true, // swipe down dismisses
);

When to use each.

  • Push route: app has its own back stack and users expect a dedicated page.
  • Bottom sheet: quick sign-in flow, e.g. gating a single action inside an existing screen.

5.3 Parameters (both modes)

ParameterTypeRequiredDescription
enabledProvidersList<SsoProvider>yesAuth methods to show
enabledWalletsList<SsoWalletType>?noWallets; requires SsoProvider.wallet
authTokenString?noAccess token from getAccessToken()
themeSsoResolvedTheme?noPer-screen theme override
localeString?noPer-screen language ('en', 'ru', …)
onSuccessFunction(SsoTokens)?noCalled on success
onCancelVoidCallback?noCalled on user dismiss
onErrorFunction(SsoError)?noCalled on critical errors
termsUrlString?noTerms of Service URL
privacyUrlString?noPrivacy Policy URL

Internal flow is managed by the SDK:

OAuth landing → Login | Sign Up

Email / Phone verification

Backup contact

Success → onSuccess

Do not wrap authScreen() in another Navigator — it owns its own.

6. Providers

Each provider is opted-in via enabledProviders. Some require platform or backend setup on top of the base install.

ProviderEnumExtra setup
Email / passwordSsoProvider.emailPasswordNone
GoogleSsoProvider.googleiOS reversed-client-id URL scheme; Android google-services.json
AppleSsoProvider.appleBackend: /apple/connect response must include a nonce field
X (Twitter)SsoProvider.xappScheme; backend must accept redirect_uri on /x/connect and whitelist the deep link
TelegramSsoProvider.telegramappScheme; verify backend does not validate origin
WalletSsoProvider.walletappScheme + walletConfig; see §6.1

6.1 Wallets

Pass walletConfig at init and enable at least one SsoWalletType:

await SsoSdk.initialize(SsoConfig(
baseUrl: '...', origin: SsoOrigin.heroDev, appName: 'MyApp',
appScheme: 'myapp',
walletConfig: const SsoWalletConfig(
walletConnectProjectId: 'your-project-id',
tonConnectManifestUrl: 'https://example.com/tonconnect-manifest.json',
),
));

SsoSdk.authScreen(
enabledProviders: const [SsoProvider.wallet],
enabledWallets: const [
SsoWalletType.metaMask,
SsoWalletType.trustWallet,
SsoWalletType.walletConnect,
SsoWalletType.phantom,
SsoWalletType.tonKeeper,
],
authToken: authToken,
);
WalletEnumChainProtocol
MetaMaskSsoWalletType.metaMaskETHWalletConnect v2 / deep link
Trust WalletSsoWalletType.trustWalletETHWalletConnect v2 / deep link
WalletConnectSsoWalletType.walletConnectETH (universal)WalletConnect v2
PhantomSsoWalletType.phantomSOLDeep link + X25519
TonkeeperSsoWalletType.tonKeeperTONTON Connect v2 (SSE bridge)

7. Post-auth API — tokens, state, helpers

Everything useful lives under SsoSdk.instance.

7.1 Token store (SsoSdk.instance.tokenStore)

final store = SsoSdk.instance.tokenStore;

store.currentTokens; // SsoTokens? (snapshot)
store.authToken; // String?
store.refreshToken; // String?
store.isAuthenticated; // bool

store.tokensStream.listen((tokens) {
if (tokens == null) {/* logged out */}
});

Tokens are persisted in flutter_secure_storage — never in SharedPreferences, SQLite or files. Never log them.

7.2 Auth controller (SsoSdk.instance.authController)

final c = SsoSdk.instance.authController;

c.stateStream.listen((state) {
switch (state) {
case SsoAuthenticated(): // signed in
case SsoUnauthenticated(): // signed out
case SsoAuthenticating(): // in progress
case SsoTokenRefreshing(): // silent refresh
case SsoAuthError(:final error): // failed
}
});

// Manual refresh (usually unnecessary — SDK refreshes at 80% TTL)
final res = await c.refreshToken();
switch (res) {
case SsoSuccess(): // ok
case SsoFailure(:final error): // handle
}

await c.logout(); // clears tokens + session state

7.3 Helpers

// Mint an access token for the auth flow
final authToken = await SsoSdk.instance.getAccessToken('app-private-token');

// Metadata about the OAuth-initiating side app
final info = await SsoSdk.instance.getSideApplicationInfo(authToken: authToken);
// info.allowProviders, info.accessToken.payload.redirectUrlOnSuccess

// Supported countries
final publicList = await SsoSdk.instance.getCountries(authToken: authToken);
final privateList = await SsoSdk.instance.getCountriesPrivate();

8. Theming

8.1 Built-in themes

SsoResolvedTheme.chatoshiDark(); SsoResolvedTheme.chatoshiLight();
SsoResolvedTheme.vipDark(); SsoResolvedTheme.vipLight();
SsoResolvedTheme.heroDark(); SsoResolvedTheme.heroLight();

Each overrides the button/input radius; the rest of the fields are colors:

ThemeradiusButtonradiusInput
Chatoshi10.010.0
VIP8.08.0
Hero100.0 (pill)14.0

Set at init (default) or override per screen:

SsoSdk.initialize(SsoConfig(theme: SsoResolvedTheme.heroDark(), ...));
SsoSdk.authScreen(theme: SsoResolvedTheme.chatoshiLight(), ...);

8.2 Shape (radius) tokens

TokenDefaultApplied to
radiusButton8.0Primary/secondary/provider buttons
radiusInput8.0Text inputs, dropdowns
radiusBlock24.0Cards, info blocks
radiusButtonIcon1000.0Round icon buttons (keep large for pill)
radiusBottomSheet8.0Auth bottom-sheet top corners

Defaults above apply when you build a custom SsoResolvedTheme(...); built-in themes set their own.

8.3 Fully custom theme

final myTheme = SsoResolvedTheme(
isDark: true,
fontFamily: 'Roboto',

// Shape tokens — any subset
radiusButton: 12.0,
radiusInput: 12.0,
radiusBlock: 24.0,
radiusButtonIcon: 1000.0,
radiusBottomSheet: 16.0,

text: SsoTextColors(
primary: Color(0xFFFFFFFF),
secondary: Color(0xFFB0B0B0),
tertiary: Color(0xFF808080),
disabled: Color(0xFF555555),
onGradient: Color(0xFFFFFFFF),
onSnackbarHint: Color(0xFFCCCCCC),
highlight: Color(0xFFFFD700),
inverse: SsoInverseTextColors(
primary: Color(0xFF000000),
secondary: Color(0xFF333333),
tertiary: Color(0xFF666666),
),
),

brand: SsoBrandColors(
primary: Color(0xFF6C5CE7),
onPrimary: Color(0xFFFFFFFF),
inverse: SsoInverseBrandColors(
primary: Color(0xFF6C5CE7),
onPrimary: Color(0xFFFFFFFF),
),
),

// ... all other color groups are required — see SsoResolvedTheme constructor
);

Color groups (all required in the constructor): text, brand, surface, background, border, interactive, alerts, accent, disabled, elevation, tag, gradient, graphs, skeleton, native, skrim, links, logo, temporaryChat, gradients, shadow.

8.4 Extending a built-in theme

Copy every field from the base theme, then override what you need. Easy to forget the radius fields — if omitted they fall back to constructor defaults and you lose the base theme's shape:

final base = SsoResolvedTheme.heroDark();
final myTheme = SsoResolvedTheme(
text: base.text, surface: base.surface,
background: base.background, border: base.border,
interactive: base.interactive, alerts: base.alerts,
accent: base.accent, disabled: base.disabled,
elevation: base.elevation, tag: base.tag,
gradient: base.gradient, graphs: base.graphs,
skeleton: base.skeleton, native: base.native,
skrim: base.skrim, links: base.links,
logo: base.logo, temporaryChat: base.temporaryChat,
gradients: base.gradients, shadow: base.shadow,
isDark: base.isDark, fontFamily: base.fontFamily,

// Carry over shape tokens — otherwise pills become 8.0 rounded corners
radiusButton: base.radiusButton,
radiusInput: base.radiusInput,
radiusBlock: base.radiusBlock,
radiusButtonIcon: base.radiusButtonIcon,
radiusBottomSheet: base.radiusBottomSheet,

// Only what you're actually changing:
brand: SsoBrandColors(
primary: const Color(0xFFFF6B6B),
onPrimary: const Color(0xFFFFFFFF),
inverse: const SsoInverseBrandColors(
primary: Color(0xFFFF6B6B), onPrimary: Color(0xFFFFFFFF),
),
),
);

8.5 Reading theme inside custom widgets

final theme = SsoThemeProvider.of(context);
// theme.brand.primary, theme.text.secondary, theme.radiusButton, ...

9. Localization

17 languages: en, ru, de, fr, es, it, pt, pl, tr, ar, zh, ja, ko, hi, id, ms, fil. Unsupported locales fall back to en.

Priority (highest wins): authScreen(locale:)SsoConfig(locale:) → device locale → English.

// Global
SsoConfig(locale: const Locale('ru'), ...);

// Per screen
SsoSdk.authScreen(locale: 'fr', ...);

Per-string overrides apply across all languages:

SsoConfig(textOverrides: {
'login_title': 'Welcome back!',
'oauth_welcome_title': 'Sign in to {appName}',
});

String keys live in SsoStringKeys.

10. Common recipes

Gate a screen on auth state

StreamBuilder<SsoTokens?>(
stream: SsoSdk.instance.tokenStore.tokensStream,
initialData: SsoSdk.instance.tokenStore.currentTokens,
builder: (ctx, snap) => snap.data != null ? const HomePage() : const SignInPage(),
);

Attach the SDK's auth token to your own API calls

final token = SsoSdk.instance.tokenStore.authToken;
if (token != null) {
dio.options.headers['Authorization'] = 'Bearer $token';
}
// Or subscribe to tokensStream and refresh the header reactively.

Log user out

await SsoSdk.instance.authController.logout();

Forward SDK analytics to Firebase

SsoConfig(
analytics: SsoAnalyticsConfig(
onEvent: (e) => FirebaseAnalytics.instance.logEvent(
name: e.name, parameters: e.properties,
),
onError: (err) => Sentry.captureException(err),
),
);

11. Gotchas

  • Call initialize() once before runApp(). Everything else assumes the singleton exists.
  • Wallet provider requires appScheme + walletConfig. Without the scheme, deep-link callbacks silently fail.
  • Apple provider needs backend cooperation. /apple/connect response must include a nonce field; otherwise auth fails at verification.
  • X provider needs backend cooperation. /x/connect must accept a redirect_uri parameter and whitelist the deep-link callback URL.
  • Don't store tokens yourself — the SDK already persists them in flutter_secure_storage. Reading via tokenStore is the canonical path.
  • Never log authToken / refreshToken. Use the state stream, not print(), when debugging.
  • Extending a built-in theme: copy the radius fields. Omitted radii fall back to constructor defaults (8 / 8 / 24 / 1000 / 8), which silently breaks pill buttons / custom shape.
  • The auth screen owns its own Navigator. Don't push nested routes inside it; close it via onSuccess / onCancel.
  • getAccessToken(appToken) is required before opening the auth screen in the standard integration. The returned authToken is what you pass to authScreen(authToken:).
  • Dispose on teardown. Call SsoSdk.dispose() if you ever need to re-initialize with different config (e.g. tests, multi-tenant apps).