Read cookies in Firebase GCP Functions using Hosting Rewrites - node.js

I'm using Firebase Functions, and Firebase Hosting. Hosting redirects all traffic to my function.
Request cookies are not available when requesting the Hosted site (i.e. not the cloud function URL). Is there no way to access request cookies?
I'm migrating a website to Firebase and was assuming I could follow basic web principals of having access to same-domain cookies.
const { runWith } = require('firebase-functions');
const cookieParser = require('cookie-parser');
const express = require('express');
const app = express();
app.use(cookieParser());
function handleRequest(req, res) {
res.cookie('firebase1', 'test', {});
if (process.env.HOSTNAME) {
res.cookie('firebase2', 'test', {
domain: process.env.HOSTNAME,
});
}
res.cookie('firebase3', 'test', {
domain: req.hostname,
});
return res.json({
hostname: process.env.HOSTNAME,
'req.cookies': req.cookies, // always empty
'req.headers.cookie': req.headers.cookie, // always undefined
});
}
app.get('*', handleRequest);
app.use(handleRequest);
exports.index = runWith({
timeoutSeconds: 10,
memory: '128MB',
}).https.onRequest(app);
firebase.json
{
"functions": {
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log"
]
},
"hosting": {
"public": "public",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"function": "index"
}
]
}
}

Answer from Firebase's support team:
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.
When present, the __session cookie is automatically made a part of the cache key, meaning that it's impossible for two users with different cookies to receive the other's cached response. Only use the __session cookie if your app serves different content depending on user authorization. Also, you need to set the Cache-Control Header as private res.setHeader('Cache-Control', 'private').

I haven't tested it but the Express API doc indicates that, since you use the cookie-parser middleware, you should do req.cookies and not req.headers.cookie

Related

Local debugging endpoint with localhost and remote endpoint with custom domain are not consistent

I have a Firebase + Cloud Function API + Express API with the following files:
/functions/index.js
const functions = require('firebase-functions');
const express = require('express');
const app = express();
app.get('/api/echo', (req, res) => {
res.status(200).json({
message: req.query.message,
});
});
exports.api = functions.https.onRequest(app);
/firebase.json
{
"hosting": {
"rewrites": [
{
"source": "/api/**",
"function": "api",
"region": "us-central1"
}
]
},
"functions": [
{
"source": "functions",
"codebase": "default",
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log"
]
}
]
}
To test the app on LOCAL I run the following command:
$ firebase emulators:start --inspect-functions
To access the endpoint on LOCAL I go to the following url:
[1] http://localhost:5001/[my-project]/us-central1/api/api/echo/?message=Hello+World
To deploy the app to Firebase (REMOTE) I run the following command:
$ firebase deploy --only hosting,functions
To access the endpoint with Google domain I go to the following url:
[2] https://us-central1-[my-project].cloudfunctions.net/api/api/echo/?message=Hello+World
To access the endpoint with my custom domain I go to the following url:
[3] https://www.[mydomain.tld]/api/echo/?message=Hello+World
With all 3 urls above I get the following (expected) output:
{"message":"Hello World"}
My problem is: On urls: [1] and [2] I need to specify /api twice (please check above). I would like to configure things around in order to just need to specify: /api once on urls: [1], [2], [3]. It doesn't look good to me having to specify: /api twice.
It is a requirement for me to access the endpoints after: /api/ because on the parent level I have a web app that uses the endpoints.
Any idea on how to do this?
Thanks!

Unable to setup secure connection using greenlock-express

I am trying to use greenlock-express to test serving an app with ssl. I am following the documentation here:
So I have app.js:
"use strict";
// Here's a vanilla HTTP app to start,
// but feel free to replace it with Express, Koa, etc
var app = function (req, res) {
res.end("Hello, Encrypted World!");
};
module.exports = app;
And I have server.js:
"use strict";
var app = require("./app.js");
require("greenlock-express")
.init({
packageRoot: __dirname,
// contact for security and critical bug notices
maintainerEmail: "my_email#gmail.com",
// contact for security and critical bug notices
configDir: "./greenlock.d",
// whether or not to run at cloudscale
cluster: false,
})
// Serves on 80 and 443
// Get's SSL certificates magically!
.serve(app);
I used this command:
npx greenlock init --config-dir ./greenlock.d --maintainer-email
'my_email#gmail.com'
And this command:
npx greenlock add --subject my_website.online --altnames my_website.online
That generated the greenlock.d folder:
config.json
{
"defaults": {
"store": {
"module": "greenlock-store-fs"
},
"challenges": {
"http-01": {
"module": "acme-http-01-standalone"
}
},
"renewOffset": "-45d",
"renewStagger": "3d",
"accountKeyType": "EC-P256",
"serverKeyType": "RSA-2048",
"subscriberEmail": "my_email#gmail.com"
},
"sites": [
{
"subject": "my_website.online",
"altnames": [
"my_website.online",
"www.my_website.online"
],
"renewAt": 1
}
]
}
After I run the app on my VPS, this is what gets logged:
However, when I try to access the app, this is what I see:
I followed the exact steps in the tutorial so I do not see why this doesn't work.
Any thoughts?

Production React App Failing With 429 Error

I have built a react app webpage, i use node, express and react. I serve it within an ec2 ubuntu server with nginx, i also installed ssl and configured DNS but when I load the page, the first time it gives no problems, but when reloaded, i get this Server responded with 429 status code
The page is very simple itself, but stills failing and i can't solve the problem, i have read a lot of posts and blogs to fix it but none has worked (this only happens in production, in dev environment i get no error when running my node app).
Some of those said that you had to lazy load images and i did it, i have also tried loading components in timeout so requests are made every 1.5 seconds...
The main files that give me problems are the manifest.json and favicon.ico, they are accesible by url so i can't understand why (another 429 example).
Node server.js
Routes has nothing, just console logs
require('dotenv').config();
const express = require("express");
const app = express();
const path = require("path");
const cors = require("cors");
const allowedOrigins = [`http://localhost:${process.env.PORT}`];
var corsOptions = {
origin: function (origin, callback) {
if (allowedOrigins.indexOf(origin) !== -1 || !origin) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
};
app.use(cors(corsOptions));
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(express.static(path.join(__dirname, "/public")));
app.use("/", require("./routes/routes"));
app.get("*", (_, res) => res.redirect("/"))
app.listen(process.env.PORT, function () {
console.log(`Listening on port ${process.env.PORT}`);
});
App directory structure
This is my folders structure, public folder is the build that react script npm run build returns.
React app directory structure
Here you can see the structure, nothing special to what you get when run npx create-react-app. As you can see, there's a favicon.ico and manifest.json.
Manifest.json
{
"short_name": "Proximedica Bajío",
"name": "Proximedica Bajío - Oxígeno Medicinal",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
This is all, if you need me to provide some other file it would be a pleasure.
Thanks!

Firebase-hosting can't get to api routes node

I have a route for '/' but it serves the public html index file instead, I tried changing the root directory of my api to /api/** but it gives me a 404 error.
it works quiet well when I run it locally but when I deploy it I run into the issue of only having the index.html file.
here is the firebase.json file
{
"hosting": {
"public": "public",
"rewrites":[
{
"source":"/api/**",
"functions":"app"
}
],
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}
and the index.js folder
const functions = require('firebase-functions')
const express = require('express')
\\.
\\.
\\requiring routes and middleware
app.use(helmet())
app.use(express.json())
app.use(cookieParser())
app.use(cors({
origin:"*",
credentials:true
}))
app.get('/',(req,res)=>{
res.status(200).send("works");
})
app.use(locationActions)
app.use(filterActions)
app.use('/auth',userAuth)
app.use('/adm',AdminRoutes)
app.use('/prod',prodActions)
app.use('/user',userActions)
exports.app = functions.https.onRequest(app);
.send() doesn't seem to work quiet well with firebase I just have to say something like response.json({message}), this will actually send the response. also mongoose connection is prohibited as I was using a free account so any connection outside firebase is rejected.

Proxy in package.json not affecting fetch request

I am trying to fetch some data from the development server using React.
I am running the client on localhost:3001 and the backend on port 3000.
The fetch request :
const users = fetch('/api/users');
users.then((err,res) => {
console.log(res);
})
When I run my development server and webpack-dev-server I get the following output:
GET http://localhost:3001/api/users 404 (Not Found)
I tried specifying the proxy in the package.json so it would proxy the request to the API server, however nothing has changed.
Here is my package.json file:
.. and the webpack.config :
Please tell me, if you need to see anything else from my project. I apologies, if I'm missing something and not being thorough, I'm still quite new to using these technologies.
You can modify your fetch request API url to give the complete hostname since
fetch('http://localhost:3000/api/users')
also make sure that you have CORS enabled on your backend
In case your want to redirect through webpack, your can try devServer.proxy as
devServer: {
inline: true,
contentBase: './dist',
port: 3001,
proxy: { "/api/**": { target: 'http://localhost:3000', secure: false } }
}
I know I'm a little late to the game here, but I'll leave it here for future reference.
To make the devServer proxy work as expected, you need to specify the HTTP Accepts header to be something else than "text/html". Do this with the init-object that fetch accepts as the second argument. A simple example:
fetch("/api/profile",{
headers:{
"accepts":"application/json"
}
})
.then(res => {
console.log(res);
return res.json();
})
.then(json => console.log(json) )
.catch( a => { console.log(a) });
The reason for this is that the WebPack Dev Server normally uses a context/namespace to differentiate between what to serve and what to forward. The create-react-app scripts do not extract a namespace from the proxy path in the package.json file. Instead the scripts has the opinionated default behaviour that any request using something else than HTTP GET will get forwarded. Also, anything using HTTP GET, but NOT text/html as the Accepts header will get forwarded.
The reasoning is because most React Apps are SPA (Single Page Applications) which use AJAX/Fetch to communicate with some API. API's normally use JSON or XML, but not text/html.
In the package.json
"proxy": {
"/api/users": {
"target": "http://localhost:3000"
}
},
I had the same problem using axios and was only able to get it working by using the complete hostname and enabling Cors.
const response = await axios.get('http://localhost/users/');
Install cors
npm i cors
Use cors
const express = require("express");
const request = require("request");
const cors = require("cors");
const app = express();
app.use(cors());
app.use("/", (req, res) => {
//...
});
app.listen(80, () => {
console.log("CORS-enabled web server listening on port 80");
});
Ref
The solution by user jellyfish-tom in https://github.com/webpack/webpack-dev-server/issues/793#issuecomment-316650146 worked for me.
devServer: {
proxy: {
"*": "http://[::1]:8081"
// "secure": false,
// "changeOrigin": true
}
},
Webpack Dev Server uses devServer.proxy config in your Webpack config to control proxying requests.

Resources