0
votes

Im trying to implement a callable cloud function into my firebase application which would store secrets to a secret manager bucket.

I understand that the callable function has access to the auth.uid from the session once a user loggs in and that I can implement the validation within the function but I would still like to know how to secure the public endpoint of the function because I can see that if I simply try to visit the function URL it actually counts towards the cloud invocation quota even though it returns an error.

I tried disabling the allUsers invocation permission within the function settings and that actually makes the url inaccessible but at the same time i get this error when trying to call the function from my app -

Access to fetch at 'https://europe-west1-atlaspark-app.cloudfunctions.net/{cloudFunctionName}' from origin 'https://atlaspark-app.web.app' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Any suggestion to what might be causing this?

1

1 Answers

1
votes

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.