Custom FlutterFlow authentication using Logto
Tags: webdev, programming, opensource, flutter
Published: 2024-05-30 08:07:08

Learn how to implement custom authentication in your Flutter application using Logto Flutter SDK.

---
# Introduction

[FlutterFlow](https://flutterflow.io/) 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](https://docs.flutterflow.io/settings-and-integrations/app-settings/authentication), it provides three different authentication integration options:

1. Through the build-in Firebase authentication
2. Through the build-in Supabase authentication
3. 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](https://docs.flutterflow.io/data-and-backend/custom-authentication), FlutterFlow provides a way to integrate with any authentication provider relying on a single custom [authentication API](https://docs.flutterflow.io/data-and-backend/custom-authentication#id-3.-authenticate-users).

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](https://blog.logto.io/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](https://docs.logto.io/quick-starts/flutter/) to implement a secure authentication flow in your FlutterFlow application.

# Prerequisites

- A [Logto Cloud](https://auth.logto.io/sign-in) account or a self-hosted Logto instance. (Checkout the [⚡ Get started](https://docs.logto.io/docs/tutorials/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](https://docs.logto.io/quick-starts/flutter/).

>  - Logto Flutter SDK package is available on [pub.dev](https://pub.dev/packages/logto_dart_sdk) and Logto [github repository](https://pub.dev/packages/logto_dart_sdk).
>  - 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](https://docs.flutterflow.io/customizing-your-app/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:

1. `main`: The main branch for the flutter project. You will need this branch to deploy your project.
2. `flutterflow`: The branch where the FlutterFlow will sync the changes from the UI editor to your codebase.
3. `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](https://docs.flutterflow.io/) to create your UI based on your requirements. In this tutorial, for the minimum requirement, we will assume you have two pages:

1. 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](https://docs.logto.io/docs/recipes/customize-sie/) guide for more details.)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sra6tewymjt8v3hbkxid.png)


2. A user profile page to display user information and sign-out button.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4kqdf45ftjfelwlxdox.png)

### 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.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gtchaywfnx6ihi61t3d5.png)

# 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`
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pk8yke8l14093537lmkn.png)



# 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 the `http` 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 the `OIDC` standard `id_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](https://pub.dev/packages/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](https://pub.dev/packages/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 the `CustomAuthManager` 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](https://docs.logto.io/quick-starts/flutter/#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 the `develop` branch to manage your custom code and merge the changes back to the `main` 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.

- [User info endpoint](https://docs.logto.io/quick-starts/flutter/#get-user-information)
- [Get resource access token](https://docs.logto.io/quick-starts/flutter/#api-resources)
- [Get organization access token](https://docs.logto.io/quick-starts/flutter/#organization)


{% cta https://logto.io/?ref=dev %} Try Logto Cloud for free {% endcta %}