Customize the FlutterFlow CustomAuthManager using Logto SDK
FlutterFlow has a built-in custom authentication feature that allows you to authenticate users using your own backend. However, the build-in custom authentication flow was designed to work with a single authentication API call. If you are using a third-party Identity Provider (IdP) the authentication request can only be done using the Resource Owner Password Credentials grant type, which is not recommended for production use. See Deprecated ropc grant type for more details.
A standard OpenID Connect (OIDC) authentication flow involves multiple steps, such as authorization, token exchange, and user information retrieval. To implement a standard OIDC authentication flow with a IdP like Logto, you need to customize the CustomAuthManager class in FlutterFlow.
This tutorial will show you how to customize the CustomAuthManager class in FlutterFlow using Logto Flutter SDK. You may take advantage of the Logto SDK for the standard OIDC authentication flow while keeping the benefits of the FlutterFlow UI builder.
- Logto SDK package is available on pub.dev and Logto github repository.
- The SDK is currently only suitable for Android and iOS platforms.
Prerequisites
- A Logto Cloud account or a self-hosted Logto.
- Create a Logto Flutter application.
- A FlutterFlow project.
Enable FlutterFlow custom code
In order to customize the CustomAuthManager class, you need to enable the custom code feature in FlutterFlow. Following the Manage Custom Code In GitHub guide to sync your FlutterFlow project with GitHub.
Manage custom code in GitHub is a premium feature in FlutterFlow. You need to upgrade your FlutterFlow to pro plan to enable this feature.
Once it is done, you will have three different branches under your GitHub FlutterFlow repository:
- main: The main branch for the flutter project. You will need this branch to deploy your project.
- flutterflow: The branch where the- FlutterFlowwill sync the changes from the FlutterFlow editor.
- develop: The branch where you can modify your custom code.
Create your UI in FlutterFlow
First, create your UI in FlutterFlow. You can follow the FlutterFlow documentation to create your UI based on your requirements. For this tutorial, for the minimum requirement, we will create two pages:
- A simple home page with a login button.
- A user profile page to display user information and logout button.
Got to the App Settings -> Authentication page and enable the custom authentication. This will create a CustomAuthManager class in your FlutterFlow project.

Once you have the UI ready, navigate to the integrations -> GitHub page and click on the Push to Repository button to push the changes to the flutterflow branch.

Customize the CustomAuthManager
Switch to the develop branch in your GitHub repository and merge the latest changes from the flutterflow branch. Including your UI pages, and the pre built CustomAuthManager class.
Install Logto SDK dependency
Add the Logto SDK dependency to your project.
  flutter pub add logto_dart_sdk
Optional Http package:
Logto client requires a http client to make API calls. You can use the http package or any other http client package of your choice.
  flutter pub add http
Update the UserProvider
Add the OpenIdClaims class to the CustomAuthUserProvider class to store the user information.
OpenIdClaimsclass is a part of the Logto SDK, which will provide theid_tokenclaims for the authenticated user.
// lib/auth/custom_auth/custom_auth_user_provider.dart
import 'package:logto_dart_sdk/src/modules/id_token.dart';
import 'package:rxdart/rxdart.dart';
import 'custom_auth_manager.dart';
class FlutterFlowAuthAuthUser {
  FlutterFlowAuthAuthUser({required this.loggedIn, this.uid, this.idToken});
  bool loggedIn;
  String? uid;
  OpenIdClaims? idToken;
}
/// Generates a stream of the authenticated user.
BehaviorSubject<FlutterFlowAuthAuthUser> flutterFlowAuthAuthUserSubject =
    BehaviorSubject.seeded(FlutterFlowAuthAuthUser(loggedIn: false));
Stream<FlutterFlowAuthAuthUser> flutterFlowAuthAuthUserStream() =>
    flutterFlowAuthAuthUserSubject
        .asBroadcastStream()
        .map((user) => currentUser = user);
Init the logto client in CustomAuthManager
Initialize the Logto client in the CustomAuthManager class.
// lib/auth/custom_auth/custom_auth_manager.dart
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:logto_dart_sdk/logto_client.dart';
import 'package:logto_dart_sdk/src/modules/id_token.dart';
import 'custom_auth_user_provider.dart';
export 'custom_auth_manager.dart';
class CustomAuthManager {
  late LogtoClient logtoClient;
  final logtoConfig = const LogtoConfig(
      appId: '<YOUR-APP-ID>',
      endpoint: '<YOUR-LOGTO-ENDPOINT>');
  // ...
  FlutterFlowAuthAuthUser? _updateCurrentUser(
      {bool loggedIn = false, String? uid, OpenIdClaims? idToken}) {
    // Update the current user stream.
    final updatedUser = FlutterFlowAuthAuthUser(
      loggedIn: loggedIn,
      uid: uid,
      idToken: idToken,
    );
    flutterFlowAuthAuthUserSubject.add(updatedUser);
    return updatedUser;
  }
  Future initialize() async {
    logtoClient = LogtoClient(config: logtoConfig, httpClient: http.Client());
    late OpenIdClaims? idToken;
    try {
      idToken = await logtoClient.idTokenClaims;
    } catch (e) {
      if (kDebugMode) {
        print('Error initializing auth: $e');
      }
    }
    _updateCurrentUser(
        loggedIn: idToken != null, uid: idToken?.subject, idToken: idToken);
  }
}
FlutterFlowAuthAuthUser? currentUser;
bool get loggedIn => currentUser?.loggedIn ?? false;
The initialize method will initialize the Logto client and update the current user stream with the user authentication status persisted in the local storage.
Logto SDK uses the flutter_secure_storage package to store the user authentication information securely.
Implement the Sign-in method
Call the LogtoClient.signIn method will initiate a standard OIDC authentication flow. The Logto Sign-In page will be opened in a webview using flutter_web_auth.
// lib/auth/custom_auth/custom_auth_manager.dart
Future<FlutterFlowAuthAuthUser?> signIn(
    String redirectUri,
  ) async {
    await logtoClient.signIn(redirectUri);
    var idTokenClaims = await logtoClient.idTokenClaims;
    return _updateCurrentUser(
      loggedIn: idTokenClaims != null,
      uid: idTokenClaims?.subject,
      idToken: idTokenClaims,
    );
  }
LogtoClient will handle the authorization, token exchange, and user information retrieval steps. Once the user is authenticated, the idTokenClaims will be stored in the local storage.
Retrieve the idTokenClaims from the LogtoClient and update the current user stream.
Implement the Sign-out method
// lib/auth/custom_auth/custom_auth_manager.dart
Future signOut() async {
    await logtoClient.signOut();
    flutterFlowAuthAuthUserSubject.add(
      FlutterFlowAuthAuthUser(loggedIn: false),
    );
  }
Update the auth util methods
- Add the authManagergetter to access theCustomAuthManagerinstance.
- Add the currentUserUidgetter to get the current user uid.
- Add the currentUserDatagetter to get the current user data.
- Add the logtoClientgetter to access the Logto client instance.
// lib/auth/custom_auth/auth_util.dart
import 'package:logto_dart_sdk/logto_client.dart';
import 'package:logto_dart_sdk/src/modules/id_token.dart';
import 'custom_auth_manager.dart';
export 'custom_auth_manager.dart';
final _authManager = CustomAuthManager();
CustomAuthManager get authManager => _authManager;
String get currentUserUid => currentUser?.uid ?? '';
OpenIdClaims? get currentUserData => currentUser?.idToken;
LogtoClient get logtoClient => _authManager.logtoClient;
Integrate the custom authentication in your UI
Home Page
Call the authManager.signIn method to initiate the authentication flow when the user clicks on the sign-in button.
redirectUriis the callback URL that will be used to capture the authorization callback from the Logto sign-in page. See the Flutter SDK for more details on the redirectUri.
// lib/pages/home_page/home_page_widget.dart
final redirectUri = 'io.logto://callback';
// ...
FFButtonWidget(
  onPressed: () async {
    GoRouter.of(context).prepareAuthEvent();
    await authManager.signIn(redirectUri);
    context.replaceNamed('user');
  },
  text: 'Sign In',
  // ...
)
User Profile Page
Use the auth util getters to access the current user data and the Logto client instance.
// lib/pages/user/user_widget.dart
import '/auth/custom_auth/auth_util.dart';
// ...
children: [
  Text(
    'User ID: $currentUserUid',
  ),
  Text(
    'Display Name: ${currentUserData?.name}',
  ),
  Text(
    'Username: ${currentUserData?.username}',
  ),
  Text(
    'Email: ${currentUserData?.emailVerified ?? currentUserData?.email}',
  ),
]
Implement the sign-out method when the user clicks on the sign-out button.
// lib/pages/user/user_widget.dart
FFButtonWidget(
  onPressed: () async {
    await authManager.signOut();
    context.replaceNamed('HomePage');
  },
  text: 'Sign Out',
  // ...
)
Further readings
Logto SDK provides more methods to interact with the Logto API. You may further customize the CustomAuthManager class to implement more features.
Dependency troubleshooting
flutter_secure_storage
We use flutter_secure_storage to implement the cross-platform persistent secure token storage. Under the hood:
- Keychain is used for iOS
- AES encryption is used for Android.
Config Android version:
In [project]/android/app/build.gradle set minSdkVersion to >= 18.
  android {
      ...
      defaultConfig {
          ...
          minSdkVersion 18
          ...
      }
  }
Disable autobackup:
By default Android backups data on Google Drive. It can cause exception java.security.InvalidKeyException:Failed to unwrap key.
To avoid this, you can disable auto backup for your app or exclude sharedprefs from the FlutterSecureStorage.
- 
To disable auto backup, go to your app manifest file and set the boolean value android:allowBackup: <manifest ... >
 ...
 <application
 android:allowBackup="false"
 android:fullBackupContent="false"
 ...
 >
 ...
 </application>
 </manifest>
- 
Exclude sharedprefs from FlutterSecureStorage. If you need to enable the android:fullBackupContent for your app. Set up a backup rule to exclude the prefs used by the plugin: <application ...
 android:fullBackupContent="@xml/backup_rules">
 </application><?xml version="1.0" encoding="utf-8"?>
 <full-backup-content>
 <exclude domain="sharedpref" path="FlutterSecureStorage"/>
 </full-backup-content>Please check flutter_secure_storage for more details. 
flutter_web_auth
flutter_web_auth is used behind Logto's flutter SDK. We rely on its webview-based interaction interface to open Logto's authorization pages.
This plugin uses ASWebAuthenticationSession on iOS 12+ and macOS 10.15+, SFAuthenticationSession on iOS 11, Chrome Custom Tabs on Android and opens a new window on Web. You can build it with iOS 8+, but it is currently only supported by iOS 11 or higher.
Register the callback url on Android
In order to capture the callback url from Logto's sign-in web page, you will need to register your sign-in redirectUri to the AndroidManifest.xml.
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" android:exported="true">
    <intent-filter android:label="flutter_web_auth">
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="io.logto"/>
    </intent-filter>
</activity>