Saltar al contenido principal

Personaliza el FlutterFlow CustomAuthManager usando Logto SDK

FlutterFlow tiene una función incorporada de autenticación personalizada que te permite autenticar usuarios usando tu propio backend. Sin embargo, el flujo de autenticación personalizada incorporado fue diseñado para trabajar con una sola llamada a la API de autenticación. Si estás utilizando un Proveedor de Identidad (IdP) de terceros, la solicitud de autenticación solo se puede realizar utilizando el tipo de concesión Resource Owner Password Credentials, lo cual no se recomienda para uso en producción. Consulta Tipo de concesión ropc obsoleto para más detalles.

Un flujo de autenticación estándar de OpenID Connect (OIDC) involucra múltiples pasos, como autorización, intercambio de tokens y recuperación de información del usuario. Para implementar un flujo de autenticación OIDC estándar con un IdP como Logto, necesitas personalizar la clase CustomAuthManager en FlutterFlow.

Este tutorial te mostrará cómo personalizar la clase CustomAuthManager en FlutterFlow usando Logto Flutter SDK. Puedes aprovechar el SDK de Logto para el flujo de autenticación OIDC estándar mientras mantienes los beneficios del constructor de interfaz de usuario de FlutterFlow.

tip

Prerrequisitos

Habilitar código personalizado en FlutterFlow

Para personalizar la clase CustomAuthManager, necesitas habilitar la función de código personalizado en FlutterFlow. Sigue la guía Gestionar código personalizado en GitHub para sincronizar tu proyecto de FlutterFlow con GitHub.

nota

Gestionar código personalizado en GitHub es una función premium en FlutterFlow. Necesitas actualizar tu FlutterFlow al plan pro para habilitar esta función.

Una vez hecho esto, tendrás tres ramas diferentes en tu repositorio de GitHub de FlutterFlow:

  1. main: La rama principal para el proyecto Flutter. Necesitarás esta rama para desplegar tu proyecto.
  2. flutterflow: La rama donde FlutterFlow sincronizará los cambios desde el editor de FlutterFlow.
  3. develop: La rama donde puedes modificar tu código personalizado.

Crea tu interfaz de usuario en FlutterFlow

Primero, crea tu interfaz de usuario en FlutterFlow. Puedes seguir la documentación de FlutterFlow para crear tu interfaz de usuario según tus requisitos. Para este tutorial, como requisito mínimo, crearemos dos páginas:

  1. Una página de inicio simple con un botón de inicio de sesión.
  2. Una página de perfil de usuario para mostrar la información del usuario y un botón de cierre de sesión.

Ve a la página App Settings -> Authentication y habilita la autenticación personalizada. Esto creará una clase CustomAuthManager en tu proyecto de FlutterFlow.

Autenticación personalizada de FlutterFlow

Una vez que tengas la interfaz de usuario lista, navega a la página integrations -> GitHub y haz clic en el botón Push to Repository para enviar los cambios a la rama flutterflow.

Envío a GitHub de FlutterFlow

Personaliza el CustomAuthManager

Cambia a la rama develop en tu repositorio de GitHub y fusiona los últimos cambios de la rama flutterflow. Incluyendo tus páginas de interfaz de usuario y la clase CustomAuthManager preconstruida.

Instala la dependencia Logto SDK

Agrega la dependencia Logto SDK a tu proyecto.

  flutter pub add logto_dart_sdk
nota

Paquete Http opcional:

El cliente Logto requiere un cliente http para realizar llamadas a la API. Puedes usar el paquete http o cualquier otro paquete de cliente http de tu elección.

  flutter pub add http

Actualiza el UserProvider

Agrega la clase OpenIdClaims a la clase CustomAuthUserProvider para almacenar la información del usuario.

La clase OpenIdClaims es parte del SDK de Logto, que proporcionará los reclamos id_token para el usuario autenticado.

// 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;
}

/// Genera un flujo del usuario autenticado.
BehaviorSubject<FlutterFlowAuthAuthUser> flutterFlowAuthAuthUserSubject =
BehaviorSubject.seeded(FlutterFlowAuthAuthUser(loggedIn: false));
Stream<FlutterFlowAuthAuthUser> flutterFlowAuthAuthUserStream() =>
flutterFlowAuthAuthUserSubject
.asBroadcastStream()
.map((user) => currentUser = user);

Inicializa el cliente logto en CustomAuthManager

Inicializa el cliente Logto en la clase CustomAuthManager.

// 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;

El método initialize inicializará el cliente Logto y actualizará el flujo de usuario actual con el estado de autenticación del usuario persistido en el almacenamiento local.

El SDK de Logto utiliza el paquete flutter_secure_storage para almacenar de manera segura la información de autenticación del usuario.

Implementa el método de inicio de sesión

Llama al método LogtoClient.signIn para iniciar un flujo de autenticación OIDC estándar. La página de inicio de sesión de Logto se abrirá en una vista web usando 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 manejará los pasos de autorización, intercambio de tokens y recuperación de información del usuario. Una vez que el usuario esté autenticado, los idTokenClaims se almacenarán en el almacenamiento local. Recupera los idTokenClaims del LogtoClient y actualiza el flujo de usuario actual.

Implementa el método de cierre de sesión

// lib/auth/custom_auth/custom_auth_manager.dart

Future signOut() async {
await logtoClient.signOut();

flutterFlowAuthAuthUserSubject.add(
FlutterFlowAuthAuthUser(loggedIn: false),
);
}

Actualiza los métodos utilitarios de autenticación

  • Agrega el getter authManager para acceder a la instancia de CustomAuthManager.
  • Agrega el getter currentUserUid para obtener el uid del usuario actual.
  • Agrega el getter currentUserData para obtener los datos del usuario actual.
  • Agrega el getter logtoClient para acceder a la instancia del cliente Logto.
// 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;

Integra la autenticación personalizada en tu interfaz de usuario

Página de inicio

Llama al método authManager.signIn para iniciar el flujo de autenticación cuando el usuario haga clic en el botón de inicio de sesión.

redirectUri es la URL de callback que se utilizará para capturar el callback de autorización desde la página de inicio de sesión de Logto. Consulta el Flutter SDK para más detalles sobre el 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',
// ...
)

Página de perfil de usuario

Usa los getters utilitarios de autenticación para acceder a los datos del usuario actual y a la instancia del cliente Logto.

// 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}',
),
]

Implementa el método de cierre de sesión cuando el usuario haga clic en el botón de cierre de sesión.

// lib/pages/user/user_widget.dart

FFButtonWidget(
onPressed: () async {
await authManager.signOut();

context.replaceNamed('HomePage');
},
text: 'Sign Out',
// ...
)

Lecturas adicionales

El SDK de Logto proporciona más métodos para interactuar con la API de Logto. Puedes personalizar aún más la clase CustomAuthManager para implementar más funciones.

Solución de problemas de dependencias

flutter_secure_storage

Usamos flutter_secure_storage para implementar el almacenamiento seguro persistente de tokens multiplataforma. Bajo el capó:

  • Se utiliza Keychain para iOS
  • Se utiliza cifrado AES para Android.

Configurar la versión de Android:

En [project]/android/app/build.gradle establece minSdkVersion a >= 18.

  android {
...

defaultConfig {
...
minSdkVersion 18
...
}
}

Desactivar la copia de seguridad automática:

nota

Por defecto, Android realiza copias de seguridad de los datos en Google Drive. Esto puede causar la excepción java.security.InvalidKeyException:Failed to unwrap key.

Para evitar esto, puedes desactivar la copia de seguridad automática para tu aplicación o excluir sharedprefs de FlutterSecureStorage.

  1. Para desactivar la copia de seguridad automática, ve a tu archivo de manifiesto de la aplicación y establece el valor booleano android:allowBackup:

    <manifest ... >
    ...
    <application
    android:allowBackup="false"
    android:fullBackupContent="false"
    ...
    >
    ...
    </application>
    </manifest>

  2. Excluir sharedprefs de FlutterSecureStorage.

    Si necesitas habilitar el android:fullBackupContent para tu aplicación. Configura una regla de copia de seguridad para excluir las preferencias utilizadas por el 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>

    Por favor, consulta flutter_secure_storage para más detalles.

flutter_web_auth

flutter_web_auth se utiliza detrás del SDK de flutter de Logto. Confiamos en su interfaz de interacción basada en webview para abrir las páginas de autorización de Logto.

nota

Este plugin utiliza ASWebAuthenticationSession en iOS 12+ y macOS 10.15+, SFAuthenticationSession en iOS 11, Chrome Custom Tabs en Android y abre una nueva ventana en Web. Puedes construirlo con iOS 8+, pero actualmente solo es compatible con iOS 11 o superior.

Registrar la URL de callback en Android

Para capturar la URL de callback desde la página web de inicio de sesión de Logto, necesitarás registrar tu redirectUri de inicio de sesión en el 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>