How to set "Access-Control-Allow-Credentials" in Firebase hosting - reactjs

I've set up a Node Express application in Firebase Functions, and a React app in Firebase Hosting (similar to the template I built, with the exception that I've configured session cookies in express and I'm using Passport JS).
My firebase.json contains the following:
{
"hosting": {
"public": "build",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "/api/**",
"function": "app"
}
]
}
}
If I make a direct fetch request from the React app to the API using the full absolute API url, everything works hunky dory:
try {
const url = 'https://us-central1-[my-app-name]-app.cloudfunctions.net/app/api/v1.0/auth/status';
const response = await fetch(url,
{
method: 'GET',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
}
);
json = await response.json();
console.log(json); // {"isAuthenticated":true}
} catch (error) {
throw error;
}
The API checks for the existence of a user in session and returns a response accordingly.
However, if I attempt to access the API through the rewrites proxy I've configured in the firebase.json file, using just the relative url /api/v1.0/auth/status then I still get a response (which confirms Firebase proxies the request appropriately), but the isAuthenticated flag is set to false, rather than true.
I assumed that this is because the proxied request is missing my valuable 'withCredentials' header, so I set this in the firebase.json file too:
{
"hosting": {
"public": "build",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "/api/**",
"function": "app"
}
],
"headers": [
{
"source": "/api/**",
"headers": [
{
"key": "Access-Control-Allow-Credentials",
"value": "true"
},
{
"key": "Content-Type",
"value": "application/json"
}
]
}
]
}
}
However, this appears to have no effect. Is there something I'm missing?
Edit: On closer inspection I believe it's because the cookie header isn't being passed onto the functions app when Firebase proxies the request (the 'credentials' headers shouldn't be required as it's not cross-origin), so I believe there must be some way to tell Firebase to include cookies in the rewrite/proxied request?

You can include this code in your express configuration.
This would solve cors problem
// allow-cors
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
next();
});

Related

Next.js CORS blocking using Vercel

This is not only a question, also an answer to my problem which took me a lot to resolve, I believe some devs will find it useful.
Let's start with some basic info:
The client app is using NextJs.
The server app is using NestJs deployed using the Vercel service.
Everything works fine in localhost, but when deployed the requests are always blocked by the CORS.
The server app deployment config (vercel.json) is the following:
{
"version": 2,
"builds": [
{
"src": "src/main.ts",
"use": "#vercel/node"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "src/main.ts",
"methods": [
"GET",
"POST",
"PUT",
"PATCH",
"DELETE"
]
}
]
}
In the server app, I enabled the CORS in the main.ts file:
app.enableCors({
allowedHeaders: '*',
origin: '*',
credentials: true,
});
Using any client I used (NextJs, Angular and even Insomnia and Postman) it shows that the CORS are set to accept any origin, still it's still blocking the requests.
The answer is not related to NextJs or NestJs but only to the vercel deployment config which needs to accept the OPTIONS type of HTTP request, the correct vercel.json file should look like this:
{
"version": 2,
"builds": [
{
"src": "src/main.ts",
"use": "#vercel/node"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "src/main.ts",
"methods": [
"GET",
"POST",
"PUT",
"PATCH",
"OPTIONS",
"DELETE"
]
}
]
}
Unfortunately, I didn't find this info in any docs I looked in, it only came up since HTTP requests of type OPTIONS are being used in the CORS mechanism.
Besides applying the CORS config for vercel, described here, you will also need your vercel lambda function to return a 200 status code for request using the OPTIONS method.
Please also that your vercel config needs to allow any custom header that you may be using when querying the endpoint. Lets say your GET request includes a header X-UserSession, such header should be allowed by CORS config as:
{ "key": "Access-Control-Allow-Headers", "value": "X-UserSession" }

Unable to upload images to AWS S3 from React App

I want to upload images to AWS S3 from my React app directly using aws-sdk.
I have setup my S3 bucket with the following policies -
Bucket Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:ListBucket",
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::<my-bucket-name>",
"arn:aws:s3:::<my-bucket-name>/*"
]
}
]
}
CORS Policy:
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"HEAD",
"GET",
"PUT",
"POST",
"DELETE"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": [
"ETag"
]
}
]
In my React app I'm using aws-sdk to upload images to S3. I have the following function -
export const uploadToS3 = (file: any) => {
AWS.config.update({
accessKeyId: S3_ACCESS_KEY,
secretAccessKey: S3_SECRET_KEY,
});
const bucket = new AWS.S3({
params: { Bucket: S3_BUCKET_NAME },
region: S3_REGION,
});
const params: any = {
ACL: "public-read",
Body: file,
Bucket: S3_BUCKET_NAME,
Key: file.name,
};
bucket
.putObject(params)
.on("httpUploadProgress", (e) => {
console.log(Math.round((e.loaded / e.total) * 100));
})
.send((error: any) => {
if (error) console.log(error);
});
};
I still get a CORS error -
Access to fetch at 'https://tribeone-nonceblox.s3-ap-south-1.amazonaws.com/' from origin 'http://localhost:3000' has been blocked by CORS policy: 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.
What should I do?

Axios call getting blocked due to CORS error

I have a react application with server side rendering using Express server . I want to make a POST request using axios but it is getting blocked with CORS error :
"Access to XMLHttpRequest at 'https://xyz/abc' from origin 'http://localhost:4249' 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.
I have tried after adding allow origin headers and cors module in my server call but that didn't work
import cors from "cors";
const server = express();
server.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Methods",
"OPTIONS, GET, POST, PUT, PATCH, DELETE"
);
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
next();
});
server.use(express.static("dist"));
server.use(cors());
Here is the axios request :
const headers = {
Accept: "application/json",
"x-ms-version": "2019-07-11",
Authorization: token,
"x-ms-date": date,
"x-ms-documentdb-isquery": true,
"Content-Type": "application/query+json",
"x-ms-documentdb-query-enablecrosspartition": "true",
"cache-control": "no-cache",
"Access-Control-Allow-Origin": "*",
};
useEffect(() => {
axios
.post(
"https://xyz/abc",
data,
{
headers: headers,
}
)
.then(function (response) {
setres(response.data);
})
.catch(function (error) {
console.log(error);
});
}, []);
Webpack Config:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
{
test: /\.(sass|css|scss)$/,
use: ["style-loader", "css-loader"],
},
],
},
};
I have encountered this problem before, and this is just what I did:
app.js (backend)
server.use(cors({
origin: '<YOUR_FRONTEND_URL_PATH>',
methods: 'GET,HEAD,POST,PUT,DELETE',
preflightContinue: false,
credentials: true
}));
axios request:
const resp = await axios.post(
apiUrl,
params,
{
withCredentials: true,
timeout: 10000
}
);

POST 403 forbidden CORS response when uploading an image to AWS S3 bucket ReactJS

Hello so I am trying to upload images to AWS S3 from my React application using an NPM package called react-s3. I am using an AWS Educate account and have setup the access and secret key correctly. After I select the image and console log the output it gives me a 403 forbidden error as follows.
POST https://shopkartimages.s3.amazonaws.com/ 403 (Forbidden)
Response {type: "cors",
url: "https://shopkartimages.s3.amazonaws.com/",
redirected: false,
status: 403, ok:
false, …}
body: (...)
bodyUsed: false
headers: Headers
__proto__: Headers
ok: false
redirected: false
status: 403
statusText: "Forbidden"
type: "cors"
url: "https://shopkartimages.s3.amazonaws.com/"
__proto__: Response
My bucket policy is as follows :
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3Permissions",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:Get*",
"s3:List*"
],
"Resource": "arn:aws:s3:::shopkartimages/*"
}
]
}
My CORS configuration in S3 is as follows:
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"PUT",
"POST",
"DELETE"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": [
"x-amz-server-side-encryption",
"x-amz-request-id",
"x-amz-id-2"
],
"MaxAgeSeconds": 3000
}
]
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3Permissions",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:Get*",
"s3:List*"
],
"Resource": "arn:aws:s3:::shopkartimages/*"
}
]
}
Your bucket policy only allow Get & List operations.
And uploading a file is a PUT operation.
I ran into this exact same problem with react-s3. I finally abandoned it in favor of AWS SDK which works fine for me (even in react). Here's what it took.
First install aws-sdk:
$ npm install aws-sdk
Then replace whatever you were doing with react-s3 with this:
import AWS from 'aws-sdk';
AWS.config.update({ region:region, credentials: new AWS.Credentials(secret_id, secret_access_key)});
var s3 = new AWS.S3({apiVersion: "2006-03-01", params: { Bucket: bucketName }});
let upload_params = {Bucket: bucketName, Key: file.path, Body: file};
let upload = new AWS.S3.ManagedUpload({params: upload_params});
let promise = upload.promise();
promise.then(
function(data){console.log("Successfully uploaded:", file.path);},
function(err){console.log("Failed to upload", file.name, "with error:", err.message);}
);
Here's my CORS stuff from S3 Bucket Permissions:
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"HEAD",
"GET",
"PUT",
"POST",
"DELETE"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": [
"ETag"
]
}
]
Also, public access is blocked and I'm not messing around with custom bucket policies.
Hope this helps someone else dealing with these CORS/S3/react nightmares.

app configuration appId is missing from Firebase Hosting reserved URL "/__/firebase/init.js" on localhost only

I recently started seeing an error on my localhost version of Firebase hosted website in Chrome:
Uncaught FirebaseError: Installations: Missing App configuration value: "appId" (installations/missing-app-config-values).
I'm not sure what has triggered this. I'm using the Firebase Hosting Reserved URL method to include the firebase config and it's had no issues until recently.
The config is loaded via <script src="/__/firebase/init.js"></script>.
This is the config file at that URL:
if (typeof firebase === 'undefined') throw new Error('hosting/init-error: Firebase SDK not detected. You must include it before /__/firebase/init.js');
var firebaseConfig = {
"projectId": "remotesoc...",
"databaseURL": "https://remotesoc...firebaseio.com",
"storageBucket": "remotesoc...appspot.com",
"locationId": "us-central",
"apiKey": "AIzaSyC3K7HT9- ... ",
"authDomain": "remotesoc...firebaseapp.com",
"messagingSenderId": "43697..."
};
if (firebaseConfig) {
firebase.initializeApp(firebaseConfig);
}
As you can see the "appId" value is missing. I'm almost certain this used to be there.
I've built and deployed the app multiple times which is listed as the way to ensure that Firebase has the correct config values for the project.
The app is definitely using the correct project, and has a reference to the hosting site in the firebase.json
{
"database": {
"rules": "database.rules.json"
},
"functions": {
"source": "functions"
},
"hosting": {
"site": "remotesoc...",
"public": "build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
},
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
}
}
I've run >firebase init several times in an attempt to reset the CLI.
The deployed site has no missing config vars and is working perfectly this is only on localhost.
I could revert to using an included config file, however that negates the benefits of having the reserved URLs inject the values for you.
Any help would be appreciated.

Resources