firebase / functions-samples

Collection of sample apps showcasing popular use cases using Cloud Functions for Firebase
https://firebase.google.com/docs/functions
Apache License 2.0
12.07k stars 3.84k forks source link

Add Cors to callable function #395

Closed pietrofxq closed 6 years ago

pietrofxq commented 6 years ago

I wrote a callable function to invite new users for my app:

exports.UserInvitation = functions.https.onCall((data, context) => {
  const email = data.email
  const password = passwordGenerator.generate({
    length: 12,
    numbers: true
  })
  console.log('Inviting user:', email)
  const mailOptions = {
    from: '',
    to: email,
    subject: ``,
    html: utils.formatEmail(`
     Hello, you've been invited to use my app.
    `)
  }

  return new Promise((resolve, reject) => {
    admin.auth().createUser({
      email: email,
      emailVerified: false,
      password: password
    }).then((user) => {
      console.log(`Created user with uid ${user.uid}`)
      transporter.sendMail(mailOptions).then(() => {
        console.log(`Email sent to ${email}`)
        resolve()
      })
    }).catch((err) => {
      console.error(err.code)
      reject(new functions.https.HttpsError(err.code, err.message))
    })
  })
})

I call it like this in my app:

functions.httpsCallable('UserInvitation')({ email: this.input.value })

But I'm getting "No Access Control Allow Origin" error when trying to call it from client. How can I fix this if I don't have access to req/res objects inside onCall, to enable cors?

ahaverty commented 6 years ago

@pietrofxq we just added this at the top of the file: const cors = require('cors')({ origin: true }); and didn't need to change anything else.

You're probably better off on the main firebase functions github or stackoverflow for stuff like this 😉

SidneyNemzer commented 6 years ago

If you're using Firebase hosting, you can use a rewrite to move your functions onto the same domain as the rest of your site: https://firebase.google.com/docs/hosting/functions#direct_hosting_requests_to_your_function

(This means requests will no longer be cross-origin, and won't trigger CORS)

pietrofxq commented 6 years ago

I was able to solve the issue. There's no way to add CORS to onCall functions because it's handled automatically. The problem was with my error handler: when returning an instance of functions.https.HttpsError, the first parameter MUST match one of the codes listed in this page: https://firebase.google.com/docs/reference/functions/functions.https.HttpsError

HelmerBarcos commented 6 years ago

@pietrofxq but the error message is not been sent. or?

jshaker commented 6 years ago

@pietrofxq Just wanted to thank you personally, I had the same issue and was pulling my hair out. Really not intuitive

8tomo8 commented 5 years ago

This can happen whenever there is an error in the function. In my case I was passing an undefined variable to the callable function

tricaricom commented 5 years ago

I am having the same issue.

My Firebase client is 5.8.2 My Firebase Cloud Function are: admin 7.0.0, and functions 2.2.0

My client code:

const someFunction = FirebaseFunctions.httpsCallable("someFunction");
someFunction({  email: "test@test.com"  })
.then(result => {
  console.log("someFunction response: ", result.data);
}).catch(error => {
   console.log("error",error);
});

My Cloud Functions code:

const functions = require("firebase-functions");
...
exports.someFunction = functions.https.onCall((data) => {

    return {
        email: data.email
    };
});

As you can see, all I am really doing for a simple test is trying to return the email back to the client, but I am getting blocked by CORS. It does not matter if I run from localhost or I deploy the client to Firebase Hosting, the result is the same.

Does anybody know what I am doing wrong?

nicholmikey commented 5 years ago

@tricaricom Did you ever solve this? I'm having the same issue.

tricaricom commented 5 years ago

@nicholmikey no I never did get it to work. I used the cors method mentioned above and it works. I would like to get callable functions to work at some point though.

8tomo8 commented 5 years ago

@tricaricom @nicholmikey The CORS error in console with callable functions only indicates there is an error in the cloud function. Check the logs inside the Firebase console and you'll see the actual error. CORS is not applicable for callable functions even when running on localhost

gregbkr commented 5 years ago

I have the same issue... Stuck since 2hours.. :-(

xximranxx commented 5 years ago

Same issue - Even stranger, I have 2 functions, both exactly the same (both are essentially empty), both a direct callables (onCall) and one of them gives a CORS error, the other not (I posted the question to reddit as well: https://www.reddit.com/r/Firebase/comments/c4js7u/cors_issue_with_one_cloud_function_but_not/)

Here is the code

// cloud functions
const functions = require('firebase-functions');
const REGION = 'europe-west1';
exports.workingFunctions = functions.region(REGION).https.onCall((data, context) => {
 console.log('workingFunctions');
 return {
  a: 1
 };
});
exports.notWorkingFunction = functions.region(REGION).https.onCall((data, context) => {
 console.log('notWorkingFunction');
 return {
  b: 2
 };
});

// webapp
export async function test() {
 console.log('1');
 let workingFunctionAPI = functions.httpsCallable("workingFunction"); workingFunctionAPI().then(result => {
  console.log('workingFunctionAPI', result)
 }).catch(err => {
  console.error('workingFunctionAPI', err);
 });

 console.log('2');
 let notWorkingFunctionAPI = functions.httpsCallable("notWorkingFunction"); notWorkingFunctionAPI().then(result => {
  console.log('notWorkingFunctionAPI', result)
 }).catch(err => {
  console.error('notWorkingFunctionAPI', err);
 }); console.log('3');
}

Another weird thing is that I had another firebase project, where I think that everything is exactly the same, but there both of these functions were working without any issues. When I deployed the same code to this project (this project is stage, the other was dev), one function is failing

xximranxx commented 5 years ago

Also, how can we reopen the issue?

gregbkr commented 5 years ago

@xximranxx : After searching hours... I fixed my problem. Probably same as yours:

Thread of similar error: https://stackoverflow.com/questions/51139843/firebase-serve-from-a-locally-served-app-call-locally-served-functions

Still you will need to comment the line before to push in cloud. But cloud html + cloud function do not give me any CORS error.

My code if it could help: https://gitlab.com/finstack/search-hack-db/tree/master/firebase-simple-function

Hope it will fix you! See you!

xximranxx commented 5 years ago

@gregbkr My issue is also for cloud html + cloud function, not only when testing locally In that case, I don't see anything at all that is different in yours

gregbkr commented 5 years ago
xximranxx commented 5 years ago

@gregbkr I tried - If I use the local emulator, everything worked perfectly immediately; no CORS issue So it's only on the cloud

Any ideas, anyone?

xximranxx commented 5 years ago

I seemed to have gotten it to work by 1) Deleting the (web) app from the firebase console 2) Deleting the cloud functions and deploy them again Not sure which step was responsible

Can I trust that it will work from now? Not sure, as I have no idea what was causing it and whether CORS is supposed to be an issue or not. I would say not, onCall should take care of that

chris-gunawardena commented 5 years ago

I think this happens when trying to evoke functions.https.onCall from hosting/ports other than the firebase serve env. I was trying to call from react-scripts start env.

CaptainYouz commented 5 years ago

Hello everyone, in my app, I first added firebase.functions().useFunctionsEmulator('http://localhost:5000'); after firebase.initializeApp() to test my callable functions locally. I had the the CORS error on when trying to call them with my app locally. By changing to firebase.functions().useFunctionsEmulator('http://localhost:5001');, it worked for me.

Thkasis commented 5 years ago

For those who may be like me forgetful, always try to wrap your Fb functions in promises. Some forms of callbacks can create this error as I just remembered after 3 hours of dealing with this. Wrap call backs in promises and use resolve/reject to return the result

harvey56 commented 5 years ago
* Added : `firebase.functions().useFunctionsEmulator('http://localhost:5001')` `<script>` section of `index.html`, so local html is targeting my LOCAL functions.

* And no more CORS error… :-))

@gregbkr That did it for me. Thanks for your reply !

That's the error message I had :

Access to fetch at 'https://us-central1-my-project.cloudfunctions.net/myFunc' from origin 'http://localhost:5000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

It was not an obvious one. There is no mention of the useFunctionsEmulator() method in the main doc. It's only in the API doc. Plus I expected that a npm run serve would serve the function locally. I feel this could be clarified in the documentation.

The useFunctionsEmulator() section in the API doc has a link that points to this : https://firebase.google.com/docs/functions/local-emulator where there is no mention of useFunctionsEmulator().

hugomejia commented 5 years ago

@tricaricom @nicholmikey The CORS error in console with callable functions only indicates there is an error in the cloud function. Check the logs inside the Firebase console and you'll see the actual error. CORS is not applicable for callable functions even when running on localhost

you're absolutely right @8tomo8 ,I feel really silly! thanks a lot men! It's 3:05AM and I've been dealing with this issue for the last 6 hours thinking it was a Cors issue (i also got the CORS message on the Chrome console), and when I checked the Firebase Console I got "Error: memory limit exceeded. Function invocation was interrupted", just changing the allocated memory from 256MB to 512MB solved the issue!!!

Pierre-Hway commented 4 years ago

It looks like there's a problem with europe region. I removed the functions.region setting (back to default us-central1 server) and it now works perfectly.

jacola commented 4 years ago

Same as @Pierre-Hway -- using the default region fixed the issue for me too.

forrestswork commented 4 years ago

I was able to solve the issue. There's no way to add CORS to onCall functions because it's handled automatically. The problem was with my error handler: when returning an instance of functions.https.HttpsError, the first parameter MUST match one of the codes listed in this page: https://firebase.google.com/docs/reference/functions/functions.https.HttpsError

this page returns a 404, the new page is https://firebase.google.com/docs/reference/functions/providers_https_.html#functionserrorcode

tibiv commented 4 years ago

In case you are using @angular/fire don't forget to specify FUNCTIONS_REGION in order to match the region where your cloud function is actually deployed.

import { AngularFireFunctionsModule, FUNCTIONS_REGION } from '@angular/fire/functions';

@NgModule({
  imports [
    AngularFireFunctionsModule
  ],
  providers: [
    { provide: FUNCTIONS_REGION, useValue: 'europe-west1' }
  ]
}
export class AppModule {}
TarikHuber commented 4 years ago

Had the same issue and found the right solution for the region problem. If you defined a region on the backend (for example europe-west1) you need to define the same region when calling that function with:

firebaseApp.functions('europe-west1').httpsCallable('yourFunction')

This should definitely be in the docu. It says that you need to call the function with the same region but no example code is shown.

marimano commented 4 years ago

I recently faced the CORS error only for newly created functions. After hours of desperate search I found that newly created functions don't have Cloud Functions Invoker at https://console.cloud.google.com/functions while the old ones have allUsers: image

Here firebase mentions that starting from Jan 15, 2020 no allUsers is set anymore https://cloud.google.com/functions/docs/securing/managing-access-iam

noeclement commented 4 years ago

@marimano just created a function and had the same issue! Seems like they changed default permissions for newly created functions. Thank you for the solution!

Arahabica commented 4 years ago

@marimano Thank you. It works!!

shanerbo commented 4 years ago

I recently faced the CORS error only for newly created functions. After hours of desperate search I found that newly created functions don't have Cloud Functions Invoker at https://console.cloud.google.com/functions while the old ones have allUsers: image

Here firebase mentions that starting from Jan 15, 2020 no allUsers is set anymore https://cloud.google.com/functions/docs/securing/managing-access-iam

This one save my day! I almost cried, I spend 5 hour on this issue, none approach worked, but this!

ttmbahclnz commented 4 years ago

It worked for me. Thank You!!

ishbaid commented 4 years ago

@marimano you're my hero <3

mark-at-pieces commented 4 years ago

I lost Saturday, Sunday and much of my hair to this Issue Thank you so much @marimano

lupas commented 4 years ago

@marimano Thanks so much, this worked! For anyone looking for the CLI command:

gcloud alpha functions add-iam-policy-binding yourFunctionName --member=allUsers --role=roles/cloudfunctions.invoker
mandarini commented 4 years ago

Thank you @marimano and @lupas . You also need to add the --role flag to that CLI command:

gcloud alpha functions add-iam-policy-binding yourFunctionName --member=allUsers --role=invoker

Available roles reference here: https://cloud.google.com/functions/docs/reference/iam/roles

lupas commented 4 years ago

Thank you @marimano and @lupas . You also need to add the --role flag to that CLI command:

gcloud alpha functions add-iam-policy-binding yourFunctionName --member=allUsers --role=invoker

Available roles reference here: https://cloud.google.com/functions/docs/reference/iam/roles

Oh you're right - that got cut off from copy+paste ;-)

Shouldn't it be --role=roles/cloudfunctions.invoker though?

AprilPolubiec commented 4 years ago

Same issue - Even stranger, I have 2 functions, both exactly the same (both are essentially empty), both a direct callables (onCall) and one of them gives a CORS error, the other not (I posted the question to reddit as well: https://www.reddit.com/r/Firebase/comments/c4js7u/cors_issue_with_one_cloud_function_but_not/)

Here is the code

// cloud functions
const functions = require('firebase-functions');
const REGION = 'europe-west1';
exports.workingFunctions = functions.region(REGION).https.onCall((data, context) => {
 console.log('workingFunctions');
 return {
  a: 1
 };
});
exports.notWorkingFunction = functions.region(REGION).https.onCall((data, context) => {
 console.log('notWorkingFunction');
 return {
  b: 2
 };
});

// webapp
export async function test() {
 console.log('1');
 let workingFunctionAPI = functions.httpsCallable("workingFunction"); workingFunctionAPI().then(result => {
  console.log('workingFunctionAPI', result)
 }).catch(err => {
  console.error('workingFunctionAPI', err);
 });

 console.log('2');
 let notWorkingFunctionAPI = functions.httpsCallable("notWorkingFunction"); notWorkingFunctionAPI().then(result => {
  console.log('notWorkingFunctionAPI', result)
 }).catch(err => {
  console.error('notWorkingFunctionAPI', err);
 }); console.log('3');
}

Another weird thing is that I had another firebase project, where I think that everything is exactly the same, but there both of these functions were working without any issues. When I deployed the same code to this project (this project is stage, the other was dev), one function is failing

I'm in a similar situation... I deployed to another environment. I have no problems with the functions in my dev environment but get the cors error in staging. It is not due to an error in the code - there are no logs, the function never gets invoked. I do know that my develop functions were deployed months ago, so 'allUsers' is set in the Cloud Functions Invoker. I'm hesitant to change that setting - is it safe to do so?

wizjamin commented 4 years ago

@marimano thank you so much. I've been trying to figure this out for days now

cris178 commented 4 years ago

Anyone have more details on how @marimano solution works? Specifically is there a way to just do it on the firebase console ui?

Edit: Found that a possible issue is that the region the function and hosting server are at could cause the issue as well. They should be in the same region as having a different region means a cross origin issue in itself. That being said the issue is still present. I just used aws lambda functions and they have built in cors support.

Matthiasc commented 4 years ago

It looks like there's a problem with europe region. I removed the functions.region setting (back to default us-central1 server) and it now works perfectly.

I had the same, when I removed the region method region("europe-west1") it reverted back to us-central1 and it worked. Not sure if only time will break this again.

jjalonso commented 4 years ago

Same problem here, it doesnt work on region('europe-west2') but it works well without. on us-central1

katowulf commented 4 years ago

Locking this thread. This is not related to functions-samples, is closed, and people should stop trying to troubleshoot here.

Solutions and key takeaways (from the comments above):

When using callable functions, if you aren't using the default region, you must specify the region on the client so the SDK knows which URL to use. Without this, you'll get CORS errors. In javascript that looks like this: firebase.functions('<region>').httpsCallable('<function name>').

If you're using Firebase hosting, you can use a rewrite to move your functions onto the same domain as the rest of your site as explained here. This means requests will no longer be cross-origin, and won't trigger CORS.

When returning an instance of functions.https.HttpsError, the first parameter MUST match one of the codes listed in this page.