Request from Firebase Hosting to Firebase Function blocked by CORS - javascript

I'm trying to send push notifications between two devices for a tiny prototype. Both of them are Vue.js apps with firebase SDK integrated, so to implement a push notification flow I deployed a firebase function, but when I call it from any of devices, a CORS error is received as a response.
Both devices (mobile and desktop) have the same client code and knows the token of each other (storage into a firebase real-time database).
The function only uses firebase messaging to send the push notification.
Firebase function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const cors = require('cors')({ origin: true });
admin.initializeApp();
exports.notification = functions.https.onRequest((req, res) => {
return cors(req, res, () => {
if (req.method === "POST") {
return admin.messaging().send(notification)
.then(result => {
console.log(result);
res.status(200).send("ok")
})
.catch(err => res.status(500).send(err));
} else {
return res.status(400).send("Method not allowed");
}
});
});
Client code:
send(notification, token) {
return fetch("https://[zone]-[project].cloudfunctions.net/notifications", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token, notification })
});
}
And the error is:
Access to fetch at 'https://[zone]-[project].cloudfunctions.net/notifications' from origin 'https://[project].web.app' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

Could you use The functions.https.onCall trigger?
See https://firebase.google.com/docs/functions/callable .
The Cloud Functions for Firebase client SDKs let you call functions directly from a Firebase app. To call a function from your app in this way, write and deploy an HTTPS Callable function in Cloud Functions, and then add client logic to call the function from your app.

The URI that I used to invoke the function was wrong. It's solved. Sorry about that question.

Related

How do I make my react app fetch my proxy API?

my api call isn't sending to the proxy url domain. I know that it isn't calling as the console.log in the server isn't going off. Could someone please help me?
//client package.json
"proxy":"http://localhost:3001"
//client app
useEffect(() => {
const apicall = async () => {
try{
const response = await fetch("/")
if (response.status !== 200){
throw new Error()
}
else{
console.log("api call was success")
console.log(response)
}
} catch(error){
console.log(error)
}}
apicall()
}, [])
//server
app.get("*", (req,res)=>{
console.log("apicalled")
res.cookie("refreshToken", 987654321, {
maxAge: 60000,
httpOnly: true
})
res.send("has cookie sent?")
})
Don't have an answer for you, but it looks like you're using create-react-app? I think if you've setup your own server, you don't need to set the proxy in your package.json.
I am running a small react app and trying to access an external api behind my company's proxies. I continue to get the cors error - I've tried setting a proxy in package.json and using setupProxy.js (separately), but haven't had any luck.

Firebase 'internal' error when using Functions and Admin

I am having trouble with some functions that have been working previously. I have created a function to let the user create another user and it was working fine. Functions that do not use Admin works fine just as before.
However, due to some Firebase updates, the function broke and I am getting an 'internal' error. I had to change import firebase from "firebase/app"; to import firebase from "firebase/compat/app"; as well as import "firebase/compat/functions"; but that did not work, as well as updating firebase to "^9.8.1", but that did not work either.
Here is my web function:
async registerUser(userInfo) {
const functions = getFunctions();
const createUser = httpsCallable(functions, "createUser");
// Used to have this:
// let createUser = firebase.functions().httpsCallable("createUser");
//Call to function where it breaks.
return await createUser({
//User data here
})
.then(
//stuff happens here
).catch((error) => {
//Error happens here
})
}
Here is the function that was already deployed in Firebase:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.createUser = functions.https.onCall(async (userData) => {
return await admin
.auth()
.createUser(userData)
.then(() => {
return { isError: false };
})
.catch((error) => {
return { isError: true, errorMessage: error };
});
});
Here is the response log:
Request Method: OPTIONS
Status Code: 403
Referrer Policy: strict-origin-when-cross-origin
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
content-length: 305
content-type: text/html; charset=UTF-8
date: Tue, 24 May 2022 16:04:14 GMT
server: Google Frontend
You can refer to the Admin SDK error handling page and check the Structure of the error, the refer to policy: strict-origin-when-cross-origin part gives us a good starting point.
But first, we can see the root cause of this issue as in the same page we have a table for Platform error codes where:
INTERNAL | Internal server error. Typically a server bug.
In the Firebase Admin Authentication API Errors we have a similar table where:
auth/internal-error | The Authentication server encountered an unexpected error while trying to process the request. The error message should contain the response from the Authentication server containing additional information. If the error persists, please report the problem to our Bug Report support channel.
Now talking about the cors message, this question suggests:
Consider importing like this, as shown in the samples:
const cors = require('cors')({origin: true});
And the general form of your function will be like this:
exports.fn = functions.https.onRequest((req, res) => {
cors(req, res, () => {
// your function body here - use the provided req and res from cors
})
});
You might need to make some changes to the structure as the question has some time, so I will also leave the reference to the Call functions via HTTP requests and check if it works for you.
I will follow up if you provide updates and consider submitting a bug report as previously mentioned by the documentation if this is necessary.

Preflight error on Firebase cloud functions

I'm facing a preflight error when I try to call a cloud function of mine from my website. I implemented the cors module in my cloud function, and my request got the cors header authorizations
The cloud function :
const cors = require('cors')({ origin: true });
exports.CLOUDFUNCTION = functions.https.onRequest(
(request: any, response: any) => {
cors(request, response, async () => {
response.status(200).send('hello');
})
}
);
The website request :
fetch('FIREBASE_URL/CLOUDFUNCTION',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Headers': 'Authorization'
},
body: JSON.stringify(body), // body is a simple {"variable": "value"}
}
);
The error
Access to fetch at 'FIREBASE_URL/CLOUDFUNCTION' from origin 'MYWEBSITE' 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.
If you are getting a 403 Forbidden Error when trying to access your function via it's URL, you have a problem with your function's deployment, it's configuration or you have made a mistake in the URL.
Note: While I use "traditional" require statements here to match your example, I encourage you to use newer ES6+ JavaScript features (const, let, async/await, import, etc.) for any newly written functions.
Deploy using the latest firebase-tools version
Make sure you are deploying using the latest version of the firebase-tools CLI.
When v7.7.0 of firebase-tools released (Jan 15, 2020), the way Cloud Functions are invoked on the server changed so that functions could be invoked by only authenticated users. To be accessible to Firebase Users, these functions must be made public by explicitly granting the allUsers group the Cloud Function Invoker permission.
In v7.7.0 and later, this is done for you as part of deployment. However, if you deploy functions using an older version, you will need to configure this permission yourself or redeploy using a newer firebase-tools version.
Check the exported function name
Make sure the function you export is named what you expect once deployed.
In particular, pay close attention to when your function is exported as part of a function group either deliberately or accidentally. This often turns up when you've split your functions into multiple files. In the below code blocks, CLOUDFUNCTION gets exported as myFunctions-CLOUDFUNCTION and not just CLOUDFUNCTION as you may expect.
// myFunctions.js
exports.CLOUDFUNCTION = functions.https.onRequest(...);
// index.js (incorrect)
exports.myFunctions = require("./myFunctions.js");
// index.js (correct)
const myFunctions = require("./myFunctions.js");
exports.CLOUDFUNCTION = myFunctions.CLOUDFUNCTION;
Check the function's URL
Check the Cloud Functions URL you are using for typos. Function names in Cloud Functions URLs are case-sensitive.
The correct URL should follow the format:
https://<REGION>-<PROJECT_ID>.cloudfunctions.net/<EXPORTED_FUNCTION_NAME>
Example:
https://us-central1-fir-sandbox.cloudfunctions.net/echo
Handle CORS errors & stop processing
In your code example, you pass in the NextFunction without an error handler. While using { origin: true }, this is "fine", but you'll start running into trouble when you start restricting the origins you call your function from. This is particularly handy for preventing your functions being invoked directly by their URL (where origin would be undefined). Take a look at the documentation or the next section for more info.
const cors = require('cors')({ origin: true });
exports.CLOUDFUNCTION = functions.https.onRequest(
(request, response) => { // <-- don't use `: any` here, as you are disabling the built-in types provided by firebase-functions
cors(request, response, async (err) => {
if (err) {
// Denied by CORS/error with CORS configuration
console.error("CORS blocked request -> ", err);
response.status(403).send("Forbidden by CORS");
return;
}
response.status(200).send('hello');
})
}
);
Optional: Tighten the cors configuration
While you can reflect the Access-Control-* headers using the cors package, consider explicitly setting these server-side.
const { projectId: PROJECT_ID } = JSON.parse(process.env.FIREBASE_CONFIG);
const cors = require('cors')({
// during emulation, allow localhost & calling directly (i.e. no origin specified);
// at all other times, restrict to deployed hosting sites only
origin: process.env.FUNCTIONS_EMULATOR === "true"
? /^(https?:\/\/localhost:\d+|undefined)$/
: [`https://${PROJECT_ID}.firebaseapp.com`, `https://${PROJECT_ID}.web.app`],
allowedHeaders: ['Content-Type', 'Authorization']
});
exports.CLOUDFUNCTION = functions.https.onRequest(
(request, response) => {
cors(request, response, async (err) => {
if (err) {
// Denied by CORS/error with CORS configuration
console.error("CORS blocked request -> ", err);
response.status(403).send("Forbidden by CORS");
return;
}
response.status(200).send('hello');
})
}
);
This simplifies your client-side code:
fetch('FIREBASE_URL/CLOUDFUNCTION',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
}
);
Optional: Use Callable Functions
If your functions will require you to do actions on behalf of a user, you could make use of Callable Cloud Functions instead of the more bare-bones HTTPS Request functions. This version of a HTTPS Function handles CORS, authentication, and supports Promise-based returning of data.
Note: This will still require the function to be public as described above.
On the server side:
exports.CLOUDFUNCTION = functions.https.onCall(async (data, context) => {
if (!context.auth) {
// users must be logged in
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called while authenticated.'
);
}
if (data.variable === undefined)) {
throw new functions.https.HttpsError(
'invalid-argument',
'Parameter "variable" must be a string'
);
}
// you can return a promise here
// this sends back the JSON string "hello world"
return "hello world";
});
On the client side:
const callFunction = firebase.functions().httpsCallable('CLOUDFUNCTION');
callFunction(body)
.then(
(responseData) => {
// TODO: handle response
},
(functionError) => {
// TODO: handle error
}
);

Using Outlook REST API Beta From an Outlook add-in

I have created an outlook add-in with ReactJS and followed this guide to get a token to be able to use the Outlook v2.0 REST APIs: https://learn.microsoft.com/en-us/office/dev/add-ins/outlook/use-rest-api
Now I would like to start using the Outlook Beta REST APIs and I figured I could use the same token to make the API calls, however I get the following error which suggests I cannot use this token:
{"error":{"code":"UnableToReadToken","message":"OAuth token submitted with the request can not be parsed.","innerError":{"requestId":"b96fc800-82d4-4b6d-8aa0-0b9ff6a36873","date":"2020-02-21T09:27:27"}}}
Is there anyway to call this API by using the token generated by Office.context.mailbox.getCallbackTokenAsync? I am aware that I can probably get an oauth2 token via Azure AD, however within the Azure AD Portal I do not have the proper admin access to follow this process so I am looking for a solution which does not rely on that.
Here is a code snippet of my functions to get the token and call the API:
getToken() {
return new Promise(async function (resolve, reject) {
try {
Office.context.mailbox.getCallbackTokenAsync({ isRest: true }, function (result) {
if (result.status === "succeeded") {
let accessToken = result.value;
console.log(result.value);
resolve(accessToken);
} else {
console.log(result.status);
reject(result.status);
}
});
} catch (error) {
console.error(error);
reject(error);
}
})
}
getRules(token) {
return new Promise(async function (resolve, reject) {
try {
const url = 'https://outlook.office.com/api/beta/me/mailfolders/inbox/messagerules';
const header = new Headers({ 'Authorization': `Bearer ${token}` });
const options = {
headers: header
};
let response = await fetch(url, options);
let jsonResponse = await response.json();
console.log(jsonResponse);
resolve(jsonResponse);
} catch (error) {
console.error(error);
reject(error);
}
});
}
You mention not having the proper admin access to use the AD v2 authentication endpoint.
There are currently two approaches to handle app registration and user authorization. Did you confirm, if by chance one of these methods might still work...
Use Azure AD v2 authentication endpoint:
https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/beta/use-outlook-rest-api-beta#RegAuthConverged
Use Azure Active Directory and OAuth:
https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/beta/use-outlook-rest-api-beta#RegAuthAzure
...
Some additional information (which you might already be aware of):
The v2 authentication endpoint has been promoted from preview to Generally Available (GA) status for Outlook and Outlook.com developers.
If you have an in-production app that uses Windows Live API to access Outlook.com mailbox data, you must rewrite the app to use the the v2 authentication endpoint and the Outlook REST API. Because Windows Live API is being deprecated for Outlook.com, and Outlook.com users get their mailboxes enabled for the Outlook REST API, these users will get HTTP 404 errors when attempting to run such Windows Live API apps.
Read more here: https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/beta/use-outlook-rest-api-beta

FCM considered external network on Cloud Functions for Firebase

I am getting an error stating external network is not accessible, which makes sense as I am on the free tier of Firebase. But I thought Firebase services were included in the free tier, and as such, I should be able to use FCM.
Here is the code I am using for my index.js for the functions.
var functions = require('firebase-functions');
var admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.buttonPress = functions.https.onRequest((req, res) => {
let testToken = "TOKEN";
let payload = {
data: {
type: req.body.type
}
};
admin.messaging().sendToDevice(testToken, payload)
.then(function (response) {
...
})
.catch(function (error) {
...
});
});
firebaser here
Billing account not configured. External network is not accessible and quotas are severily limited. Configure billing account to remove these restrictions.
This message now shows up for any Cloud Functions that are invoked from projects that are on the free tier. It doesn't mean that any calls have actively been blocked, just they calls to external services will be blocked for this project.
We're looking if we can get the message removed.
For Free tier account, Firebase has imposed a restriction on accessing external service that is not within google's network.
To get to the root cause of the problem just go to the Firebase console and check your functions's log. The log will show exactly what service or packages you installed is trying to make external HTTP request.
To sent FCM through cloud functions, you can use the code below.
Check log if you are getting right tokens.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendNotification = functions.firestore
.document('/users/{documentId}')
.onWrite((change, context) => {
console.log("DOCUMENT ID : " + context.params.documentId);
//Get all data
const payload = {
notification: {
title: 'Test title!',
body: `${userName} sent you a following request.`
// icon: follower.photoURL
}
};
admin.messaging().sendToDevice(followedFCMToken, payload)
.then(function (response) {
console.log("Push response : " + response);
return response
})
.catch(function (error) {
console.error("Error in sending push");
});
});

Categories

Resources