简体   繁体   English

如何直接从 Flutter 代码创建新的 Firestore 用户集合

[英]How to create a new Firestore user collection directly from Flutter code

I have been able to get Firebase Authentication to work for Google sign in, Anonymous sign in and from Email and Password sign in, including sending a verification email during email and password sign in thanks to help on stackoverflow. I have been able to get Firebase Authentication to work for Google sign in, Anonymous sign in and from Email and Password sign in, including sending a verification email during email and password sign in thanks to help on stackoverflow. Everything works as intended.一切都按预期工作。 Now for my next step I am trying to create a user collection in Firestore using the uid created by Firebase Authentication.现在我的下一步是尝试使用 Firebase 身份验证创建的 uid 在 Firestore 中创建用户集合。 I am confident my code is written correctly because I have tested it with (unsecure) Security Rules and the process worked exactly as desired.我相信我的代码编写正确,因为我已经使用(不安全的)安全规则对其进行了测试,并且该过程完全符合预期。 I have reviewed the Firebase documentation several times but I cannot figure out what is wrong with my Security Rules code.我已经多次查看 Firebase 文档,但我无法弄清楚我的安全规则代码有什么问题。 I am trying to get the user to add a screenName after they have been authenticated by Google Sign in before allowing them access to the rest of the app.我试图让用户在通过 Google 登录验证后添加一个 screenName,然后再允许他们访问应用程序的 rest。 Once I can figure out the process for Google Sign in I will then do the same for Email and Password but I will ask any questions related to that separately.一旦我弄清楚了谷歌登录的过程,我将对 Email 和密码做同样的事情,但我会单独提出与此相关的任何问题。 How can I fix my Security rules to allow a new user to create a Screen name that will be added to the user collection in Firestore?如何修复我的安全规则以允许新用户创建将添加到 Firestore 中的用户集合的屏幕名称? Thanks in advance for the help.在此先感谢您的帮助。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{uid}/jobs/{document=**} {
       allow read, write: if request.auth.uid == uid;
  }

  match /users/{uid}/{document=**} {
       allow read, write: if request.auth.uid == uid;
  }

  }
  
  }


   class HomePage extends StatefulWidget {
      const HomePage({
        Key? key,
        
      }) : super(key: key);
    
      
    
      @override
      State<HomePage> createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      @override
      void initState() {
        super.initState();
        createUserInFirestore();
      }
    
      Future<void> createUserInFirestore() async {
        final GoogleSignIn googleSignIn = GoogleSignIn();
        final GoogleSignInAccount? user = googleSignIn.currentUser;
        final usersRef = FirebaseFirestore.instance.collection('users');
        final DocumentSnapshot doc = await usersRef.doc(user?.id).get();
        if (!doc.exists) {
          final userName = await Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => const CreateAccountPage(),
            ),
          );
          usersRef.doc(user?.id).set({
            'id': user?.id,
            'userName': userName,
            'photoUrl': user?.photoUrl,
            'email': user?.email,
            'displayName': user?.displayName,
            'bio': '',
            'timestamp': documentIdFromCurrentDate(),
          });
        doc = await usersRef.doc(user?.id).get();
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return AdaptiveLayoutScaffold(
          drawer: const SideSheet(
            userImage: FakeUserAvatars.stacy,
            userName: 'Stacy James',
          ),
          landscapeBodyWidget: Container(),
          portraitBodyWidget: Container(),
        );
      }
    }

class CreateAccountPage extends StatefulWidget {
  const CreateAccountPage({
    Key? key,
  }) : super(key: key);

  @override
  _CreateAccountPageState createState() => _CreateAccountPageState();
}

class _CreateAccountPageState extends State<CreateAccountPage> {
  final _formKey = GlobalKey<FormState>();
  late String userName;

  void submit() {
    _formKey.currentState?.save();
    Navigator.pop(context, userName);
  }

  @override
  Widget build(BuildContext context) {
    return AdaptiveLayoutScaffold(
      appBar: const Header(
        automaticallyImplyLeading: false,
        pageName: 'User Name',
      ),
      landscapeBodyWidget: Container(),
      portraitBodyWidget: ListView(
        children: [
          Column(
            children: [
              const Padding(
                padding: EdgeInsets.only(top: 16.0),
                child: Center(
                  child: Text('Create a User Name'),
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: Form(
                  key: _formKey,
                  child: TextFormField(
                    autovalidateMode: AutovalidateMode.always,
                    decoration: InputDecoration(
                      hintText: 'Must be between 3 and 20 characters',
                      labelText: 'User Name',
                      prefixIcon: Icon(
                        Icons.person,
                        color: Theme.of(context).iconTheme.color,
                      ),
                    ),
                    keyboardType: TextInputType.text,
                    onSaved: (val) => userName = val as String,
                  ),
                ),
              ),
              PlatformElevatedButton(
                onPressed: submit,
                buttonText: 'Create User Name',
              ),
            ],
          ),
        ],
      ),
    );
  }
}

After reading what you suggested and a few other things I used the Firestore Rules Playground to fix my code and then updated my Auth class to include a new method called createUserInFirestore() to handle the creation of a new user in Firestore using the uid after the user is created by Firebase Authentication.在阅读了您的建议和其他一些内容之后,我使用 Firestore Rules Playground 修复了我的代码,然后更新了我的 Auth class 以包含一个名为 createUserInFirestore() 的新方法来处理在 Firestore 中使用 uid 在 Firestore 中创建新用户用户由 Firebase 身份验证创建。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
  
  match /users/{uid}/{document=**} {
       allow read: if request.auth.uid == uid;
       allow create: if request.auth.uid == uid;
       allow update: if request.auth.uid == uid;
       allow delete: if request.auth.uid == uid;
       
  }
 

  }
 
  }

abstract class AuthBase {
  User? get currentUser;

  Stream<User?> authStateChanges();
  Future<User?> signInWithGoogle();
  Future<User?> createUserWithEmailAndPassword(
    String email,
    String password,
  );
  Future<void> checkEmailVerified(BuildContext context, Timer timer);
  Future<User?> signInWithEmailAndPassword(String email, String password);
  Future<User?> signInAnonymously();
  Future<void> resetPassword(BuildContext context, String email);
  Future<void> confirmSignOut(BuildContext context);
  Future<void> signOut();
}

class Auth implements AuthBase {
  final _firebaseAuth = FirebaseAuth.instance;

  @override
  User? get currentUser => _firebaseAuth.currentUser;

  @override
  Stream<User?> authStateChanges() => _firebaseAuth.authStateChanges();

  void _createNewUserInFirestore() {
    final User? user = currentUser;
    final CollectionReference<Map<String, dynamic>> usersRef =
        FirebaseFirestore.instance.collection('users');
    usersRef.doc(user?.uid).set({
      'id': user?.uid,
      'screenName': '',
      'displayName': user?.displayName,
      'photoUrl': user?.photoURL,
      'bio': '',
      'darkMode': false,
      'timestamp': documentIdFromCurrentDate(),
    });
  }

  @override
  Future<User?> signInWithGoogle() async {
    final GoogleSignIn googleSignIn = GoogleSignIn();
    final GoogleSignInAccount? googleUser = await googleSignIn.signIn();
    if (googleUser != null) {
      final googleAuth = await googleUser.authentication;
      if (googleAuth.idToken != null) {
        final UserCredential userCredential =
            await _firebaseAuth.signInWithCredential(
          GoogleAuthProvider.credential(
            idToken: googleAuth.idToken,
            accessToken: googleAuth.accessToken,
          ),
        );
        _createNewUserInFirestore();
        return userCredential.user;
      } else {
        throw FirebaseAuthException(
          code: FirebaseExceptionString.missingGoogleIDTokenCode,
          message: FirebaseExceptionString.missingGoogleIDTokenMessage,
        );
      }
    } else {
      throw FirebaseAuthException(
        code: FirebaseExceptionString.abortedByUserCode,
        message: FirebaseExceptionString.canceledByUserMessage,
      );
    }
  }

  @override
  Future<User?> createUserWithEmailAndPassword(
    String email,
    String password,
  ) async {
    final UserCredential userCredential =
        await _firebaseAuth.createUserWithEmailAndPassword(
      email: email,
      password: password,
    );
    _createNewUserInFirestore();
    return userCredential.user;
  }

  @override
  Future<void> checkEmailVerified(BuildContext context, Timer timer) async {
    final User? user = currentUser;
    await user?.reload();
    final User? signedInUser = user;
    if (signedInUser != null && signedInUser.emailVerified) {
      timer.cancel();
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(
          builder: (context) => const HomePage(),
        ),
      );
    }
  }

  @override
  Future<User?> signInWithEmailAndPassword(
    String email,
    String password,
  ) async {
    final UserCredential userCredential =
        await _firebaseAuth.signInWithCredential(
      EmailAuthProvider.credential(
        email: email,
        password: password,
      ),
    );
    return userCredential.user;
  }

  @override
  Future<void> resetPassword(
    BuildContext context,
    String email,
  ) async {
    try {
      await _firebaseAuth.sendPasswordResetEmail(email: email);
      Navigator.of(context).pop();
    } catch (e) {
      print(
        e.toString(),
      );
    }
  }

  @override
  Future<User?> signInAnonymously() async {
    final UserCredential userCredential =
        await _firebaseAuth.signInAnonymously();
    return userCredential.user;
  }

  Future<void> _signOut(BuildContext context) async {
    try {
      final AuthBase auth = Provider.of<AuthBase>(
        context,
        listen: false,
      );
      await auth.signOut();
      Navigator.pushAndRemoveUntil<dynamic>(
        context,
        MaterialPageRoute<dynamic>(
          builder: (BuildContext context) => const LandingPage(),
        ),
        (route) => false, //Setting to false disables the back button
      );
    } catch (e) {
      print(
        e.toString(),
      );
    }
  }

  @override
  Future<void> confirmSignOut(BuildContext context) async {
    final bool? didRequestSignOut = await showAlertDialog(
      context,
      cancelActionText: DialogString.cancel,
      content: DialogString.signOutAccount,
      defaultActionText: DialogString.signOut,
      title: DialogString.signOut,
    );
    if (didRequestSignOut == true) {
      _signOut(context);
    }
  }

  @override
  Future<void> signOut() async {
    final GoogleSignIn googleSignIn = GoogleSignIn();
    await googleSignIn.signOut();
    await _firebaseAuth.signOut();
  }
}


class HomePage extends StatelessWidget {
  const HomePage({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AdaptiveLayoutScaffold(
      drawer: const SideSheet(
        userImage: FakeUserAvatars.stacy,
        userName: 'Stacy James',
        userTitle: 'Business Owner',
      ),
      landscapeBodyWidget: Container(),
      portraitBodyWidget: Container(),
    );
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM