Search code examples
firebasefluttergoogle-cloud-firestoregoogle-cloud-functionsbusboy

Flutter calling firebase cloud function admin.auth.updateUser


EDIT**

Ok so I was able to get the parameters working thanks to first answer provided but now I have an issue whereby my function is creating a new user entirely in Firebase and not update an existing one, the uid that i am passing into the auth.admin.updateUser is teh uid of the existing user who's email i want to update. Here is the updated cloud function which is adding a new user rather than updating the existing:

    exports.updateEmail = functions.https.onCall((data, context) => {

  const email = data.email;
  const uid = data.uid;

  admin.auth().updateUser(uid, {
              email: email
          })
          .then(function(userRecord) {
              // See the UserRecord reference doc for the contents of userRecord.
              console.log("Successfully updated user", userRecord.toJSON());
              return response.status(200).json(userRecord.toJSON());
          })
          .catch(function(error) {
              console.log("Error updating user:", error);
              return response.status(404).json({
                  error: 'Something went wrong.'
              });
          });
});

I got the function from the firebase docs but it isn't doing what I intended it to do.

ORIGINAL POST**

I'm having some difficulty getting a cloud function to work when calling the function from within my flutter code. The issue that I am having is that the uid and email fields are undefined even though I am passing them through to the cloud function using busboy fields.

I'm trying to pass the email and uid field though to the function as follows:

final request = http.MultipartRequest('POST', Uri.parse('****************my function url************'));

request.fields['email'] = Uri.encodeComponent(newEmail);
request.fields['uid'] = Uri.encodeComponent(selectedUser.uid);

request.headers['Authorization'] = 'Bearer ${_authenticatedUser.token}';

  final http.StreamedResponse streamedResponse = await request.send();

And on the Node.js side I am trying to use these fields using busboy, here is my cloud function in Node.js:

exports.changeEmail = functions.https.onRequest((request, response) => {

if (!request.headers.authorization ||
    !request.headers.authorization.startsWith('Bearer ')
) {
    return response.status(401).json({
        error: 'Unauthorized.'
    });
}

let idToken;
idToken = request.headers.authorization.split('Bearer ')[1];
let email;
let uid;

const busboy = new Busboy({
    headers: request.headers
});

busboy.on('field', (fieldname, value) => {

    if (fieldname == 'email') {


        email = decodeURIComponent(value);
    }

    if (fieldname == 'uid') {

        uid = decodeURIComponent(value);
    }
});



admin.auth().updateUser(uid, {
        email: email
    })
    .then(function(userRecord) {
        // See the UserRecord reference doc for the contents of userRecord.
        console.log("Successfully updated user", userRecord.toJSON());
        return response.status(200).json(userRecord.toJSON());
    })
    .catch(function(error) {
        console.log("Error updating user:", error);
        return response.status(404).json({
            error: 'Something went wrong.'
        });
    });

});

Even though I am passing the fields in with busboy fields they are not getting set in the function, is there something I am doing wrong here?


Solution

  • Why don't you use a callable function? It will automatically receive the authentication data.

    The documentation even has examples on how to get the uid and email:

    Declare the function:

    exports.addMessage = functions.https.onCall((data, context) => {
      // ...
    });
    

    Get the user properties from the context parameter:

    // Message text passed from the client.
    const text = data.text;
    // Authentication / user information is automatically added to the request.
    const uid = context.auth.uid;
    const name = context.auth.token.name || null;
    const picture = context.auth.token.picture || null;
    const email = context.auth.token.email || null;
    

    Call the function from your Flutter code:

    Install cloud_functions package and then:

    import 'package:cloud_functions/cloud_functions.dart';
    
    await CloudFunctions.instance.call(functionName: 'addMessage');
    

    If the user is authenticated before calling the function that's all you need to do.

    You can also pass additional parameters to the function:

    await CloudFunctions.instance.call(functionName: 'addMessage', parameters: {"email": "[email protected]"});
    

    Any parameters will be passed to the data parameter on the function side.