firebase function url rewrite breaking cookies - firebase

I have two cloud functions, youtubeRedirect and youtubeToken. Before doing the URL rewrite, (as found here https://firebase.google.com/docs/hosting/functions), everything was working.
youtubeRedirect takes the user to a google auth screen, and provides a redirect to the youtubeToken function. It sets these two cookies
res.cookie('state', state.toString(), { maxAge: 3600000, secure: true, httpOnly: true });
res.cookie('UID', req.query.uid);
When the user is redirect to youtubeToken, the cookie value is checked;
if (!req.cookies.state) {throw new Error('State cookie not set or expired.');}
When using URL rewrites for my custom domain, it fails at this point as req.cookies is empty. When I check my cookies in Chrome, I can see the cookies have been set for my custom domain. I have rewritten both youtubeRedirect and youtubeToken in my firebase.json file
"hosting": {
"public": "api",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "/youtubeRedirect",
"function": "youtubeRedirect"
},
{
"source": "/youtubeToken",
"function": "youtubeToken"
}
]
}
The auth side of things is working fine - authorised redirect URI's etc. all changed beforehand, where it used to redirect to us-central1-[appName].cloudfunctions.net it now redirects to my custom domain. The redirection to youtubeToken is working - my brower URL is showing my custom domain.
If I revert everything back to using the us-central1-[appName].cloudfunctions.net function address, it all works. Checking the cookies in devtools shows that the cookies are set as I expect. If I run it all with my custom domain, the cookies are also there... except the function itself cannot find them.
It seems to be that the cookies are being checked somehow before the URL rewrite has occurred, and failing? Really struggling to see a solution to this!
I thought I had found a smoking gun when having console.log(req.get('host')) in youtubeToken returned us-central1-[appName].cloudfunctions.net however placing it within youtubeRedirect has the same outcome, so that just confused me further...

Found the answer in another question
When using Firebase Hosting together with Cloud Functions or Cloud Run, cookies are generally stripped from incoming requests. This is necessary to allow for efficient CDN cache behavior. Only the specially-named __session cookie is permitted to pass through to the execution of your app.
Source
My solution is to set the cookie like so in youtubeRedirect
const sessionCookie = {
state: state.toString(),
UID: req.query.uid
}
res.cookie('__session', JSON.stringify(sessionCookie), { maxAge: 3600000, secure: true, httpOnly: true });
And then retrieve it in youtubeToken
const sessionCookie = JSON.parse(req.cookies.__session)
if (!sessionCookie.state) {

Related

Request header field sentry-trace is not allowed by Access-Control-Allow-Headers in preflight response

I'm developing a Frontend using NextJS and Keycloak for auth-purpose. After adding Sentry, I'm facing this issue here, where the token endpoint of Keycloak is returning an error; So I can log in.
I've tried many things:
Change the web-origin config of Keycloak, which (obviously) doesn't change or solves the problem
Play with the Sentry client config, without success, because the denyUrls property still make the Sentry SDK send the sentry-trace into the request.
Now I don't have any more Idea, so I coming here for more help.
So after some investigations, I came across this tracingOrigins property that can be set using integrations like this:
integrations: [
new (Sentry.Integrations as any).BrowserTracing({
tracingOrigins: [
process.env.NEXT_PUBLIC_URL,
process.env.NEXT_PUBLIC_BACKEND_URL,
process.env.NEXT_PUBLIC_MATOMO_URL,
],
}),
],
This config is done inside the sentry.client.config.ts file. The downside is that, urls which are not included there, are simply not tracked.
Unfortunately, Keycloak has hardcoded list of allowed headers, so you can't configure Keycloak for sentry-trace header.
You can have some non ideal work arounds:
don't use sentry
compile own hacked Keycloak version, where you allow that header
add reverse proxy in front of Keycloak, which will add sentry-trace header to allowed headers
...
I've solved this issue on a nextJs application by adding the following header to the static sourcemap response.
'Access-Control-Allow-Headers' on next.config.js
const CONFIG = {
headers: () => [
{
source: "/_next/:path*",
headers: [
{ key: "Access-Control-Allow-Origin", value: SHOP_ORIGIN },
{ key: 'Access-Control-Allow-Headers', value: '*' },
],
},
],
}

how to handle root path request with node.js on firebase hosting?

I'm developing web system using firebase hosting + functions.
Inspite of specifying rewrite rules on firebase.json,
a part of routing doesn't work.
root/
 ├ functions/
 │ ├index.js
 │ ├routes/
 │ │ └ index.js
 │ └views/
 │ ├ index.jade
 │ └ sub.jade
 └ public/
└index.html // this is created by default. I don't want to use this.
This is my firebase.json
"rewrites": [{
"source": "**",
"function": "app"
}],
And this is node.js code.
router.get('/', function(req, res, next) {
res.render('index');
});
router.get('/subdir', function(req, res, next) {
res.render('sub');
});
result
https://myurl/ -> public/index.html is shown.(not handled on node.js)
https://myurl/ -> handled on node.js
Do you know how to handle root path request with node.js on firebase hosting.
See https://firebase.google.com/docs/hosting/full-config#hosting_priority_order .
Priority order of Hosting responses
The different Firebase Hosting configuration options described on this page can sometimes overlap. If there is a conflict, Hosting determines its response using the following priority order:
Reserved namespaces that begin with a /__/* path segment
Configured redirects
Exact-match static content
Configured rewrites
Custom 404 page
Default 404 page
Exact-match static content is high priority order than Configured rewrites.
So https://myurl/ get static content /index.html.
Can you try remove public/index.html?
I tried. I can rewrite.
See https://github.com/zkohi/firebase-hosting-using-functions-samples .
And you should check https://firebase.google.com/docs/hosting/functions .
Make a dummy folder that is empty. eg.: dummyApiFolder
Make a very simple cloud function. eg.
exports.bla = functions.https.onRequest(async (req, res) => {
res.send("server bla");
});
Use the following hosting rules
"hosting": {
"target": "api",
"public": "dummyApiFolder",
"rewrites": [
{
"source": "**",
"function": "bla"
}
]
}
3.1. target is only needed if you have set this up in your .firebaserc file
3.2. "public": "dummyApiFolder", is needed. Otherwise the CLI won't allow the deployment
Now all requests should get forwarded to bla.
The reason I'm advising to start with a simple cloud function is, express can be setup incorrectly. This can also give you an error, but you end up not knowing if it's a firebase issue or a code issue!
It's simple! Just rename the index.html file in the public directory into some other name. Also, just in case: In the firebase.json file, Change the public property's value
from public to .

firebase hosting blocking script due to CORS issue

I am using firebase hosting to host few scripts and trying to access them from another site. it naturally gets blocked due to CORS issues. based on my research on other forum threads etc i modified the firebase.json as below
{
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"headers": [ {
"source" : "**",
"headers" : [ {
"key" : "Access-Control-Allow-Origin",
"value" : "*"
} ]
}]
}
}
which essentially allow any url to access the resources hosted here. however, on trying to run my site i still see below
Access to XMLHttpRequest at 'https://oracle-bot-sdk.firebaseapp.com//loader.json'
from origin 'https://insurance-bot.moblize.it' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
what else is needed?
In addition to your firebase.json changes for cors, your firebase functions http / https function needs to also include the cors plugin.
Example
const cors = require('cors')({origin: true});
const functions = require('firebase-functions');
const app = functions.https.onRequest((req, res) => {
cors(req, res, () => {
// Your app stuff here
// Send Response
res.status(200).send(<response data>);
});
});
Express App Example
import express from "express";
const cors = require('cors')({origin: true});
const app = express();
app.get('**', (req, res) => {
cors(req, res, () => {
// Your App Here
// Send response
res.status(200).send(<response data>);
});
});
More documentation Serve Dynamic Content with Cloud Functions - Create an HTTP function to your Hosting site (Cors is not mentioned in the documentation btw)
Is the site (https://insurance-bot.moblize.it/) that is calling to https://oracle-bot-sdk.firebaseapp.com a Firebase hosted app?
I only ask because with version 4.2+ of Firebase Tools allows you to setup Multisite hosting using the same Firebase Project. I am not sure if that would help your situation out at all. Just wanted to mention it.
In the error message:
insurance-bot.moblize.it/:1 Failed to load https://oracle-bot-sdk.firebaseapp.com//loader.json: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://insurance-bot.moblize.it' is therefore not allowed access.
I noticed an extra '/' in https://oracle-bot-sdk.firebaseapp.com//loader.json. I doubt that is the issue, but wanted to mention it.
There is something that you could try. Similar to the answers above but a little different:
"headers": [
{
"source": "*",
"headers": [
{
"key": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
]
Also I would read some of the info here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Access-Control-Allow-Origin If you have not already.
I hope I was able to help in some way. Let me know.
My guess that you've mixed up firebase hosting and firebase cloud functions. Firebase hosting is made for hosting static websites and web apps. As you try to access from your website that is hosted on different domain your configuration for hosting is not applied. You mentioned that you host some scripts and it sounds like cloud functions. And good old CORS headers can help to your cloud functions like:
exports.corsEnabledFunction = (req, res) => {
res.set("Access-Control-Allow-Origin", "*");
res.set("Access-Control-Allow-Methods", "GET");
res.set("Access-Control-Allow-Headers", "Content-Type");
res.set("Access-Control-Max-Age", "3600");
// Continue with function code
...
}
More info: https://cloud.google.com/functions/docs/writing/http#handling_cors_requests
Make sure you have the Blaze or Flame plan, I think Spark plan blocks external access, maybe for the same reason as it does with cloud functions
Cloud Functions for Firebase - Billing account not configured
Go to the Google Cloud Console: https://console.cloud.google.com/functions/
Click the checkbox next to the function on which you want to grant access.
Click Show Info Panel in the top right corner to show the Permissions tab.
Click Add member.
In the New members field, type allUsers.
Select the role Cloud Functions > Cloud Functions Invoker from the Select a role drop-down menu.
Click Save.
taken from: https://github.com/firebase/firebase-functions/issues/645#issuecomment-605835353
This was the best solution for me as posted above
Go to the Google Cloud Console: https://console.cloud.google.com/functions/
Click the checkbox next to the function on which you want to grant access.
Click Show Info Panel in the top right corner to show the Permissions tab.
Click Add member.
In the New members field, type allUsers.
Select the role Cloud Functions > Cloud Functions Invoker from the Select a role drop-down menu.
Click Save.
taken from: https://github.com/firebase/firebase-functions/issues/645#issuecomment-605835353
Try pasting this as it's directly from the documentation, Customize Hosting Behavior:
"hosting": {
// Add the "headers" section within "hosting".
"headers": [ {
"source" : "**/*.#(eot|otf|ttf|ttc|woff|font.css)",
"headers" : [ {
"key" : "Access-Control-Allow-Origin",
"value" : "*"
} ]
}
}
Firebase hosting CORS doesn't work WITH custom domain.
However, CORS API works with https://yyyyyyy.web.app/ or firebaseapp.com domain

Best setup/workflow for testing and deploying an application using Google Firebase Hosting and Functions

I built a small web application (www.suntax.de) with vuejs and hosted on Google Firebase. I use the Firebase Hosting, Database and Functions. I use Functions to do some calculation on the backend.
Everything is running now, but I had some trouble getting there and some things I would like to improve regarding my workflow.
Thus, I would like to explain you what I did and where I struggle now.
First I set up the vuejs applicationd deployed it to firebase. I added my custom domain and everything was doing fine.
Later I implemented the cloud function and want to make a call from my vuejs app.
After firebase deploy, I can call this function in the browser and it just works fine:
https://us-central1-suntax-6d0ea.cloudfunctions.net/calcEEGcompensation?year=2013&month=1&size=10
Now I thought, that I just call the URL of the function from my vuejs app. But then I got the following error message:
[Error] Origin * is not allowed by Access-Control-Allow-Origin.
I was then reading that I had to add a rewritesection in the firebase.json:
Now my firebase.json looks like this:
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
// Add the following rewrites section *within* "hosting"
"rewrites": [ {
"source": "/calcEEGcompensation", "function": "calcEEGcompensation"
} ]
}
}
Now I was able to call my firebase function with the following URL:
https://www.suntax.de/calcEEGcompensation?year=2013&month=1&size=10
After integrating the above URL in my vuejs application, the application is running fine after deployment to firebase server.
As I want to keep improving the application, I would like to test everything locally before deploying.
I know that I can run firebase hosting and functions locally by:
firebase serve --only functions,hosting
However, now my application has the hard coded call to my function https://www.suntax.de/calcEEGcompensation?year=2013&month=1&size=10 and this again leads to the error [Error] Origin * is not allowed by Access-Control-Allow-Origin.
But also changing the URL to the local function
http://localhost:5001/suntax-6d0ea/us-central1/calcEEGcompensation?year=2013&month=1&size=10
leads to the error message
[Error] Origin * is not allowed by Access-Control-Allow-Origin.
Some further reading brought me to the solution with cors. So I changed my function to:
const functions = require('firebase-functions');
const cors = require('cors')({origin: true});
exports.calcEEGcompensation = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const yearOfCommissioning = req.query.year;
const monthOfCommissioning = req.query.month;
const pvsystemsize = req.query.size;
...
res.send("hello");
});
});
This helped and everything works now:
- The deployed application is still running fine.
- I can run the application locally while calling the local function as well as the deployed function. I just have to change the URL of the function.
But this is now my question:
Can I solve this issue in a better way? If I test the vuejs application and the function locally, I have to change the function URL before deployment. And then I have to change it back while testing locally.
I was not able to test my application and function locally without cors.
My ideal solution would be to have a setup, that can be fully tested locally and which can be easily deployed with firebase deploy without any changes of URLs. Is this possible?
Thanks and best regards,
Christoph
I found the solution which is pretty simple and does exactly what I want. I have no idea why I did not figure it out before.
Here is what I did:
I just call the relative URL from my firebase hosting:
calcEEGcompensation?year=2013&month=1&size=10
and everything works fine if the rewrites are properly set in firebase.json:
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
// Add the following rewrites section *within* "hosting"
"rewrites": [ {
"source": "/calcEEGcompensation", "function": "calcEEGcompensation"
} ]
}
}
After setting up everything like this, I can just execute firebase serve --only functions,hosting and I can test everything locally.
After executing firebase deploy, everything runs smoothly on the server.
I do not need cors.
Thanks #wonsuc for your answers.
Update(For Firebase Hosting):
Currently there is no workaround you can solve it with Firebase Hosting SDK.
But there is alternative way you can achieve this.
Try below code in your hosting source.
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
console.log('It's a local server!');
}
In my opinion, these are best way to check dev environment currently.
Therefore you should use location.hostname in your Firebase Hosting, and server.address() in Cloud Functions.
And define your Functions end point with constant variable.
const DEBUG = location.hostname === 'localhost' || location.hostname === '127.0.0.1';
const FUNCTIONS_ENDPOINT_DEV = 'http://localhost:5001/';
const FUNCTIONS_ENDPOINT_PRD = 'https://us-central1-something.cloudfunctions.net/';
const FUNCTIONS_URL_CALC = 'calcEEGcompensation?year=2013&month=1&size=10';
var endpoint;
if (DEBUG) {
endpoint = FUNCTIONS_ENDPOINT_DEV + FUNCTIONS_URL_CALC;
} else {
endpoint = FUNCTIONS_ENDPOINT_PRD + FUNCTIONS_URL_CALC;
}
Original answer(For Cloud Functions for Firebase):
Have you tried node.js net module's server.address() function?
This method will tell you if your functions code is running on localhost or real deployed server.
For examples, you can use like this.
const server = app.listen(function() {
let host = server.address().address;
let port = server.address().port;
if (!host || host === '::') {
host = 'localhost:';
}
console.log('Server is running on %s%s', host, port);
});

Firebase set up rewrites to pass current user to Cloud Function

I'm trying to use Cloud Functions for Firebase to serve content and I'd like to detect whether a user is logged in or not. I've set up a rewrite in my firebase.json that looks like this:
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"rewrites": [{
"source": "**",
"function": "getProfile"
}]
}
}
This works fine and I'm serving appropriate content based on the path that's being requested. However, because I'm not doing anything on the client side (i.e. I'm using rewrites rather than client-side redirects), I'm missing the opportunity to get the current user from a client-side script.
Is there some way I can use a header or a property of the request object so that I can serve different content to logged in vs. non-logged in users in my server-side rewrites scenario?
Firebase Hosting passes along any cookie named __session when it calls a Cloud Function. An easy way to do this is to simply listen for ID tokens in your web app and set the cookie appropriately:
firebase.auth().onIdTokenChanged(user => {
if (user) {
user.getIdToken().then(token => {
document.cookie = `__session=${token};max-age=3600`;
});
} else {
document.cookie = '__session=;max-age=0';
}
});
Then, in your Cloud Function, you can parse the ID token out of the cookie and verify it using code like in this sample.

Resources