I am using sessionStorage and also firebase authentication for email and password.
In my server.js I am wondering how can I make it so that if a user is not logged in they cannot access a route, or rather be redirected to the login route instead.
The firebase sdk I am using is only usable via the client side. Is there any documentation to help that I have been unable to find.
Please let me know if I need to clarify my question more and I will do my best to do so.
Here is my server.js:
const express = require('express');
const admin = require('firebase-admin');
const bcrypt = require('bcrypt');
const path = require('path');
let serviceAccount = require("./1234.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
let staticPath = path.join(__dirname,"public");
const app = express();
app.use(express.static(staticPath));
app.use(express.json());
app.get('/login', (req,res) => {
res.sendFile(path.join(staticPath, "form.html"));
})
app.get('/seller', (req,res) => {
// if(!user) res.redirect('/login');
res.sendFile(path.join(staticPath, "seller.html"));
})
Edit:
So far the only thing that helps me with this is by creating a seller.js
and inserting this code here, but I am unsure of if this method is safe or if there is a way to hide this from being manipulated:
body = document.getElementsByTagName('BODY')[0];
user = JSON.parse(sessionStorage.user);
if(user && user.seller){
console.log('Allow Access')
} else{
console.log('Deny')
body.innerHTML = `
<div class="sticky" id="nav"></div>
<div style="padding:300px">
<center>You do not have permission to view this page.</center>
</div>
<div id="footer"></div>
`;
}
You can use the firebase-admin package to verify the token on the server. If the verification passes, you can continue with route logic. To make things simple, you could wire up a middleware in Express that verifies the token, rather than repeating the calls for authenticated routes.
Relevant documentation: https://firebase.google.com/docs/auth/admin/verify-id-tokens#web
Also, you should not rely on client-side scripting to verify a user's authentication status if you're trying to restrict resources on a server. It would be trivial for someone to find the endpoints being used, and, assuming there's no logic on the server to verify the user, they could potentially retrieve sensitive information.
here is the thing. I have my app that has separated back-end and front-end into two different project (back-end with Node-JS, and front-end with Vue-JS). I got a home page with a button that should redirect the user to the authentication server to log in.
So i made an "on-click" function in my homePage.vue that will consume a function from my api.
I've read a lot of documentation and seen some examples. But to be honest, during those past weeks, even if I tried, I still do understand nothing about how authentication works.
You can see below the js file i wrote inspired by examples that i found:
auth.js
const express = require('express');
const { auth } = require('express-openid-connect');
const app = express();
app.use(
auth({
issuerBaseURL: 'myDomainUrl',
baseURL: 'http://localhost:8080',
clientID: 'myClient_ID',
secret: 'aaaaaaaaaaaaaaaaaaaa',
idpLogout: true,
authRequired: false,
})
);
module.exports = app;
There is also the route with the function that I try to implement:
auth.route.js
module.exports = app => {
var router = require("express").Router();
router.get('', location.replace('myDomainUrl'));
app.use('/api/login', router);
};
I don't know if it's important but my back-end runs on the port 4000 and my front-end runs on the port 8080.
If someone can explain me how I can do to make my authentication work and what I have to change, it would be great.
Thanks you in advance, I hope I was clear enough about my problem. If not, do not hesitate to ask me what was not clear.
I am using axios to call the firebase cloud functions I have created with express/firebase. I realized even without using and without importing the firebase and without initializeApp(firebaseConfig) in my frontend, I was able to call my cloud functions with the axios with the following way:
axios.get('https://us-central1...')
How I create cloud functions:
index.js
module.exports ={
...require('./controllers/foo')
}
foo.js
const functions = require('firebase-functions');
const express = require('express');
const cors = require('cors');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
const app = express();
//Middleware
app.use(express.json());
app.use(cors({ origin: true}));
app.get('/', async (req, res) => {
// Function goes in here
});
app.post('/', async (req, res) => {
// Function goes in here
});
app.put('/:id', async (req, res) => {
// Function goes in here
});
app.delete('/:id', async (req, res) => {
// Function goes in here
});
exports.foo = functions.https.onRequest(app);
Is this a normal behavior or is it due to way of working of express (cors as middleware) or axios somehow? Or do I just have to add auth and firestore rules into my database? Whatever the reason is then what is the meaning of the firebaseConfig information that firebase provides for us?
PS: Just to play around I have tried to add firestore rules. I have added the following rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
}
}
Even though in rules playground I was not able to retrieve anything, from my application I still got the query I wanted and I don't know why.
Yes that is absolutely normal. The HTTP Functions are made so you can integrate you Firebase Project with any (absolutely any) other language or platform by using HTTP requests as the trigger name shows. As you already do, you can even implement express apps behind those requests.
With those you gave full power and responsebility what goes trough them and with that comes also the need for you to know who is calling your HTTP requests. if you want to secure them you can use the link from the comment and check how to make Authorized HTTP Ednpoinds.
If you want to call those just from your App Frontend I would recommend to use Callable Firebse Functions because those will work only from your app and also provide the data of the user who called them in the context of your cloud function triggered.
I'm running MongoDB Atlas on node express and I got this error when I tested with postman.
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
const uri = process.env.ATLAS_URI;
mongoose.connect(uri, { useNewUrlParser: true, useCreateIndex: true }
);
const connection = mongoose.connection;
connection.once('open', () => {
console.log("MongoDB database connection established successfully");
})
const exercisesRouter = require('./routes/exercises');
const usersRouter = require('./routes/users');
app.use('/exercises', exercisesRouter);
app.use('/users', usersRouter);
app.listen(port, () => {
console.log(`Server is running on port: ${port}`);
});
This is my .env, I'm guessing the problem might be here too, Kindly help:
ATLAS_URI=mongodb+srv://userone:useronepassword1234#cluster0.swye5.mongodb.net/<dbname>?retryWrites=true&w=majority
In my case, I had to go to Atlas, and reset my whitelisted IP to the correct address.
Then I restarted my local server and tried posting again on postman... And it worked!
First replace <dbname> with your actual DB name, if not created,
create one.
Then create collection as required on the Atlas UI itself.
In the Network Access, click on ADD IP ADDRESS and select "allow
access from anywhere".
Rewrite your code this way:
mongoose
.connect(process.env.MONGO_PROD_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true, })
.then(() => console.log("Database connected!"))
.catch(err => console.log(err));
Now your DB should be connected and working fine.
If still not resolved, check this link.
I was facing the same issue. It is resolved. I think you might have not allowed network access to everyone in Atlas: MongoDB. Do it will resolve the issue.
Check your Network Access IP list in MongoDB Cloud.You will only be able to connect to your cluster from the list of IP Addresses
Ensure that your IP Address Setting is Active
Check if you didn't forgot to set password in connection string.
Step 1:
Go to your Atlas account and open your project.
Step 2:
In the left menu, navigate to Network Access section:
Step 3:
Add your IP Address so only you would be able to connect to your cluster. You can also add 0.0.0.0/0 and it will allow access from everywhere.
I also faced the same error. In my case, the error was coming up because useFindAndModify was set to false in mongoose connection
Code with error
mongoose.connect(dbUrl, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false
}, () => {
console.log('connected to database myDb ;)')
})
Working Code
mongoose.connect(dbUrl, {
useNewUrlParser: true,
useUnifiedTopology: true
}, () => {
console.log('connected to database myDb ;)')
})
Change your MongoDB database-user password. It should be alphanumeric with no special characters. Nothing worked out for me, but, changing the password did.
This error can be caused by typos in user properties. (I was trying to set "Email" instead of what was defined on my user model: "email").
If you are seeing this using Typescript ensure you are importing the connect function from mongoose and use that to connect.
import { connect } from "mongoose";
connect(...).then()
It's kind of late but probably because of this line useCreateIndex: true it's not working. It seems in mongoDB version 5. this is not supported anymore. Rewrite like the answer of manoj_mi5
mongoose
.connect(process.env.MONGODB_URL, {
useNewUrlParser: true,
useUnifiedTopology: true})
.then(() => console.log("Database connected!"))
.catch(err => console.log(err));
to check if there are more errors.
In Mongoose version 6 and above don't require those
{ useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false
}
so just delete it.
And if you still see app crashed - waiting for file changes before starting...
just save it one more time and it will work
"error:MongooseError: Operation users.insertOne() buffering timed out after 10000ms"
mongoose.connect(process.env.MONGO_URL, {
useNewURLParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
},6000000)
.then(console.log("connected to server"))
.catch((err) => console.log(err));
add time like 6000000 after options
In Latest version of mongoose.
we don't require this object.
{ useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false
}
But if you are dealing with older versions of mongoose then definetly you need it.
Also in your mongodb network address add this address 0.0.0.0/0 in place of your ip address.
in this line of code, ATLAS_URI=mongodb+srv://userone:useronepassword1234#cluster0.swye5.mongodb.net/?retryWrites=true&w=majority
make sure you write actual database name without < > symbols. You have to create your database first in Mongo Atlas.
To solve this, I created a function in index.js, where I asynchronically connecting to my Database and then starting the server, because mongoose doesn't wait for db connection it executes everything on spot, for me that was the problem.
async function start() {
try {
//Database Connect
await mongoose.connect(
process.env.DB_CONNECTION,
{
useNewUrlParser: true,
useUnifiedTopology: true,
},
() => {
console.log("Database Connected");
}
);
app.listen(3000, () => {
console.log("Server is running on port 3000 ...");
});
} catch (error) {
console.error(error);
}
}
Double-check some of the things listed below.
Check the username in the database and make sure you have used the same username in the application.
Check the password.
Check the URl of the Database (if any letter is different or changed).
Check the network access if your IP is not present in the IP Access list of the network access section. (Add your IP address if not present there or create add a new IP address).
Add a new IP Address by:
-> click on ADD IP ADDRESS -> click ALLOW ACCESS FROM ANYWHERE -> click confirm.
Also check, if you are on your company's network and added that IP Address to the IP Access list, you might face the same issue, if so, then try switching to your mobile internet or some other than the company's network.
Now, run the application.
Checking the above-all points and making them correct has fixed the issue for me. Hope the same for you. :)
I had this similar issue of recent and what I think gave the error was
require('dotenv').config()
Changed it to this
require('dotenv/config')
or
require('dotenv')
after importing the package, below call the config function
dotenv.config()
I experienced the same issue. But my MongoDB is running locally on my machine. I had forgotten to open the connection before sending my query to the database.
So, I added the code to open and close the connection, and it worked.
try{
await mongoose.connect(uri);
// My mongoose database request code
}
finally{
await mongoose.connection.close();
}
I had the same issue, I removed useCreateIndex: true, and used only:
{
useNewUrlParser: true
}
So I faced the same error but for my case, the reason was because I used the mongoose.createConnection(...) method instead of the mongoose.connect(...) method.
The relevant difference between both of them is that, with mongoose.connect, the created connection is automatically linked with your mongoose models when you do mongoose.model('User', userSchema). However, with mongoose.createConnection, you need to link it with your schema directly like so:
import * as mongoose from 'mongoose'
import { userSchema } from '../path/to/your/schema'
const dbURL = 'mongodb://localhost:27017'
const db = mongoose.createConnection(dbURL, {dbName: 'my-db-name'})
export const User = db.model('User', userSchema)
The important bit is that on the last line, we use the created connection instance db to create our model, rather than using mongoose.model directly.
Keep in mind, this solution is only relevant when you use mongoose.createConnection instead of mongoose.connect
Creating New database user Worked for me.
I turned off my mobile hotspot and back on and it worked.
I'm trying to create a firebase cloud function. So I would to run my firebase cloud function locally.
But it do not work how to setup authentication.
I have installed firebase tools : https://firebase.google.com/docs/functions/local-emulator
I've runned the command firebase login, so now I'm logged.
Then I've created my json key with this tutorial : https://cloud.google.com/docs/authentication/getting-started
Now if I type echo $GOOGLE_APPLICATION_CREDENTIALS the result is /home/$USER/.google/****.json which contain
"project_id","private_key_id","private_key","client_email", "client_id", "auth_uri", "token_uri", "auth_provider_x509_cert_url", "client_x509_cert_url"
Also I've tried to install the full google cloud sdk and I runned : gcloud auth application-default login but no success.
Npm package versions :
"firebase-functions":"3.0.2"
"firebase-admin": "8.2.0"
I think I've provided enought information but feel free to ask me more if you want.
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const express = require("express");
const app = express();
app.get("/", async (req, res) => {
admin.firestore().collection('something').get().then((collection) =>
return res.send({"count": collection.docs.length, "status": 200});
});
exports.exports = functions.https.onRequest(app);
the code is not important, the most important thing is that even I've done all theses steps, when I emulate my firebase locally with firebase serve and I trigger a function, I have this error :
Error: The incoming JSON object does not contain a client_email field
I can ensure you the json file contains client_email field.
Can you help me to authenticate with google ?
Thanks for your help.
I had a similar problem. It's likely a bug in version 7.0.2 of firebase-tools. I rolled back to version 7.0.0 and it works now.
So the temporary solution is:
npm i firebase-tools#7.0.0 -g
In short:
admin.initializeApp({ credential: admin.credential.applicationDefault() });
See docs for admin.credential.applicationDefault()
Update: Note that this is only recommended for testing/experimenting:
This strategy is useful when testing and experimenting, but can make
it hard to tell which credentials your application is using. We
recommend explicitly specifying which credentials the application
should use, ... Source
A little more info
I had the same when trying to call a firebase function locally which tries to update some documents in firestore database in batch. (Didn't test without batch).
To start calling firebase functions locally, I use:
firebase function:shell
As you probably know, this lists the available functions for your project.
I called my function and got the following error callstack:
Unhandled error Error: The incoming JSON object does not contain a client_email field
> at JWT.fromJSON (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\google-auth-library\build\src\auth\jwtclient.js:165:19)
> at GoogleAuth.fromJSON (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\google-auth-library\build\src\auth\googleauth.js:294:16)
> at GoogleAuth.getClient (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\google-auth-library\build\src\auth\googleauth.js:476:52)
> at GrpcClient._getCredentials (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\google-gax\build\src\grpc.js:107:40)
> at GrpcClient.createStub (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\google-gax\build\src\grpc.js:223:34)
> at new FirestoreClient (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\#google-cloud\firestore\build\src\v1\firestore_client.js:128:39)
> at ClientPool.Firestore._clientPool.pool_1.ClientPool [as clientFactory] (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\#google-cloud\firestore\build\src\index.js:315:26)
> at ClientPool.acquire (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\#google-cloud\firestore\build\src\pool.js:61:35)
> at ClientPool.run (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\#google-cloud\firestore\build\src\pool.js:114:29)
> at Firestore.readStream (D:\thdk\Projects\timesheets\functions\node_modules\firebase-admin\node_modules\#google-cloud\firestore\build\src\index.js:995:26)
RESPONSE RECEIVED FROM FUNCTION: 500, {
"error": {
"status": "INTERNAL",
"message": "INTERNAL"
}
}
I was running my function locally using the command line:
firebase functions:shell
I was using this code:
// Reference report in Firestore
const db = admin.firestore();
admin.initializeApp();
export const performMyCallableFirebaseFunction = (db, { from, to }) => {
return db.collection("collectionName").where("prop", "==", from).limit(500).get().then(snapshot => {
if (snapshot.empty) return new Promise(resolve => resolve(`No docs found with prop: ${from}`));
const batch = db.batch();
snapshot.forEach(doc => batch.update(doc.ref, { prop: to }));
return batch.commit();
});
};
exports.myCallableFirebaseFunction = functions.https.onCall(data => performMyCallableFirebaseFunction(db, data.from, data.to));
I changed the line
admin.initializeApp();
to
admin.initializeApp({ credential: admin.credential.applicationDefault() });
and now I was able to call my function locally using:
firebase functions:shell
firebase > myCallableFirebaseFunction({from: "foo", to: "bar"})
See docs for admin.credential.applicationDefault()
You probably need to set up the Firebase Admin SDK to use the Firebase emulator. You can do it by passing a credential property when calling the admin.initializeApp() method:
const serviceAccount = require('../serviceAccount.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
You can download your service account JSON file in the Firebase console:
Click on the "settings" icon;
Go to "Users and permissions";
Click on the link where it says "N service accounts also have access to this project";
Click on the "Generate new private key" button.
Here is how I've solved the problem after struggling couple of hours:
Short answer:
Create Firebase-adminsdk key
How to do it:
Go to Google-cloud-platform > Service accounts https://console.cloud.google.com/iam-admin/serviceaccounts/
Select your project
Select your firebase-admin-sdk looks like firebase-adminsdk-u4k3i#example..
Enable edit mode
Create key and select JSON
You get the option to download a .json. Which has ProjectID, PrivateKey and ClientEmail in it
use the information like this where you initialize your app:
// Providing a service account object inline
admin.initializeApp({
credential: admin.credential.cert({
projectId: "<PROJECT_ID>",
clientEmail: "foo#<PROJECT_ID>.iam.gserviceaccount.com",
privateKey: "-----BEGIN PRIVATE KEY-----<KEY>-----END PRIVATE KEY-----\n"
})
});
Once you have created a Firebase project, you can initialize the SDK with an authorization strategy that combines your service account file together with Google Application Default Credentials.
To authenticate a service account and authorize it to access Firebase services, you must generate a private key file in JSON format.
To generate a private key file for your service account:
In the Firebase console, open Settings > Service Accounts.
Click Generate New Private Key, then confirm by clicking Generate Key.
Securely store the JSON file containing the key.
Set the environment variable GOOGLE_APPLICATION_CREDENTIALS to the file path of the JSON file that contains your service account key. This variable only applies to your current shell session, so if you open a new session, set the variable again.
$env:GOOGLE_APPLICATION_CREDENTIALS="C:\Users\username\Downloads\service-account-file.json"
https://firebase.google.com/docs/admin/setup?authuser=3
I was getting this error when running firebase emulators:start.
As per the investigation from this bug: https://github.com/firebase/firebase-tools/issues/1451, it seems that this is an issue with referencing the app directly instead of via the admin module.
i.e. this causes the error:
const app = admin.initializeApp();
const firestore = app.firestore();
but this does not:
admin.initializeApp();
const firestore = admin.firestore();
However for the original question, you're using admin.firestore() so that wouldn't be the problem. It seems that admin.initializeApp() is never called. Perhaps that could be the cause of your issue?