ExpressJS app login fails after saving first login history - node.js

I've implemented a ExpressJS app using PassportJS, MongoDB (Mongoose) and JSON web tokens.
Inside successful login route implementation
await user.insertLoginTime();
const token = user.generateJwt();
res.status(200).json({ token });
Mongoose model method 'insertLoginTime'
userSchema.methods.insertLoginTime = async function() {
try {
const presentTime = new Date().getTime();
await this.loginHistory.push(presentTime);
await this.save();
return;
} catch (error) {
throw new Error(error);
}
};
First login is successful and 'Postman' receives token but returns invalid password error over next attempts.
Removing the 'insertLoginTime' method successfully logs-in on multiple attempts. Is there any mistake in the 'insertLoginTime' implementation?

I've resolved this issue. It was occurring because I added password hashing to Schema 'pre' hook for 'save'. On saving login history, the hashed password was being hashed again and saved back. This was deleting the actual password hash and causing next attempts to fail.

Related

Appwrite authentication - "createJWT is not a function" error

I am trying to build a Node.js server-side signup function for user authentication. The data for the user is being sent via "req.body" and the authentication database is provided by Appwrite.
The signup function should:
Create a user with the credentials provided in the request body.
Return the user details, such as the username and email.
Generate and return a token (cookie/JWT)
I am encountering issues with the Appwrite documentation and would appreciate guidance on building this function.
When trying to POST a new user using the Users API, an error of
createJWT is not a function
is produced, and when using the Account API, an error of
User (role: guests) missing scope (account)
is produced.
Here's the code I have:
const sdk = require('node-appwrite')
const client = sdk.Client()
client
.setEndpoint(endpoint)
.setProject(projectId)
.setKey('...')
const users = sdk.Users(client)
async function signup(req, res) {
try {
const { email, username } = req.body
let { password } = req.body
password = await bcrypt.hash(password, SALT_ROUNDS)
const result = await users.createBcryptUser("unique()", email, password, username)
// Create a token
// Combine data
res.send(userWithToken)
} catch (err) {
error('Failed to signup', err)
throw new Error(err)
}
}
The Users API is intended to be used in an admin perspective rather than as a user. You can use the Account API to execute things on behalf of a user, but the JWT token is typically generated client side and passed to the server, where you can call client.setJWT().

I'm trying to use node to login. I am using mongodb as my database, I am getting SyntaxError: Unexpected token '.' in "_id:findUser?._id,"

This is the code snippet. Node version v12.22.9
This error also popped up somewhere else, but i commented it because it was not that big a deal.
Essentially, this error is appearing everywhere I have used this format firstname:findUser?.firstname,. How do I deal with it?
//login fucntion
const loginUserCtrl = asyncHandler(async(req,res)=>{
//get email and pass
const {email,password} = req.body;
//console.log(email,password);
//check if user exists
const findUser = await User.findOne({email}); //true or false
if (findUser && await findUser.isPasswordMatched(password)){
//res.json(findUser); //return the found user
res.json({
_id:findUser?._id,
firstname:findUser?.firstname,
lastname:findUser?.lastname,
email:findUser?.email,
mobile:findUser?.mobile,
token:generateToken(findUser?._id),
});
}else{
res.send("Invalid credentials");
}
});
If the provided email and password match with credentials in the database, the system will generate a token and return the user data in form of a json
Edited:
I tried removing the ? in each of the lines and the code worked.

Express GET request not working after adding authentication

I'm stuck on this very simple problem and I don't know what I am doing wrong. I'm creating a simple RESTAPI with users and authentication. This is the GET request that I'm trying to run:
router.get('/users/me',auth ,async(req,res)=>{
console.log(req.user)
console.log('Entered the get request')
res.send(getUserData(req.user)) })
Basically, at the end of the authentication, I save the user fetched from the database into req.user object and then I send it to the client using res.send(req.user). As you can see in the code below:
const auth = async(req,res,next)=>{
try{
const token = req.header('Authorization').replace('Bearer ','')
const decoded = jwt.verify(token,'secretkey')
const db = req.app.locals.database
const user = await db.collection('users').findOne({
_id: decoded.id,
'tokens.token':token
})
console.log(user)
if(!user){
throw new Error()
}
req.user = user
next()
}catch(e){
res.status(401).send('Please Authenticate')
}}
The problem is that this authentication function is working perfectly fine. I have used it on other routes as well. But in this GET request, the authentication function runs (I identified this using many console logs) and fetches the data correctly. But it doesn't enter the get request once the middleware(auth function) has completed the call.
From POSTMAN, when I send GET requests, I get a 404 error and I have no idea why. I have literally spent hours trying to make sense of this. Please help me out if you know what might be the problem.
EDIT: So I was able to run the get request by moving the request above the following request that I also have in the same file:
router.get('/users/:id',auth ,async(req,res)=>{
const id = req.params.id
const db = req.app.locals.database
try{
const result = await db.collection('users').findOne({_id: id})
const userData = getUserData(result)
res.status(200).send(userData)
}catch(e){
res.status(404).send()
}
})
But now this request doesn't work. I cant figure out what positioning has to do with requests functionality. Can anyone please explain?

How to verify Google signin (via Firebase) idToken in nodejs backend?

Trying to verify idToken of a user signed in via firebase authentication (Google signin) in nodejs server. Server throws Firebase ID token has invalid signature.
Tried verifying with firebase-admin as well as jsonwebtoken with public key from the url: https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com. Both methods work perfect for users signed in with a password, but throws 'Invalid Signature' in case of a user signed in via google.
Is there anything I am doing wrong? Do I need to verify with google-auth-library instead?
Code:
import * as admin from "firebase-admin";
admin.initializeApp({
credential: admin.credential.cert(require("../../serviceAccount")), // file received from firebase project settings page
databaseURL: "as mentioned in the firebase project settings page",
});
// Some code here
var token = "token received from client side";
var decoded = await admin.auth().verifyIdToken(token);
PS:
All client side features (after signing in) are working fine.
Everything else on the backend is working fine.
Decoding the token in both cases gives expected JSON.
For test run, token is being forceRefreshed everytime before calling the API.
OP here,
I am dumb.
I was using the print() function of flutter to log the token and call the API myself. Didn't know Flutter's print function has an output character limit. Login using password gives smaller tokens thus the whole token was logged. But Google sign in gives a longer token, longer than the output character limit of print.
Solution : Use log function from 'dart:developer' package.
import 'dart:developer';
//
log(await _auth.idToken);
const { OAuth2Client } = require("google-auth-library");
const client = new OAuth2Client(googleClient[process.env.ENV])
let token = 123456789011-crhch2kuum79bk0qr3usa39f7b9chikc.apps.googleusercontent.com
async function googleLoginVerify(token) {
try {
const ticket = await client.verifyIdToken({
idToken: token,
audience: googleClient[process.env.ENV],
});
const payLoad = ticket.getPayload();
return {
success: true,
data: payLoad,
};
} catch (err) {
console.log(err.message);
return {
success: false,
message: err.message,
};
}
}

Firebase Authentication Microservice

I am trying to write a REST microservice in Node.js that would deal with user authentication (among some other things) requests coming from different platforms.
What I would like is for the device to remember which user is signed in and keep the session for itself only. What currently happening is that I am able to login only one user at a time; if another user logs in from another device, the new user is returned as the currentUser. It's my first time using Firebase Authentication so I am very confused.
Here's the code for the login endpoint:
async signInUser( req, res, next ) {
var user = firebase.auth().currentUser;
if ( !user) {
var email = req.body.email;
var password = req.body.password;
// sign user in: if login fails, send error message as response
user = await firebase.auth().signInWithEmailAndPassword( email, password)
.catch( function( error) {
res.send( error.message);
});
}
// login successful: send user object as response
res.send( user);
}
Firebase have custom authentication you can pass your user unique id to firebase and genrate token for perticular user so you can able create unique session for different user and you can refer below link
https://firebase.google.com/docs/auth/admin/create-custom-tokens

Resources