Learn how to implement custom authentication in your Flutter application using Logto Flutter SDK.
Introduction
FlutterFlow is a low-code platform that allows you to build Flutter applications visually. It provides a drag-and-drop interface to design your app's UI and generates the corresponding Flutter code. According to the official documentation, it provides three different authentication integration options:
- Through the build-in Firebase authentication
- Through the build-in Supabase authentication
- Custom authentication
For the first two, FlutterFlow provides a seamless integration with Firebase and Supabase. You will need to set up your Firebase or Supabase project and configure the authentication settings in FlutterFlow. However, if you want to use a different authentication provider, you will need to implement the authentication logic yourself.
As for the custom authentication, FlutterFlow provides a way to integrate with any authentication provider relying on a single custom authentication API.
However, a direct user credential exchange between the client and the authentication server is not recommended by modern security standards. Instead, you should use a secure authentication flow such as OAuth 2.0 or OpenID Connect (OIDC) to authenticate users. For modern OAuth 2.0 or OIDC based Identity Providers (IdP) such as Auth0, Okta, and Logto, a resource owner password credentials (ROPC) grant type is not recommended or prohibited due to security reasons. See Deprecated ropc grant type for more details.
A standard OAuth 2.0 or OIDC authentication flow involves multiple steps and redirects between the client application, the authorization server, and the user's browser. In this post, we will show you how to customize the FluterFlow's CustomAuthManager
class using Logto Flutter SDK to implement a secure authentication flow in your FlutterFlow application.
Prerequisites
- A Logto Cloud account or a self-hosted Logto instance. (Checkout the β‘ Get started guide to create a Logto instance)
- A Flutter application created using FlutterFlow.
- Register a flutter application in your Logto console.
- A GitHub repository to manage your custom code in FlutterFlow.
- Checkout our Flutter SDK's integration guide.
- Logto Flutter SDK package is available on pub.dev and Logto github repository.
- The SDK is currently only suitable for Android and iOS platforms.
- Manage custom code in GitHub is a premium feature in FlutterFlow. You need to upgrade your FlutterFlow to
Pro
plan to enable this feature.
Step 1: Enable manage custom code in FlutterFlow
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 link and sync your FlutterFlow project with GitHub.
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 FlutterFlow will sync the changes from the UI editor to your codebase. -
develop
: The branch where you can modify your custom code.
Step 2: Design and create your custom UI flow in FlutterFlow
Build your pages
Create your UI in FlutterFlow. You can follow the FlutterFlow documentation to create your UI based on your requirements. In this tutorial, for the minimum requirement, we will assume you have two pages:
A simple
HomePage
with a sign-in button. (No sign-in form is needed, user authentication flow is handled at the Logto side. Please check customize sie guide for more details.)
A user profile page to display user information and sign-out button.
Enable custom authentication in FlutterFlow
Got to the App Settings
- Authentication
page and enable the custom authentication. This will create a CustomAuthManager
class and related files in your FlutterFlow project.
Step 3: Sync your FlutterFlow project with GitHub
After you have created your custom UI and enabled the custom authentication in FlutterFlow, you need to sync your project with GitHub. Go to the integrations
- GitHub
page and and click on the Push to Repository
Step 4: Customize the CustomAuthManager
code
Switch to the develop
branch in your GitHub repository and merge the latest changes from the flutterflow
branch. This will sync all the UI changes to your develop
branch including your page widgets, 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 thehttp
package or any other http client package of your choice.flutter pub add http
Update the UserProvider class
UserProvider
class is responsible for managing the user authentication state. We need to customize the properties to store the user authentication information provided by the Logto SDK.
Add a idToken
property with type OpenIdClaims
to store the id_token
claims for the authenticated user.
OpenIdClaims
class is defined in Logto SDK, which will provide theOIDC
standardid_token
claims from a 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);
Customize the CustomAuthManager class and initialize Logto client
// 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;
// Logto configuration
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 init a Logto client instance 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 data securely. Once the user is authenticated, the
id_token
claims will be stored in the local storage.
Implement the sign-in method using Logto client
Call the LogtoClient.signIn
method will initiate a standard OIDC
authentication flow. The Logto sign-in page will be opened in a webview. The webview based authentication flow is powered by 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),
);
}
The signOut
method will clear the user authentication data stored in the local storage and update the current user stream.
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;
Step 5: Update the sign-in and sign-out buttons 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 implement sign-in 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 will be redirected to the user
page after successful authentication.
User Profile Page
Use the auth util getters to access the current user data and the Logto client instance.
For example, to display the user information using multiple Text
widgets:
// 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}',
),
]
Trigger the sign-out method when the user clicks on the sign-out button and redirect the user back to the home page.
// lib/pages/user/user_widget.dart
FFButtonWidget(
onPressed: () async {
await authManager.signOut();
context.replaceNamed('HomePage');
},
text: 'Sign Out',
// ...
)
Testing
Run your FlutterFlow application on an emulator. Click on the sign-in button on the home page to initiate the authentication flow. The Logto sign-in page will be opened in a webview. After successful authentication, the user will be redirected to the user profile page. The user information will be displayed on the user profile page. Click on the sign-out button to sign out the user and redirect the user back to the home page.
Don't forget to merge the develop
branch back to the main
branch and push the changes to the GitHub repository.
FlutterFlow github push is a one-way sync from FlutterFlow to GitHub. Any changes made under the fluterflow branch will be overwritten by the
FlutterFlow
UI editor when you push the changes to the GitHub repository. Make sure to use thedevelop
branch to manage your custom code and merge the changes back to themain
branch for deployment.
Further Readings
Logto SDK provides more methods to interact with the Logto API. You may further customize the CustomAuthManager
class to implement more features using the Logto SDK.
Top comments (0)