Registro e inicio de sesión en tu App Flutter con el API REST de Firebase Auth

Hoy en día, las aplicaciones necesitan ser personalizadas y adaptadas a las preferencias de los usuarios para lograr una experiencia encantadora cuando los usuarios interactúan con ellas. La información básica de nuestros usuarios podría obtenerse a través de diferentes métodos de autenticación. Como probablemente sabes, Firebase Auth es uno de ellos y en él nos enfocaremos en este post.

Nota: Este post es una traducción del artículo “Signup and Login into your Flutter app using Firebase Auth API REST” publicado por el usuario juandgaines en el Huawei Developer Forum.

Hasta ahora, probablemente te estés preguntando por qué estamos mencionando Firebase Auth en una publicación de Huawei. Bueno, quizá esta es la razón por la que llegas aquí: Firebase Auth para Android no funciona correctamente en los teléfonos HMS.

Propuesta de solución

Implementar AGC Auth Service usando method channels

Esta es la opción más recomendable en lo que respecta a los teléfonos HMS, de hecho, Auth Service también es compatible con dispositivos de otros fabricantes y hasta con IOS.

Utilizar Firebase Auth API REST en lugar de la biblioteca Flutter.

Si deseas mantener la compatibilidad con Firebase mientras llega un plugin oficial de AGC Auth Service para Flutter. Puedes usar las APIs REST de Firebase para que tu app siga funcionando en HMS y pueda seguir proporcionando un flujo de operación normal a los usuarios HUAWEI. No recomiendo esta solución a largo plazo con las aplicaciones, pero es un bypass y una solución temporal para hacer los teléfonos HMS compatibles con el flujo de autenticación de Firebase.

La implementación del segundo método será explicada en este post.

Configuración

Visita la documentación de Firebase Auth API REST. El inicio de sesión con nombre de usuario y contraseña es el escenario que queremos desarrollar en este caso. Por lo tanto, se necesitarán dos APIs para la implementación: Registraro e inicio de sesión con contraseña.

Obtén el API key de tu proyecto desde la consola de Firebase.

Asegúrate de contar con alguna librería HTTP en tu proyecto de Flutter.

Desarrollo

Desarrollar clases para la recibir la carga útil en la respuesta de la API de Firebase de acuerdo a la documentación. Puedes utilizar una herramienta de conversión en línea para simplificar el trabajo.

class LogInUserResponse {
String kind;
String localId;
String email;
String displayName;
String idToken;
bool registered;
String refreshToken;
String expiresIn;
LogInUserResponse(
{this.kind,
this.localId,
this.email,
this.displayName,
this.idToken,
this.registered,
this.refreshToken,
this.expiresIn});
LogInUserResponse.fromJson(Map<String, dynamic> json) {
kind = json['kind'];
localId = json['localId'];
email = json['email'];
displayName = json['displayName'];
idToken = json['idToken'];
registered = json['registered'];
refreshToken = json['refreshToken'];
expiresIn = json['expiresIn'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['kind'] = this.kind;
data['localId'] = this.localId;
data['email'] = this.email;
data['displayName'] = this.displayName;
data['idToken'] = this.idToken;
data['registered'] = this.registered;
data['refreshToken'] = this.refreshToken;
data['expiresIn'] = this.expiresIn;
return data;
}
}
class ErrorResponse {
Error error;
ErrorResponse({this.error});ErrorResponse.fromJson(Map<String, dynamic> json) {
error = json['error'] != null ? new Error.fromJson(json['error']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.error != null) {
data['error'] = this.error.toJson();
}
return data;
}
}
class Error {
int code;
String message;
List<Errors> errors;
Error({this.code, this.message, this.errors});Error.fromJson(Map<String, dynamic> json) {
code = json['code'];
message = json['message'];
if (json['errors'] != null) {
errors = new List<Errors>();
json['errors'].forEach((v) {
errors.add(new Errors.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['code'] = this.code;
data['message'] = this.message;
if (this.errors != null) {
data['errors'] = this.errors.map((v) => v.toJson()).toList();
}
return data;
}
}
class Errors {
String message;
String domain;
String reason;
Errors({this.message, this.domain, this.reason});Errors.fromJson(Map<String, dynamic> json) {
message = json['message'];
domain = json['domain'];
reason = json['reason'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['message'] = this.message;
data['domain'] = this.domain;
data['reason'] = this.reason;
return data;
}
}

Implementa la siguiente clase para manejar las solicitudes HTTP. Reemplaza todas las apariciones de la etiqueta YOUR_API_KEY_HERE con tu API KEY de Firebase.

class AuthFirebaseProvider with ChangeNotifier {
Map<String, String> _getCommonHeaders() {
return <String, String>{
HttpHeaders.contentTypeHeader: 'application/json; charset=UTF-8'
};
}
Future<CustomeResponse> signUpUser(String email, String password) async {
var queryParameters = {
'key': 'YOUR_API_KEY_HERE',
};
var uri = Uri.https("identitytoolkit.googleapis.com", "/v1/accounts:signUp",
queryParameters);
final http.Response response = await http.post(uri,
headers: _getCommonHeaders(),
body: jsonEncode(<String, dynamic>{
"email": email,
"password": password,
"returnSecureToken": true
}));
if (response.statusCode == 200) {
return new CustomeResponse(null, response);
} else {
final jsonV = json.decode(response.body);
final error = ErrorResponse.fromJson(jsonV);
return new CustomeResponse(error, null);
}
}
Future<CustomeResponse> loginUser(String email, String password) async {
var queryParameters = {
'key': 'YOUR_API_KEY_HERE',
};
var uri = Uri.https("identitytoolkit.googleapis.com",
"/v1/accounts:signInWithPassword", queryParameters);
final http.Response response = await http.post(uri,
headers: _getCommonHeaders(),
body: jsonEncode(<String, dynamic>{
"email": email,
"password": password,
"returnSecureToken": true
}));
if (response.statusCode == 200) {
return new CustomeResponse(null, response.body);
} else {
final jsonV = json.decode(response.body);
final error = ErrorResponse.fromJson(jsonV);
return new CustomeResponse(error, null);
}
}
}

Invoca los métodos de la clase según lo requieras, este es un ejemplo con un Stateful Widget:

void _signUpOrLogin(
BuildContext context, String email, String password) async {
//authFirebaseProvider?.signUpUser("jdgpdtse90@gmail.com", "huawei123");
final customeResponse =
await authFirebaseProvider?.loginUser(email, password);
if (customeResponse != null && customeResponse.object != null) {
final jsonV = json.decode(customeResponse.object);
final loginUser = LogInUserResponse.fromJson(jsonV);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MapRoute()),
);
} else {
ErrorResponse er = customeResponse.error;
final snackBar = SnackBar(content: Text('${er.error.message}'));
Scaffold.of(context).showSnackBar(snackBar);
}
}

Realiza los cambios que necesites en la lógica del código. Por ejemplo implementar los mismos casos de error utilizados por la librería. No es una solución perfecta, pero ayudará por el momento.

Resultado

El resultado después de usar el API REST como alternativa será satisfactorio. A continuación te muestro un usuario de pruebas creado a partir de esta solución:

En conclusión, este método te ayudará a resolver por el momento las necesidades de la compatibilidad con dispositivos HMS. Es recomendable utilizar AGC Auth Service tan pronto como un plugin oficial esté disponible.

--

--