Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter - Get Firebase custom claims while writing Firebase user to own user instance

I am trying to implement the example given at How do I access custom claims? to my existing code.

I have a Stream which listens to auth changes and updates my own user object with the responded Firebase user. When I store my user object, I would like to get the custom claims of that user as well.

The problem is in _userFromFirebaseUser.

It says "The await expression can only be used in an async function. Try marking the function body with either 'async' or 'async*'."

But when I do so, the error is hops to my stream where it then says "The argument type 'Future Function(User)' can't be assigned to the parameter type 'User Function(User)'." for "_userFromFirebaseUser" in

// auth change user stream
  Stream<local.User> get user {
    return _auth.authStateChanges().map(_userFromFirebaseUser);
  }

Here is my complete authentication class:

import 'package:<my-pckg>/models/user.dart' as local;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:<my-pckg>/services/database.dart';
//import 'package:shared_preferences/shared_preferences.dart';

class AuthService {
  final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;

  // create user obj based on firebase user
  local.User _userFromFirebaseUser(auth.User user) {
    
    final isAdmin = (await _currentUserClaims)['admin'] == true;
    
    return user != null
        ? local.User(
            uid: user.uid,
            email: user.email,
            displayName: user.displayName,
            isAdmin: isAdmin)
        : null;
  }

  // auth change user stream
  Stream<local.User> get user {
    return _auth.authStateChanges().map(_userFromFirebaseUser);
  }

  // sign in anon
  Future signInAnon() async {
    try {
      auth.UserCredential result = await _auth.signInAnonymously();
      auth.User user = result.user;
      return _userFromFirebaseUser(user);
    } catch (e) {
      print(e.toString());
      return null;
    }
  }

  // sign in with email and password
  Future signInWithEmailAndPassword(String email, String password) async {
    try {
      auth.UserCredential result = await _auth.signInWithEmailAndPassword(
          email: email, password: password);
      auth.User user = result.user;
      print('Successfully logged in, User UID: ${user.uid}');
      return user;
    } catch (error) {
      print(error.toString());
      return null;
    }
  }

  // register with email and password
  Future registerWithEmailAndPassword(String email, String password) async {
    try {
      auth.UserCredential result = await _auth.createUserWithEmailAndPassword(
          email: email, password: password);
      auth.User user = result.user;
      // create a new document for the user with the uid
      await DatabaseService(uid: user.uid).updateUserData(null);
      print('Successfully registered, User UID: ${user.uid}');
      return _userFromFirebaseUser(user);
    } catch (error) {
      print(error.toString());
      return null;
    }
  }

  // sign out
  Future signOut() async {
    try {
      print('User signed out');
      return await _auth.signOut();
    } catch (error) {
      print(error.toString());
      return null;
    }
  }

  Future<Map<dynamic, dynamic>> get _currentUserClaims async {
    final user = _auth.currentUser;

    // If refresh is set to true, a refresh of the id token is forced.
    final idTokenResult = await user.getIdTokenResult(true);

    return idTokenResult.claims;
  }
}

Am I heading into the wrong direction? Is there anything obvious, that I simply do not consider?

Thanks for your help!

like image 529
Ocrimops Avatar asked Nov 15 '25 22:11

Ocrimops


1 Answers

For those, heading into the same problem, I found the solution after further research:

You will have to change the .map to .asyncMap.

Here is the code, which works for me:

import 'package:<my-pckg>/models/user.dart' as local;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:<my-pckg>/services/database.dart';
//import 'package:shared_preferences/shared_preferences.dart';

class AuthService {
  final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;

  // create user obj based on firebase user
  Future<local.User> _userFromFirebaseUser(auth.User user) async {
    final isAdmin = (await _userClaims)['admin'] == true;

    return user != null
        ? local.User(
            uid: user.uid,
            email: user.email,
            displayName: user.displayName,
            isAdmin: isAdmin)
        : null;
  }

  // auth change user stream
  Stream<local.User> get user {
    return _auth.authStateChanges().asyncMap(_userFromFirebaseUser);
  }

  // sign in anon
  Future signInAnon() async {
    try {
      auth.UserCredential result = await _auth.signInAnonymously();
      auth.User user = result.user;
      return _userFromFirebaseUser(user);
    } catch (e) {
      print(e.toString());
      return null;
    }
  }

  // sign in with email and password
  Future signInWithEmailAndPassword(String email, String password) async {
    try {
      auth.UserCredential result = await _auth.signInWithEmailAndPassword(
          email: email, password: password);
      auth.User user = result.user;
      print('Successfully logged in, User UID: ${user.uid}');
      return user;
    } catch (error) {
      print(error.toString());
      return null;
    }
  }

  // register with email and password
  Future registerWithEmailAndPassword(String email, String password) async {
    try {
      auth.UserCredential result = await _auth.createUserWithEmailAndPassword(
          email: email, password: password);
      auth.User user = result.user;
      // create a new document for the user with the uid
      await DatabaseService(uid: user.uid).updateUserData(null);
      print('Successfully registered, User UID: ${user.uid}');
      return _userFromFirebaseUser(user);
    } catch (error) {
      print(error.toString());
      return null;
    }
  }

  // sign out
  Future signOut() async {
    try {
      print('User signed out');
      return await _auth.signOut();
    } catch (error) {
      print(error.toString());
      return null;
    }
  }

  Future<Map<dynamic, dynamic>> get _userClaims async {
    final user = _auth.currentUser;

    // If refresh is set to true, a refresh of the id token is forced.
    final idTokenResult = await user.getIdTokenResult(true);

    return idTokenResult.claims;
  }
}

Found here: In flutter, how can I "merge" Firebase onAuthStateChanged with user.getTokenId() to return a Stream?

like image 146
Ocrimops Avatar answered Nov 17 '25 17:11

Ocrimops



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!