Reputation: 375
I built a Firebase HTTP Event function with Node and Express. The function is working, but when I invoke the function on the client side I get 403 Forbidden
. The first time I invoked the function I was asked to sign in with a Google account. I signed in with the same account I use for Firebase, but when I invoked the function I got:
I looked at the use roles on Google cloud platform and the permission to invoke the function is set to allUsers
. I signed out and back in again in the Firebase CLI.
Here is the index.js
in the functions folder:
const functions = require('firebase-functions');
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const port = process.env.port || 5600
const nodemailer = require('nodemailer');
app.use(express.static('Public'));
app.use(bodyParser.urlencoded({ extended: true }));
const urlencodedParser = bodyParser.urlencoded({extended: true});
app.post("/api/user", urlencodedParser, (req, res) => {
res.sendFile('../Public/bedankt.html', {root: __dirname})
const persGegevens = req.body
const string = JSON.stringify(persGegevens, (key, value) => {
if (typeof value === "string"){
return value.toUpperCase();
} else {
return value
}
}, 1);
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: '[email protected]',
pass: 'Gietvloermakers2020!'
}
});
var mailOptions = {
from: '[email protected]',
to: '[email protected]',
subject: 'Nieuwe bestelling op Gietvloermakers',
html: string
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
});
});
exports.app1 = functions.https.onRequest(app);
app.listen(port);
console.log(port);
Here is the html:
<form id="controlleer-form" action="/api/user" method="post" enctype="application/x-www-form-urlencoded">
<div class="controleer-div">
<h2>Uw bestelling</h2>
<p>Aantal m2</p>
<input class="controle-input" type="text" name="aantalM2" id="aantalM2" readonly>
<p>Kleur</p>
<input class="controle-input" type="text" name="kleur" id="kleur" readonly>
<p>Assistentie</p>
<input class="controle-input" type="text" name="assistentie" id="assistentie" readonly>
<p>Gereedschappen</p>
<input class="controle-input" type="text" name="gereedschappen" id="gereedschappen" readonly>
<p>Totale prijs</p>
<input class="controle-input" type="text" name="totale-prijs" id="totale-prijs" readonly>
<a href="bestellen.html"><p id="andere-kleur">Bestelling aanpassen</p></a>
</div>
<div class="controleer-div">
<h2>Uw gegevens</h2>
<p>Voornaam</p>
<input type="text" name="voornaam" placeholder="Voornaam">
<p>Achternaam</p>
<input type="text" name="Achternaam" placeholder="Achternaam">
<p>Straatnaam en huisnummer</p>
<input type="text" name="Achternaam" placeholder="Straatnaam en huisnummer">
<p>Postcode</p>
<input type="text" name="Achternaam" placeholder="Postcode">
<p>Telefoonnummer</p>
<input type="tel" name="telefoonnummer" placeholder="Telefoonnummer">
<p>Emailadres</p>
<input type="email" name="email" placeholder="Emailadres"><br>
<input id="verzenden" type="submit">
</div>
</form>
Here is the firebase.json:
{
"hosting": {
"public": "Public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [{
"source": "**",
"function": "app1"
}]
}
}
I tried but I exhausted all possible solutions I've found online so far.
Upvotes: 30
Views: 26748
Reputation: 1278
Solution for me:
Delete gcf-artifacts
in https://console.cloud.google.com/artifacts/browse/{projectName}
Delete functions in https://console.firebase.google.com/u/0/project/{projectName}/functions
Deploy Firebase functions
Upvotes: 5
Reputation: 1454
If you are getting 403 forbidden error like below
Error: Forbidden Your client does not have permission to get URL /api/test from this server.
Please follow below steps to grant access to all users. Basically this is to allow unauthenticated clients to access your api endpoint.
That's it, now test your api.
Upvotes: 26
Reputation: 506
Had the same problem (was asked to login with my Google Account, then denied access). It turned out that functions do currently not work outside the default region. In my case, I had to make a change here:
exports.app = functions
.region('europe-west6') // does not work, delete this line
.https.onRequest(app);
Upvotes: 4
Reputation: 626
This has to do with permission access to your cloud functions http requests and cloud function events, you need to edit your cloud function IAM permission.
Upvotes: 8
Reputation: 1023
I encountered this recently. It turns out that as of January 15, 2020 new functions require authentication by default.
See the docs here for details.
The solution was to manually add the Cloud Functions Invoker
permission to the allUsers
user in the Cloud Functions page in the Google Cloud Console.
Upvotes: 65
Reputation: 26171
Your code exports the express application as the Cloud Function app1
on this line:
exports.app1 = functions.https.onRequest(app);
In your screenshot, you have tried to access the non-existent app
Cloud Function instead resulting in the 403 Forbidden
response.
This means the correct URL to call from your client is
http://us-central1-gietvloermakers.cloudfunctions.net/app1/api/user
^^^^
(or you could change the name of the export to app
)
Having a closer look at your source code, you should also remove the following lines. If you wanted to test your code you would instead use firebase serve
.
const port = process.env.port || 5600
/* ... */
app.listen(port);
On the following lines, you also inject the body parser twice.
app.use(bodyParser.urlencoded({ extended: true })); // use this
const urlencodedParser = bodyParser.urlencoded({extended: true}); // or this, not both
app.post("/api/user", urlencodedParser, ...
In your code, you also have:
app.post("/api/user", urlencodedParser, (req, res) => {
res.sendFile('../Public/bedankt.html', {root: __dirname})
/* do some other stuff */
})
This is invalid for a Cloud Function, because as soon as the Cloud Function handler (your code) calls end()
, redirect()
or send()
, the Cloud Function is allowed to be terminated at any time which means that your email may never be sent. To fix this you need to send the file last.
app.post("/api/user", urlencodedParser, (req, res) => {
/* do some other stuff */
res.sendFile('../Public/bedankt.html', {root: __dirname})
});
My last observation, is that the error may be caused by the folder Public
not existing on the server. Based on your sendFile
call, you are expecting that the folder "Public" is available to your deployed function but as it is not inside the functions
folder, it will not be deployed with your code.
res.sendFile('../Public/bedankt.html', {root: __dirname})
As this file would also be accessible at your-domain.com/bedankt.html
, we'll redirect to it. If you wanted to send the HTML content of this file instead, move it inside your deployed functions directory.
res.redirect('/bedankt.html')
Because you appear to be trying to use your express function behind Firebase hosting, we can trim your index.js
file to the following:
const functions = require('firebase-functions');
const express = require('express');
const bodyParser = require('body-parser');
const nodemailer = require('nodemailer');
const apiApp = express();
apiApp.use(bodyParser.urlencoded({ extended: true }));
apiApp.post("/api/user", (req, res) => {
const persGegevens = req.body
const string = JSON.stringify(persGegevens, (key, value) => {
if (typeof value === "string"){
return value.toUpperCase();
} else {
return value
}
}, 1);
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: '[email protected]',
pass: 'Gietvloermakers2020!'
}
});
var mailOptions = {
from: '[email protected]',
to: '[email protected]',
subject: 'Nieuwe bestelling op Gietvloermakers',
html: string
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
res.redirect('/bedankt.html?success=0');
} else {
console.log('Email sent: ' + info.response);
res.redirect('/bedankt.html?success=1');
}
});
});
// note rename to api
exports.api = functions.https.onRequest(apiApp);
which requires updating your firebase.json
file to:
{
"hosting": {
"public": "Public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [{
"source": "/api/**",
"function": "api"
}]
}
}
This configuration will first attempt to find a matching file in your Public
directory. If it can't find a match, it will check if the requested path starts with /api
and if so, launch your Cloud Function. If it still can't find a match, it will show your 404 page (or the built in one if it doesn't exist).
Upvotes: 2