Flutter SDK
Abrova Auth for Flutter (abrova_auth). Anonymous, Apple, Google, e-mail and password, magic link and Telegram sign-in with only your public key; the session is kept in secure storage and the user token works in every Abrova SDK.
Uses the public API key only. Safe to ship in an app
initialize() loads the saved session with no network call
Refresh token in the Keychain / Keystore-backed storage
tokenProvider and chatTokenProvider for the other Abrova SDKs
Anonymous, Apple, Google, e-mail, magic link, Telegram
linkApple, linkGoogle, linkPassword keep the user id
This page describes abrova_auth 0.1.1.
Flutter 3.13 or higher. Dart 3.0 or higher. Uses flutter_secure_storage for the session.
Using an AI assistant? Give it this page with Copy page for AI, or all the docs as plain text from /docs/llms.txt. See AI assistants.
Installation#
Packages come from Abrova Center, so pub.dev blocks don't matter. Add the package with the Abrova registry as its hosted source in your pubspec.yaml:
dependencies: abrova_auth: hosted: https://center.abrova.ir/api/packages/abrova/pub version: ^0.1.1
Then install it:
flutter pub get
In Abrova Console, turn on Abrova Auth for your project (Settings › Products) and the sign-in methods you want (Abrova Auth › Sign-in methods).
Initialization#
Call AbrovaAuth.initialize once, before runApp, with your public API key (ab_live_...). It restores the session saved on the device without a network call, so currentUser is ready right away.
import 'package:flutter/material.dart';
import 'package:abrova_auth/abrova_auth.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await AbrovaAuth.initialize('ab_live_your_api_key');
runApp(const MyApp());
}Then use AbrovaAuth.instance anywhere.
| Option | Default | Description |
|---|---|---|
| apiKey | - | Your public API key (ab_live_...). Never the server secret. |
| timeout | 15 seconds | Request timeout. |
| refreshMargin | 2 minutes | Renew the access token when less than this is left. |
| storage | SecureAbrovaAuthStorage() | Where the session is kept. MemoryAbrovaAuthStorage keeps nothing (tests). |
Sign in#
Show only the methods your project has turned on, and sign in only when nobody is signed in yet:
final auth = AbrovaAuth.instance;
final methods = await auth.getMethods();
// methods.anonymous, methods.apple, methods.google, methods.password, methods.magicLink, methods.telegram
if (auth.currentUser == null && methods.anonymous) {
await auth.signInAnonymously();
}
print(auth.currentUser!.id); // ru_...signInAnonymously() returns the current user when an anonymous user is already signed in on this device.
Use the user with other Abrova SDKs#
AbrovaTrace.setUserId(auth.currentUser!.id).Pass the token provider. The other SDK asks it for a token when it needs one; Abrova Auth renews the token before it expires, and callers at the same time share one renewal.
await AbrovaSync.init(AbrovaSyncConfig( apiKey: 'ab_live_your_api_key', tokenProvider: AbrovaAuth.instance.tokenProvider, // null while signed out ));
Abrova Chat takes AbrovaAuth.instance.chatTokenProvider (it throws instead of returning null) and currentUser.id as its user id. After a sign-in or sign-out, call the other SDK's refresh method, for example AbrovaSync.refreshUserToken(). See Use with other Abrova products.
Sign-in methods#
Each method has its own page with the Console setup: Anonymous, Apple, Google, E-mail & password, Magic link, Telegram.
E-mail and password
final result = await auth.signUpWithEmail( email: email, password: password, redirectUrl: 'https://example.com/auth/verify', // optional: sends a verification e-mail ); // result.signedIn is false when the project requires a verified e-mail first. await auth.signInWithEmail(email: email, password: password); await auth.sendPasswordReset(email, redirectUrl: 'https://example.com/auth/reset'); await auth.resetPassword(tokenFromLink, newPassword); // ends every session; does not sign in await auth.verifyEmail(tokenFromLink); await auth.sendEmailVerification(redirectUrl: 'https://example.com/auth/verify');
The links open your redirectUrl with ?token=.... It must start with one of the link addresses in Abrova Auth › Settings.
Magic link
await auth.sendMagicLink(email, redirectUrl: 'myapp://auth/magic'); // When the app is opened with the link: await auth.signInWithMagicLink(Uri.parse(link).queryParameters['token']!);
Telegram
final r = await auth.requestTelegramCode('+989121234567');
if (!r.sent && r.botUrl != null) {
// First time: open r.botUrl so the user starts the bot and shares the number.
}
await auth.verifyTelegramCode('+989121234567', code);Apple and Google
Run the provider's native plugin with the hashed nonce, and give Abrova the raw one:
final raw = AbrovaAuthNonce.generate();
// sign_in_with_apple
final apple = await SignInWithApple.getAppleIDCredential(
scopes: [AppleIDAuthorizationScopes.email, AppleIDAuthorizationScopes.fullName],
nonce: AbrovaAuthNonce.sha256Hex(raw),
);
await auth.signInWithApple(
idToken: apple.identityToken!,
rawNonce: raw,
authorizationCode: apple.authorizationCode, // lets account deletion revoke the Apple sign-in
name: [apple.givenName, apple.familyName].whereType<String>().join(' '),
);
// google_sign_in 7 — use a new nonce for each sign-in
final rawG = AbrovaAuthNonce.generate();
await GoogleSignIn.instance.initialize(
serverClientId: 'xxx.apps.googleusercontent.com',
nonce: AbrovaAuthNonce.sha256Hex(rawG),
);
final account = await GoogleSignIn.instance.authenticate();
await auth.signInWithGoogle(idToken: account.authentication.idToken!, rawNonce: rawG);Add your bundle IDs and Google client IDs in the Console first. Each nonce works once.
Google sign-in needs a connection to Google, and on Android also Google Play services; some users in Iran have neither. Anonymous, e-mail and password, magic link and Telegram code work without Google, so offer at least one of them as well. See Sign in with Google.
Upgrade an anonymous user#
Linking keeps the user id, so the user's data stays theirs:
try {
await auth.linkGoogle(idToken: idToken, rawNonce: raw);
// or linkApple(...), linkPassword(email: ..., password: ...)
} on AbrovaProviderAlreadyLinkedException {
// This Google account already has a user: offer to sign in with it instead.
}See Anonymous users & upgrading.
The current user#
auth.currentUser; // AbrovaAuthUser? — null when signed out
auth.isSignedIn;
auth.authStateChanges.listen((user) {
// Emits the current user first, then on every sign-in, sign-out and profile change.
});
final me = await auth.getMe(); // fresh from the server
final token = await auth.getToken(); // the access token, if you call your own API with it| Field | Type | Description |
|---|---|---|
| id | String | The user id (ru_...) |
| anonymous | bool | True until a method is linked |
| emailVerified | bool | True once the e-mail is proved |
| createdAt | DateTime? | When the user was created |
| providers | List<String> | Linked methods, for example apple, password |
Sign out and delete the account#
await auth.signOut(); // this device; never fails await auth.signOutEverywhere(); // every device await auth.deleteAccount(); // deletes the user, then signs out
Apple requires apps with sign-in to offer account deletion in the app. See Security & data.
Handle errors#
Every call throws AbrovaAuthException:
try {
await auth.signInWithEmail(email: email, password: password);
} on AbrovaAuthException catch (e) {
switch (e.code) {
case AbrovaAuthErrorCode.invalidCredentials:
showError('Wrong e-mail or password');
case AbrovaAuthErrorCode.rateLimited:
showError('Too many attempts. Try again in ${e.retryAfter?.inSeconds ?? 60} s');
case AbrovaAuthErrorCode.networkError:
showError('No connection');
default:
showError('Sign-in failed');
}
}code is one of AbrovaAuthErrorCode; message is an English hint for developers, not for users. isNetworkError and isSessionEnded help with the common cases. A network error never signs the user out; when the session ends (revoked, expired, account disabled), the SDK signs out and authStateChanges emits null. All codes: Error codes.
Troubleshooting#
METHOD_DISABLED or PRODUCT_DISABLED
Turn on Abrova Auth under Settings › Products and the method under Abrova Auth › Sign-in methods. Use getMethods() to show only the methods that are on.
A new anonymous user on every start
Call initialize() before reading currentUser, and sign in only when currentUser is null.
INVALID_ID_TOKEN from Apple or Google
Check that the bundle ID, Services ID or client ID is in the Console, and that you gave the provider AbrovaAuthNonce.sha256Hex(raw) and Abrova the raw value.
Other Abrova SDKs still act as the previous user
They cache their token. Call their refresh method (for example AbrovaSync.refreshUserToken()) from your authStateChanges listener.