Flutter
Sessions that survive restarts, OAuth in the system browser, and app lifecycle handling with vaultbase_flutter.
vaultbase_flutter adds three things to the client for apps. It re-exports
package:vaultbase, so import only this:
import 'package:vaultbase_flutter/vaultbase_flutter.dart';Keeping the session
Section titled “Keeping the session”The default MemoryAuthStore forgets the session when the app exits. Pass a
persistent store instead:
| Store | Where the session goes | Use on |
|---|---|---|
SecureAuthStore() | Keychain (iOS, macOS), Keystore-backed storage (Android), via flutter_secure_storage | Mobile and desktop |
PrefsAuthStore() | Shared preferences: localStorage on the web | The web, or where secure storage isn’t available |
final vb = Vaultbase('https://api.example.com', authStore: kIsWeb ? PrefsAuthStore() : SecureAuthStore());await vb.authStore.ready;The saved session loads when the store is created. Requests and the realtime
connection wait for it, so awaiting ready isn’t required, but it lets the
first frame know whether the user is signed in.
Options:
SecureAuthStore( storage: const FlutterSecureStorage(/* platform options */), key: 'vaultbase_auth', // one key per server, if the app talks to several);PrefsAuthStore(key: 'vaultbase_auth');Any other storage works through AsyncAuthStore (in package:vaultbase):
final store = AsyncAuthStore( read: () => myStorage.read('session'), // Future<String?> write: (value) => value == null ? myStorage.delete('session') : myStorage.write('session', value),);Writes run one at a time, in order. Saved data that doesn’t parse counts as no session.
App lifecycle
Section titled “App lifecycle”final lifecycle = VaultbaseLifecycle(vb);// lifecycle.dispose() if the client goes away before the app does- In the background for 30 seconds (
pauseAfter), the realtime connection closes. Mobile systems drop idle sockets anyway; this makes it predictable. - Back in the foreground, it reconnects and subscribes again. Changes made
meanwhile aren’t replayed, so reload what’s on screen if that matters
(listen to
vb.realtime.statesforopen). - On resume, a session that expires within 5 minutes (
refreshWithin) is refreshed.
OAuth sign-in
Section titled “OAuth sign-in”signInWithOAuth2 runs the whole flow: it asks the server for the
provider’s sign-in URL, opens it in the system’s sign-in browser
(ASWebAuthenticationSession on iOS and macOS, Custom Tabs on Android, a popup
on the web), and exchanges the code it comes back with.
try { final result = await vb.auth.collection('users').signInWithOAuth2('github', redirectUri: 'com.example.app://oauth'); switch (result) { case SignedIn(): break; // stored; authStore.onChange fires case MfaRequired(:final mfaToken): // ask for a code, then loginMfa(mfaToken, code: ...) case MergeRequired(:final mergeToken, :final email): // the email has an account: oauth2MergeConfirm(mergeToken, password: ...) }} on CancelledException { // the user closed the browser}The server keeps the PKCE verifier (pass usePkce: false to turn it off).
Behind it are oauth2Authorize and oauth2ExchangeRedirect, if you need the
steps yourself.
Set-up
Section titled “Set-up”On the server:
- Configure the provider (
oauth2.<provider>.*settings). - Allow the redirect: add
com.example.app://oauth*tooauth2.allowed_redirect_uris(or the provider’s own list). See OAuth2.
At the provider, register the same redirect URI as the callback. Some
providers only accept https callbacks; for those, use an https page that
forwards to the app scheme, and pass that page as redirectUri.
On each platform (details in the
flutter_web_auth_2 set-up):
-
Android: register the scheme for the callback activity in
android/app/src/main/AndroidManifest.xml:<activityandroid:name="com.linusu.flutter_web_auth_2.CallbackActivity"android:exported="true"android:taskAffinity=""><intent-filter android:label="flutter_web_auth_2"><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><data android:scheme="com.example.app" /></intent-filter></activity>An
httpsredirect on Android also needshttpsHostandhttpsPathinoptions: FlutterWebAuth2Options(...). -
iOS and macOS: nothing to register; the sign-in session catches the scheme itself.
-
Web: the redirect must be a page of the app (same origin) that posts the URL back to it: add
web/auth.htmlfrom theflutter_web_auth_2instructions and usehttps://your.app/auth.htmlasredirectUri.
Native Apple and Google sign-in
Section titled “Native Apple and Google sign-in”The platform’s own sign-in sheet (Sign in with Apple, Google sign-in) instead
of a browser tab. The sheet comes from the platform packages, which you add to
the app yourself; it hands back an ID token, and signInWithIdToken sends it
to the server. Needs server 0.28 or later.
Sign in with Apple
Section titled “Sign in with Apple”With sign_in_with_apple:
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
final users = vb.auth.collection('users');final nonce = await users.idTokenNonce('apple');final apple = await SignInWithApple.getAppleIDCredential( scopes: [AppleIDAuthorizationScopes.email, AppleIDAuthorizationScopes.fullName], nonce: nonce,);final result = await users.signInWithIdToken('apple', apple.identityToken!, nonce: nonce, // Apple gives the name only on the first sign-in, never in the token. name: [apple.givenName, apple.familyName].whereType<String>().join(' '));On the server: enable apple and add the app’s bundle ID to
oauth2.apple.native_client_ids (Settings → OAuth2 → Apple → App bundle IDs).
In Xcode, add the Sign in with Apple capability. The nonce is required for
Apple.
With google_sign_in:
import 'package:google_sign_in/google_sign_in.dart';
// Once, at start-up. On Android, serverClientId is the web client ID.await GoogleSignIn.instance.initialize(serverClientId: webClientId);
// On the button:final account = await GoogleSignIn.instance.authenticate();final result = await vb.auth .collection('users') .signInWithIdToken('google', account.authentication.idToken!);google_sign_in takes its nonce once, in initialize, so a single-use
nonce from the server doesn’t fit; leave it out. Each ID token still signs
in only once.
On the server: enable google, and list the iOS and Android OAuth client IDs
in oauth2.google.native_client_ids (Settings → OAuth2 → Google → App client
IDs). The web client ID in client_id is accepted too; Android tokens carry
it when the app passes it as serverClientId.
What comes back
Section titled “What comes back”The same results as the browser flow: SignedIn, MfaRequired, or
MergeRequired when the email belongs to an existing account. A user who
signs in with Google in the browser and with Google in the app is the same
user. Server details: Native sign-in.
Minimum versions
Section titled “Minimum versions”Flutter 3.27 (Dart 3.6), Android minSdk 21 (flutter_secure_storage may ask
for more), iOS 13.