Search code examples
javascriptswiftfirebasegoogle-cloud-platformgoogle-cloud-firestore

How to add new members in Group using Firebase


I am developing the app which has grouping function. Now I have the problem about adding new member in group.

Like Slack, in group creating onboarding flow user can decide group name and add members which don't use the app yet. As adding members function, I want to use inviting mail link using firebase dynamic links.

Data structure is below

User
 - id
 - name
 - email

Group
  - id
  - groupName
  - members[]

Group's members array has user id. But when someone creates new group, it is possible that new users don't register the app yet. So they don't have user id property in the app.

How do I fix this problem? When someone creates new group, should I develop sign up functions new user using Firebase auth? This means then new member has automatically user id, and adding those to members property. Or should group member array have mail address instead of user id.

Please tell me. I'm happy with Swift or JavaScript you will teach me. Thank you.


Solution

  • UPDATE

    After reading your comment I would propose another approach.

    When the Group creator user adds users to a group, if a user does not already exists you could, from the front-end, call a Callable Cloud Function (CF) that creates a temporary Firestore document in a specific collection. The ID of this document will be the (future) userId.

    Then, still in this same Cloud Function, you send an email to the email address (you need to generate yourself the email, for example with the email extension) with a link containing the userId as query string value.

    Example of code for this first CF:

    exports.provisionNewAccount = functions
        .https.onCall(async (data, context) => {
    
            try {
    
                // You can check that the caller is authenticated
                // if (context.auth.uid) {execute the rest of the code} else {throw an error} 
    
                // New user email
                const userEmail = data.email;
    
                // Generate the new user docID
                const fakeDocRef = admin.firestore().collection('_').doc();
                const requestId = fakeDocRef.id;
    
                // Create the doc in a specific collection
                await admin.firestore().collection('usersCreationRequests').doc(requestId).set({ email: userEmail, treated: false });
    
                // Generate the link to include in the email
                const linkURL = 'https://your.app/register.html?requestId=' + requestId
                
                // Send the email by creating a doc in the Extension collection
                await db
                    .collection("emails")
                    .add({
                        to: userEmail,
                        message: {
                            subject: "....",
                            html: `<a href="${linkURL}">Click to create your account</a>`  // adapt the html to add some text
                        },
                    });
                    
                    return {result: 'OK'}
    
            } catch (error) {
                console.log(JSON.stringify(error));
                throw new functions.https.HttpsError('internal', JSON.stringify(error));
            }
    
        });
    

    You call it as explained here, by passing the future user's email.

    When the email recipient clicks on the link, you open a specific page of your web app that shows a set of fields for the future user to enter his password, display name etc. Then on clicking on a sign-in button in this page you call a Callable Cloud Function passing it the Firestore document ID plus the field values (you get the document ID from the query string).

    As shown below, this Cloud Function creates the user in the Authentication service (using the Admin SDK) and flag the Firestore document as treated. Upon getting back the Cloud Function result in the web app you authenticate the user (you have his email and password, since he/she entered it in the form).

    exports.createNewAccount = functions
        .https.onCall(async (data, context) => {
    
            try {
    
                const userEmail = data.email;
                const userId = data.userId;
                const userPassword = data.password;
                const userDisplayName = data.displayName;
    
                // Fetch the user doc created in the first CF
                const snapshot = await admin.firestore().collection('usersCreationRequests').doc(userId).get();
    
                const treated = snapshot.get('treated');
                const email = snapshot.get('email');
    
                if (!treated && userEmail === email) {
    
                    const createUserPayload = {
                        email,
                        emailVerified: false,
                        password: userPassword,
                        displayName: userDisplayName
                    };
    
                    const userRecord = await admin.auth().createUser(createUserPayload);
    
                    return { result: 'OK' }
    
                } else {
    
                    return { result: 'User already created' }
    
                }
    
    
            } catch (error) {
                console.log(JSON.stringify(error));
                throw new functions.https.HttpsError('internal', JSON.stringify(error));
            }
    
        });
    

    I’m actually using this exact approach for a B2B collaborative web app in which users can invite new users by email.


    INITIAL ANSWER

    (Totally different from the update)

    So they don't have user id property in the app… How do I fix this problem? When someone creates new group, should I develop sign up functions new user using Firebase auth?

    You can use the Anonymous Authentication mode, it exactly corresponds to your needs:

    You can use Firebase Authentication to create and use temporary anonymous accounts to authenticate with Firebase. These temporary anonymous accounts can be used to allow users who haven't yet signed up to your app to work with data protected by security rules. If an anonymous user decides to sign up to your app, you can link their sign-in credentials to the anonymous account so that they can continue to work with their protected data in future sessions.

    When signing-in with Anonymous Authentication a userId (uid) will be created and later you will be able to convert an anonymous account to a permanent account