I was building a project on node.js recently, I came across this bug of user authentication. The problem is that after I log in from one account, if at the same time I refresh the home page of the website from another device, it redirects to the dashboard of the first user account. In other words, a single user is getting logged in on all the devices over the network. If suppose I don't refresh the page, and I log in normally on the application, it works fine. This problem is happening, both on the localhost as well as after hosting it on Heroku. Technologies used- node.js for handling back-end views and URLs. HTML, CSS, JavaScript for the front-end. Firebase for the database and authentication. Here is the code for the login part-
const express = require("express");
const path = require("path");
//Create Router Object.
const router = express.Router();
//Main Login Page (GET)
router.get("/", (request, response) => {
response.sendFile(path.resolve("./views/html/login.html"));
});
//Main Login Page (POST)
router.post("/", (request, response) => {
let email = request.body.email;
let password = request.body.password;
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);
firebase.auth().signInWithEmailAndPassword(email, password)
.then(r => {
let user = firebase.auth().currentUser;
if (user.emailVerified)
response.redirect('/dashboard');
else
response.send("<h1>Please Verify your email address.</h1>");
})
.catch(error => {
console.log(error);
response.send("<h1>Invalid Credentials</h1>");
});
});
Please can someone help me by resolve this bug in my project?
Calling firebase.auth().signInWithEmailAndPassword(email, password) signs the user in on the location wherever you call this code. Since you run this in an express.js app in Node.js, the user is signed in to that Node.js process, until you sign them out again. This is why the Firebase Authentication SDK you are using is only meant to be used in client-side applications, where this behavior is working as intended.
When integrating Firebase Authentication in a server-side process, you should use the Firebase Admin SDK. This Admin SDK has no concept of a currently logged in user, and no signInWithEmailAndPassword. Instead, if you need to know the identity of the user accessing the server-side code, you'll:
Sign the user in in their client-side app with the Firebase Authentication SDK.
Get the ID token for that user on the client, and pass that with your call to the server.
On the server, verify that ID token, to ensure the user is who they claim they are.
Then use the information on the user's identity to determine if they're authorized to access the information.
For more on this, see the Firebase documentation on verifying ID tokens.
Related
I'm building a web app that is being used on top of microservices architecture.
Using node/express js I have implemented auth service and products service both are listening on different ports like
http://localhost:8001 for authentication service
http://localhost:8002 for products service.
Kong Gateway used to authenticate and connect the microservices with jwt. Implemented passport-jwt and passport-local strategy to authenticate the users from client side using post calls.
Finally I have implemented the google auth on server side using passport-google strategy in this below URL
http://localhost:8001/auth/google -> it directs me to google auth consent screen after sign in it is redirecting to below Url
http://localhost:8001/auth/google/callback with token. it works fine at server end.
async googlecallback(req, res, next){
passport.authenticate('google', {
session: false,
}, (err, user, message) => {
if (!user) {
return next(new UnAuthorizedException(message))
}
const token = user.generateToken()
user = UserTransformer.transform(user)
user.token = token
this.Response(res, user, message) // sending response to client using custom method
})(req, res)
}
. When I come to authenticate the user from angular app client side. I'm unable to proceed further. just struggling here. :-(
How can I authenticate the user when they click google sign in button in angular 10 on client side?
My front end app Url like http://localhost:4002/account/login
Tried to use window.open("http://localhost:8001/auth/google","_blank") method, not working as expected.
res.setHeader('x-code', 'jwthere'); header method. Also tried to pass the JWT token with URL parameter. but both seems unsecure.
http://localhost:4002/account/login?token=7wF8bit5W1Pfi5Glt1X8H0YQu8BN7OeNRcX1zbj3AGpUHaYSxLlNIjHpzuw
security is the major concern here. I want the google sign in like khanacademy social login
https://www.khanacademy.org
In the html file that I have for the sign-in page, I perform the authentication using Firebase and on successful authentication, I redirect the given user to the homepage. When I call firebase.auth().currentUser in the express file, I use for rendering and routing pages, I get undefined or null for the current user.
Can anyone help me understand what the issue might be?
This is how I perform the authentication:
firebase
.auth()
.signInWithEmailAndPassword(temail, tpass)
.then(function(firebaseUser) {
window.location.href = "http://localhost:5000/homepage";
})
.catch(function(error) {
window.alert("incorrect pass");
});
This is the code that I have in my express file:
app.get("/homepage", (req, res) => {
var user = firebase.auth().currentUser;
console.log("USER IS " + user);
res.render("menu", { title: "Welcome" });
});
Backend code doesn't have a sense of "current user". When you sign in on the frontend, the current user is only known on that client. It isn't known on the backend. If you want the backend to know which user is signed in, the client will have to send an ID token to the backend for it to verify. The documentation for the Firebase Admin SDK is used for that on the backend. The client must send the ID token to in the request to your route, and the code handling that route must verify the token in order to know the user that made the request. From the documentation:
If your Firebase client app communicates with a custom backend server, you might need to identify the currently signed-in user on that server. To do so securely, after a successful sign-in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity and authenticity of the ID token and retrieve the uid from it. You can use the uid transmitted in this way to securely identify the currently signed-in user on your server.
When the user lands on a new page, Firebase automatically restores their previous authentication state. But to do so, it may have to contact the server, which means that it may take a few moments. While Firebase is restoring the state, auth().currentUser will be null.
To ensure you get the correct authentication state, you should use an authentication state listener, as shown in the documentation on getting the current user:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
Problem: The client is using MemberPress/Wordpress as their main platform. The users are being managed there. I built a React app to coexist with the Wordpress platform.
What I have done: Memberpress has webhooks. So when a user logs into WordPress I hook into that action with a node server that inserts the user into a custom database and I generate a token so that the user can access the nodes the user owns within my infrastructure.
module.exports = (req, res) => {
const { id, email } = req.body.data;
request(
"https://api.graph.cool/simple/v1/73289237283967",
mutation,
{
wpId: id,
email
}
).then(data => {
res.json({ status: "success" });
});
};
The above gets triggered every time a user logs in. This executes a graphQl mutation that has a custom resolver that checks if the user with that wpId exists. If not it will create a new user. Then it generates a node token https://github.com/prisma-archive/graphcool-templates/blob/master/auth/email-password/src/signup.ts and sends back in the response.
Once I obtain the token I can make requests on the user.
Problem: I am not sure how I can connect the two. I have to redirect the user to the wordpress login page. The login action triggers the event, and a token will be generated. I am not sure how I can store that token in an efficient way that can then be sent back to my app.
My thought would be to write a small plugin inside WordPress to handle the response of the hook and store the token in local storage within WordPress. Then whenever the user clicks to go to the app I can put the token in the URL (feels slightly weird and cant directly go to the URL). I am out of any other thoughts and would appreciate a nudge in the right direction.
Background:
I have written a Node.JS script that successfully connects to the Facebook Graph API through my facebook app. I can read data when I give it an oauth access_token, I want this script to run on my server every night to store some data. I have done a lot of research of both the facebook api, oauth and similar questions on stack overflow. I am searching the /search/?type=event&q=query endpoint
Problem:
However, Facebook returns a 60 day access_token through the oauth2 login process that required me to create an express server that simply initiates the oauth2 process, allows the user to login, and receives the access_token code and I am storing it.
I want the script to save data so that my server can provide access to updated data every day. I don't want to have to remember to login to generate the key once every 60 days.
Question:
Is there anyway to receive a oauth2 access_token without setting up an http or express server?
More importantly, how do I get the access_token without manually having to running that server every ~60 days.
Code:
The Module I am using requires the access_token and client_secret
fs.readFile('./facebookAuthServer/oauth.txt', function read(err, data) {
if (err) {
throw err;
}
fbNode.setAuthorization({token: data, clientSecret: authSettings.clientSecret});
// Use the auth for next call
fbNode.fetchItems(displayItems);
});
Is there some way to spoof headers? or could I use a short lived access token and refresh it? Anyway to refresh a 60 day token? Has anyone created a server side implementation of Oauth2 that does not require visiting the FB login more than the first time?
Here is how you can refresh your own access token using Grant and request.
First you need an OAuth client server up and running:
var express = require('express')
var session = require('express-session')
var Grant = require('grant-express')
var grant = new Grant({
server:{host:'dummy.com:3000', protocol:'http'},
facebook:{
key:'[APP_ID]',
secret:'[APP_SECRET]',
scope:['user_about_me','user_birthday'],
callback:'/callback'
}
})
var app = express()
app.use(session({secret:'very secret'}))
app.use(grant)
app.get('/callback', function (req, res) {
res.end(JSON.stringify(req.query))
})
app.listen(3000, function () {
console.log('Oh Hi', 3000)
})
Next you need an HTTP client that will simulate the browser request:
var request = require('request')
request.get({
uri:'http://dummy.com:3000/connect/facebook',
headers:{
'user-agent':'Mozilla/5.0 ...',
cookie:'datr=...; lu=...; p=-2; c_user=...; fr=...; xs=...; ...'
},
jar:request.jar(),
json:true
}, function (err, res, body) {
if (err) console.log(err)
console.log(body)
})
How you use it:
Register OAuth app on Facebook and set your Site URL (I'm assuming http://dummy.com:3000)
Add 127.0.0.1 dummy.com to your hosts file
Configure and start the server from above
Navigate to dummy.com:3000 in your browser
Open up the Developer Tools and navigate to the Network tab, make sure Preserve log is checked
Navigate to http://dummy.com:3000/connect/facebook and authenticate as usual
Take a look at the authorize request in the Network tab and copy the relevant headers to the HTTP client example (the user-agent and the cookie)
Run the HTTP client code (that's the code you are going to execute from time to time, the server should be running as well)
Resources:
Introduction about how to use Grant
Grant documentation
Request documentation - see the options section
Module:
I wrapped the above code into a module https://github.com/simov/facebook-refresh-token
Start with reading the docs, it's all described there, instead of guessing:
https://developers.facebook.com/docs/facebook-login/access-tokens
https://developers.facebook.com/docs/facebook-login/access-tokens#refreshtokens
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.4#login
It's somehow a pity that you don't write WHAT you want to query via the Graph API, because depending on that you could either use a non-expiring page access token or an app access token, which also doesn't have to be renewed, instead of an user access token.
There's no way to automatically extend the long-lived access token. The user must visit your app again.
I have a Phonegap application that is communicating with Nodejs server.
Also for the Facebook\Twitter login I'm using Hello.js library (which is very easy to use by the way).
Unfortunately this library only makes client side login (authentication), so at server side I don't know if the user is valid or not (have been looged in with Facebook\Twitter).
Edit:
After the user is logged in (client side), Hello.js provides the user credentials, with a Facebook unique user ID, but I don't know how to pass it safely to the server, or generally if its a good idea to use it as a user id in my DB.
I'm looking for a simple example that will check the validity of the login at server side too.
Thanks.
If you are using https then sending the id to your server will be fine. What you can do is just check to see if that unique id already exists in your DB and return that users data (if needed) or create a new account.
I would also recommend creating a JWT (JSON Web Token) on the server side and sending that back to the app to be stored in local storage and used to validate all future requests to your node server. You can implement that method pretty easily if you use the jwt.verify method as middleware on all of your routes.
Here is a quick example:
var jwt = require('jsonwebtoken');
var jwtValidation = function(req, res, next) {
var token = req.body.jwt_token;
if (token) {
jwt.verify(token, 'yourSecretKeyHere', function(err, decoded) {
if (err) {
// Error when checking JWT - redirect to unauthorized
res.redirect('/unauthorized');
} else if (decoded.id) {
// Token that was passed in has been decoded
// Check your DB for the decoded.id
// Complete any other needed tasks then call next();
next();
} else {
// Something else went wrong - redirect to unauthorized
res.redirect('/unauthorized');
}
});
} else {
// No token present - redirect to unauthorized
res.redirect('/unauthorized');
}
};
module.exports = jwtValidation;
This is the main idea as I figured:
In the Phonegap application, after the user has logged in, this function will be called:
hello.on('auth.login', function(r){
var token = r.authResponse.access_token;
}
now, you can send only the token to the server, and the server will get the user credentials directly from Facebook.
For example, in Facebook, call this usr:
https://graph.facebook.com/me?access_token={token}