You are using Firebase HTTPS Callable Functions which has a feature of Firebase Authentication to authenticate users to your app.
But you want to make your Cloud Function private in order to be secure and not accessed publicly.
=======EDIT=======
Why your URL is not accessible after making your Cloud Function private?
Since the Cloud Function is private, it needs Authentication to be accessed. You will not be able to access the Cloud Function endpoint from the browser since it needs the Authorization token related to the IAM user or service account who has the permissions to invoke the Cloud Function. And the browser is not providing an authorization token automatically.
You are getting the CORS issue on the client side.
I have reproduced the issue by creating a private Cloud Function ( by removing the AllUsers
Member from the Cloud Function Invoker Role
) and calling it from a Firebase app. And I indeed was getting the CORS issue.
In order to avoid CORS, I tried to add the Cloud function as a Firebase Hosting rewrite in firebase.jon :
Note: Firebase Hosting supports Cloud Functions in us-central1 only so this option will not be a solution for you. However I did the following test with Firebase Hosting in us-central1
"rewrites": [
{
"source":"/test",
"function":"test"
}
]
Also, I changed the Cloud functions code to verify ID tokens
Cloud function
const cors = require('cors')({origin: true});
const functions = require('firebase-functions');
var admin = require("firebase-admin");
var serviceAccount = require(".ServiceAccount.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "DATABASE_URL"
});
exports.test = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const tokenId = req.get('Authorization').split('Bearer ')[1];
return admin.auth().verifyIdToken(tokenId)
.then((decoded) => res.status(200).send(decoded))
.catch(function(err) {
console.error(err);
console.log(err);
res.status(401).send(err)});
});
});
Client Side:
firebase.auth().currentUser.getIdToken(true)
.then(function(idToken){
console.log('Id token is..');
console.log(idToken);
fetch('Cloud_Function_endpoint',{
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + idToken
},
method: 'post',
body: JSON.stringify({ data })
}).then(result => {
console.log(result);
}).catch(error => {
console.log(error);
});
}).catch(function(error){
console.log(error);
});
So after making these changes, I was no longer getting the CORS issue however I was getting the 401 (Unauthorized)
error.
Solutions Tested:
We can’t use Firebase Hosting, as there is no Firebase Hosting service account to add roles/permissions since requests are originated from clients.
To secure a Cloud Function, an alternative is to enable the Domain Restriction Sharing Policy (DRS) and this way we will not be able to serve public Cloud Functions.
But the previous is the same as removing the AllUsers
from a Cloud Function making it private.
I was able to find that for the private functions, we can specify an IAM policy for a given user / service account. And to authenticate the requests from the client, we will need to supply a bearer token associated with the user / service account.But this also can’t be achieved since the bearer token required for IAM is too short lived to be used in firebase.json file.
Conclusions:
- It is not possible to access a private Cloud Function from a Firebase app.
- The only solution is to have a public Cloud Function.