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 (Check out the ⚡ Get started guide to create one if you don't have).
- 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 theFlutterFlow
will 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.
OpenIdClaims
class is a part of the Logto SDK, which will provide theid_token
claims 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
authManager
getter to access theCustomAuthManager
instance. - Add the
currentUserUid
getter to get the current user uid. - Add the
currentUserData
getter to get the current user data. - Add the
logtoClient
getter 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.
redirectUri
is 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>