Get hands-on experience with 20+ free Google Cloud products and $300 in free credit for new customers.

Firebase Functions not Working .

hey there , I am currently working as a Android Developer intern at a Company. Currently I am the only techie in the company and i was asked to make an app for the company's founder and i am kind of stuck in an issue from days . I made a project in the firebase (hence i have the Owner role in GCP/Firebase for project) as we have this functionalities in the app which require firebase firestore , storage , and firebase functions . Firebase firestore and Storage is working fine. As soon as i tried to deploy two functions in the firebase/Google Cloud Console , then i was facing difficulty deploying the functions. After some digging i found out that the functions can be deployed only by the Default Compute Service Account for projects which come under organisations (our project was also an organisation level project in both firebase and google cloud , because of creating the project in firebase using the company email address xyz@company.com ) . Knowing this i was able to deploy the functions after few tries, but when i try to trigger them , they are just not getting triggered. I made a HTTP (on Call) testing function named sendHello , which basically send hello from the function (or server) on button click and when i try triggering this function (on button click) , it is giving me UNAUTHENTICATED error ...the other two Primary functions were HTTP (on Request ) function are getting triggered but they are failed to the intended work ..... Due to being stuck for the long time, i migrated from this project to new project (created a new project under normal personal email address firebase account ) and all the functions were running and working as expected.

these are three functions and their respective triggering function in android project in the activity is as follows ....

 

 

 

const functions = require("firebase-functions");
const nodemailer = require("nodemailer");
const admin = require('firebase-admin');

// Initialize Firebase Admin SDK
admin.initializeApp();

// Configure your email transport using the SMTP settings of your email provider
const transporter = nodemailer.createTransport({
    service: 'gmail', // Use your email service, such as Gmail
    host: 'smtp.gmail.com',
    port: 587,
    secure: false,
    auth: {
        user: 'xyz@gmail.com', // Your email id
        pass: 'xxxxxxxxxxxxxxxxx', // Your email password or an app-specific password if 2FA is enabled
    },
});

// Cloud function to send an email invite to the new co-owner
exports.sendInviteEmail = functions.https.onRequest((req, res) => {
    const email = req.body.email;  // Get the email from the request body
    const granter = req.body.granter;  // Get the granter name
    const appLink = "https://play.google.com/store/apps/details?id=com.example.abc";  // Link to your app's download page

    const mailOptions = {
        from: 'xyz@gmail.com',  // Sender address
        to: email,  // Recipient email
        subject: 'Co-Owner Access Granted',
        text: `You have been made co-owner by ${granter} in ABC app. Kindly download the ABC app from ${appLink}`,
    };

    // Send the email
    transporter.sendMail(mailOptions, (error, info) => {
        if (error) {
            return res.status(500).send({ success: false, error: error.toString() });
        }
        return res.status(200).send({ success: true, info });
    });
});


//Function to monitor new user account creation
exports.monitorCoOwnerAccountCreation = functions.https.onRequest(async (req, res) => {
    const { granter, granterKey, granterName, email } = req.body;

    try {
        // Set up a Firestore trigger to monitor user creation in Authentication
        admin.auth().getUserByEmail(email).then((userRecord) => {
            const coownerAccessReceiver = userRecord.uid;

            // Save co-owner details to Firestore
            const coOwnersUserDetails = {
                    coownerAccessGranter: granter,
                    coownerAccessGranterKey: granterKey,
                    coownerAccessGranterName: granterName,
                    coownerAccessReceiver: coownerAccessReceiver,
            };

            firestore.collection("CoOwners")
                .doc(coownerAccessReceiver)
                .set(coOwnersUserDetails)
                .then(() => {
                    console.log("Co-owner document successfully created.");
                    res.status(200).send({ success: true });
                })
            .catch((error) => {
            console.error("Error creating co-owner document: ", error);
            res.status(500).send({ success: false, error: error.message });
        });
        }).catch((error) => {
            console.log(`User with email ${email} does not exist yet.`);
            res.status(200).send({ success: false, message: `No user with email ${email} exists.` });
        });
    } catch (error) {
        res.status(500).send({ success: false, error: error.message });
    }
});

// Simple test function to send "Hello" response
exports.sendHello = functions.https.onCall((data, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError(
            'unauthenticated',
            'The function must be called while authenticated.'
        );
    }
    return "Hello from Firebase!";
});
private fun sendInviteEmail(granterUID: String, credential: String) {
    val url = "https://xxxxxx.cloudfunctions.net/sendInviteEmail"
    val jsonObject = JSONObject().apply {
        put("email", credential)
        put("granter", granterUID)
    }
    val requestBody = RequestBody.create(
        "application/json; charset=utf-8".toMediaTypeOrNull(), jsonObject.toString()
    )

    val request = Request.Builder()
        .url(url)
        .post(requestBody)
        .build()

    val client = OkHttpClient()

    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            Log.e("NetworkErrorEmail", "Network call failed", e) // More detailed error log
        }

        override fun onResponse(call: Call, response: Response) {
            if (response.isSuccessful) {
                Log.d("emailSent","Email sent successfully")
            } else {
                Log.d("emailSent","Failed to send email: ${response.message}")
            }
        }
    })
}

private fun monitorNewCoOwnerCreation(granterUID: String, granterKey: String, granterName: String, credential: String) {
    val url = "https://xxxxxxx.cloudfunctions.net/monitorCoOwnerAccountCreation"
    val jsonObject = JSONObject().apply {
        put("granter", granterUID)
        put("granterKey", granterKey)
        put("granterName", granterName)
        put("email", credential)
    }
    val requestBody = RequestBody.create(
        "application/json; charset=utf-8".toMediaTypeOrNull(), jsonObject.toString()
    )

    val request = Request.Builder()
        .url(url)
        .post(requestBody)
        .build()

    val client = OkHttpClient()

    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            Log.e("NetworkErrorMonitor", "Network call failed", e) // More detailed error log
        }


        override fun onResponse(call: Call, response: Response) {
            if (response.isSuccessful) {
                Log.d("monitorStart","Firebase Function triggered successfully")
            } else {
                Log.d("monitorStart","Failed to trigger Firebase Function: ${response.message}")
            }
        }
    })
}


private fun sendHelloFunction() {
    // Call the 'sendHello' function
    functions
        .getHttpsCallable("sendHello")
        .call()
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                // Handle successful response
                val result = task.result?.data as? String
                Toast.makeText(this, result, Toast.LENGTH_LONG).show()
            } else {
                // Handle error
                Toast.makeText(this, "Function call failed: ${task.exception?.message}", Toast.LENGTH_LONG).show()
            }
        }
}

 

 

 

I am just getting empty response messages in this two logs ...
Log.d("emailSent","Failed to send email: ${response.message}") and
Log.d("monitorStart","Failed to trigger Firebase Function: ${response.message}")
for sendInviteEmail and monitorNewCoOwnerCreation respectively .... and Function call failed: UNAUTHENTICATED for this toast in sendHelloFunction
Toast.makeText(this, "Function call failed: ${task.exception?.message}", Toast.LENGTH_LONG).show()

0 2 2,704
2 REPLIES 2

Hi @Divyansh-Raj,

Welcome to the Google Cloud community!

The UNAUTHENTICATED error for sendHello and the failures for sendInviteEmail and monitorCoOwnerAccountCreation strongly suggest that the problem lies in how your Android app is interacting with Firebase Authentication and passing those credentials to your functions.

What you should investigate:

  • Organization vs. Personal Project: You observed that functions worked correctly in a personal project but not in the organization project. This reinforces that the issue is likely related to authentication setup and not the functions code itself. Organization projects often have stricter security settings and authentication flows.
  • Default Service Account: The link mentions the "Default Compute Service Account." While your functions deployment might be working because of this account, that account is not what your Android app should use. Your app needs to authenticate a user. The Default Service Account is for backend-to-backend communication, not for user interaction with your functions.
  • Authentication in Android: The core problem is that your Android code (especially sendInviteEmail and monitorNewCoOwnerCreation) likely isn't including proper Firebase Authentication. When you make HTTP requests to your functions, you aren't attaching the authentication token that Firebase would expect. sendHello, being a callable function, is explicitly checking for authentication, and failing because no authentication is passed.

You can try below troubleshooting steps and recommendations that could help resolve this behavior:

  1. Proper Firebase Authentication Setup in Android: Ensure you have correctly set up Firebase Authentication in your Android app. This typically involves initializing Firebase and using a method like email/password, Google Sign-In, or another authentication provider.
  2. Obtain ID Token: After a user successfully logs in, get their ID token using Firebase's Authentication API. This token proves the user's identity.
  3. Include ID Token in HTTP Requests: For sendInviteEmail and monitorNewCoOwnerAccountCreation, include the ID token in the Authorization header of your HTTP requests. 
  4. Callable Functions: For sendHello, Firebase client SDK (using getHttpsCallable) will automatically handle authentication if the user is logged in through the Firebase Authentication SDK. No explicit header is needed there. The fact that this is failing means the user is not logged in within the client.

If the observed latency is not acceptable for your end, please reach out to Google Cloud Support. Their team has specialized expertise in diagnosing underlying problems. When contacting them, provide comprehensive details and include screenshots. This will help them better understand your issue. However, I can’t give a specific date as to when this will be resolved.

I hope the above information is helpful.

Hello @ChieKo 
ThankYou for correctly pointing out the flaws in my code.
After i published this post and before your reply , i got to know that the ID token is required to send the request to firebase HTTP trigger based functions and i rectified my code accordingly.
The android side code is as follows for calling the sendInviteEmail.

 

 

 

//Somewhere in the onCreate of activity

val firebaseUser = FirebaseAuth.getInstance().currentUser
if (firebaseUser != null) {
                        firebaseUser.getIdToken(true).addOnCompleteListener { task ->
                            if (task.isSuccessful) {
                                val idToken = task.result?.token
                                // Now, send the request with the ID token
                                sendInviteEmail(firebaseUser.uid, credential, idToken)
                            } else {
                                Log.e("ID Token Error", "Failed to retrieve ID token: ${task.exception}")
                            }
                        }
                    }

private fun sendInviteEmail(granterUID: String, credential: String, idToken: String?) {
        val url = "https:/xxxxxx.cloudfunctions.net/sendInviteEmail"
        
        val jsonObject = JSONObject().apply {
            put("email", credential)
            put("granter", granterUID)
        }

        val requestBody = RequestBody.create(
            "application/json; charset=utf-8".toMediaTypeOrNull(), jsonObject.toString()
        )

        Log.d("BearerToken",idToken.toString())

        val request = Request.Builder()
            .url(url)
            .post(requestBody)
            .addHeader("Authorization", "Bearer $idToken")  // Add the ID token here
            .build()

        val client = OkHttpClient()

        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.e("NetworkErrorEmail", "Network call failed", e) // More detailed error log
            }

            override fun onResponse(call: Call, response: Response) {
                if (response.isSuccessful) {
                    Log.d("emailSent", "Email sent successfully")
                } else {
                    Log.d("emailSent", "Failed to send email: ${response.message}")
                }
            }
        })
    }

  

 

 

  and the sendInviteEmail functions code is as follows ...

 

 

exports.sendInviteEmail = functions.https.onRequest(async (req, res) => {
    const idToken = req.headers.authorization?.split("Bearer ")[1];

    console.log("Function triggered, checking authorization...");

    if (!idToken) {
        return res.status(403).send("Unauthorized");
    }

    console.log("Token has some value");

    try {
        // Verify the ID token
        const decodedToken = await admin.auth().verifyIdToken(idToken);
        console.log('Successfully Verified ID token:', decodedToken);

        // Proceed with email logic
        const email = req.body.email;
        const granter = req.body.granter;
        const appLink = "https://play.google.com/store/apps/details?id=com.company.abc";

        const mailOptions = {
            from: 'xyz@company.com',
            to: email,
            subject: 'Co-Owner Access Granted',
            text: `You have been made co-owner by ${granter} in ABC. Kindly download the ABC app from ${appLink}`,
        };

        transporter.sendMail(mailOptions, (error, info) => {
            if (error) {
                return res.status(500).send({ success: false, error: error.toString() });
            }
            return res.status(200).send({ success: true, info });
        });
    } catch (error) {
        console.error("Error verifying ID token:", error);
        return res.status(403).send("Unauthorized");
    }
});

 

 

 As you can see i am  sending the token and verifying it in the functions code, but i am still encountering the same problem.
To use this token verification workflow , i think the function resource has to be granted the cloud Run Invoker role for allAuthenticatedUsers Principal.

I tried granting the specified permissions in GCP as follows:Cloud Run Functions -> "Go to Cloud Run" at the top right -> selecting the checkbox next to function resource name -> Permissions ->Add principal -> allUsers/allAuthenticatedUsers principal and Cloud Run Invoker Role -> unable to update policyThere was dialog, IAM Policy Update Failed, with the message :
The 'Domain Restricted Sharing' organization policy (constraints/iam.allowedPolicyMemberDomains) is enforced. Only principals in allowed domains can be added as principals in the policy. Correct the principal emails and try again .
I also have shared the Owner rights  with the Founder (my Employer) of the company, from the firebase, who is also the Organisation Super administator of the company  and when we try to edit the Domain Restricted Sharing in the organizational policies section (in IAM in GCP) , then we both have only viewing access to this policy .
I don't know why the founder was also not able to edit the policy, i mean if he can't then who can edit the organizational policies.
The Policy can be only be edited at the organization level and only by the user who is Organization Policy Administrator. We are still not being able to figure out who is the Organization Policy Administrator for the organization.
i think this role/permission has something to do with the Google Cloud Workspace or Admin panel , which the founder has access to.
Also both the founder and i need additional permissions to view the organization policy in the and again this permissions can only be granted by Organization Policy Administrator.(We were trying to view the organization policies in order to get to know about the OPA ).
Our main focus is currently figuring out who is Organization Policy Administrator and if no one, then how can someone be Organization Policy Administrator for the Organization , because then only we can disable the domain restricted sharing policy for org and then grant cloud invoker role to function for allAuthenticatedUser.

@ChieKo  Please see if you can help us, we are really stuck in the situation for days.
  
Top Solution Authors