How can I get specific errors when trying to login using feathers.js - feathersjs

Whenever I try to login with the correct user and correct password everything is fine, but whenever I try to login with a not existing user or a mistaken password I just get the same mistake which is:
{
"name": "NotAuthenticated",
"message": "Invalid login",
"code": 401,
"className": "not-authenticated",
"errors": {}
}
The expected outcome is to show: user doesn't exist. Or for example: given user and password doesn't match
here is what I'm doing on my code
var username = "givenUsername"
var password = "givenPassword"
client.authenticate({
strategy: 'local',
username, password
}).then((authResponse)=>{
console.log(authRersponse)
}).catch((err)=>{
console.error(err)
})

This is not done by default because it would allow an attacker to guess which email addresses or user names are registered on your system. You can always customize the local authentication strategy to throw the errors you would like, for example by overriding findEntity and comparePassword:
const { AuthenticationService, JWTStrategy } = require('#feathersjs/authentication');
const { LocalStrategy } = require('#feathersjs/authentication-local');
const { NotAuthenticated } = require('#feathersjs/errors');
class MyLocalStrategy extends LocalStrategy {
async findEntity(username, params) {
try {
const entity = await super.findEntity(username, params);
return entity;
} catch (error) {
throw new Error('Entity not found');
}
}
async comparePassword(entity, password) {
try {
const result = await super.comparePassword(entity, password);
return result;
} catch (error) {
throw new Error('Invalid password');
}
}
}
module.exports = app => {
const authService = new AuthenticationService(app);
authService.register('local', new MyLocalStrategy());
// ...
app.use('/authentication', authService);
}

Related

How to avoid error 500 on Nextjs API on client-side fetch?

I have the following API to get the user's data based on a [pid]:
import prisma from "../../../../lib/prisma";
// Master read function - API route includes profile, subnodes and contents
async function getProfile(req, res) {
const profilePID = await prisma.profileNode.findUnique({
where: {
userName: req.query.pid
},
include: {
subnode: {
include: {
content: true,
}
},
},
})
// Integer for how many accounts the current user is following
const followingCount = await prisma.follower.count({
where: {
followerId: profilePID.userId
},
select: {
profileId: true
}
})
// integer for how many accounts the current user is being followed
const followerCount = await prisma.follower.count({
where: {
profileId: profilePID.userId
},
select: {
profileId: true
}
})
// detailed profile info of the people you are following
const following = await prisma.follower.findMany({
where: {
followerId: profilePID.userId,
NOT: {
profileId: null,
}
},
include: {
followees: true
}
})
// aggregate all data queries into one
const aggregatedData = {
profilesYouAreFollowing: followingCount.profileId,
yourProfileFollowers: followerCount.profileId,
followingData: following,
profileData: profilePID
}
if (aggregatedData) {
res.status(200).json(aggregatedData)
} else {
return res.status(500).json({ error: 'Something went wrong' })
}
}
export default async function handler(req, res) {
// commit to the database
if (req.method === 'GET') {
return getProfile(req, res)
}
}
As you would observe, the first request is to find the profileNode using a [pid] - which is a string like localhost:3000/user/ABC. Then I would get the userId (an integer) within the profileNode. The userId is then used in the rest of the prisma query to the database for followers and followers' details since all the ids are stored as integer.
I used SWR for client-side fetch, which is all fine but I noticed that while fetching, it will cause an error 500 before the data is fully fetched.
Now, while this does not hinder data fetching for presenting data to the client since SWR takes care of error handling and continue fetching until all the data is acquired, however, it does throw an error on other code like JSON.parse, as the error 500 has passed an undefined value to it - thus throwing an error.
Any tips or tricks as to how to get rid of the error 500?
Added client side code below:
const { data, error } = useSWR(`/api/profiles/read/${slug}`, fetcher)
const [subnodes, setSubnodes] = useState();
// authentication using next-auth session and fetched client-side userId
// compare equality - if equal, set Auth to true and show edit components
useEffect(() => {
async function fetchingData() {
setLoading(true);
// session
const session = await getSession();
let sessionUserId;
if (!session) {
sessionUserId = null;
} else {
sessionUserId = session.user.id;
}
// client
const clientId = await data?.profileData.userId;
// authentication check
if (sessionUserId !== clientId) {
setAuth(false);
} else {
setAuth(true);
}
async function asyncStringify(str) {
return JSON.parse(JSON.stringify(str));
}
const awaitJson = await asyncStringify(data?.profileData.subnode)
setSubnodes(awaitJson);
setLoading(false)
}
fetchingData();
}, []);

How to response client(React) errCode and errorMessage in Grapql, Apollo Sever, primsa?

How to response client(React) errCode and errorMessage in Grapql, Apollo Sever, primsa?
login: async (parent, args, context, info) => {
try {
const user = await context.prisma.user.findUnique({ where: { email: args.email } })
if (!user) {
return new ApolloError("Invalid password or password!")
// i want return {errCode: 1, errMessage: "Invalid password!"}
// instead of return new ApolloError("Invalid password or password!")
}
const valid = await bcrypt.compare(args.password, user.password)
if (!valid) {
return new ApolloError("Invalid password or password!")
// i want return {errCode: 1, errMessage: "Invalid password or password!"}
// instead of return new ApolloError("Invalid password or password!")
}
delete user["password"]
const token = jwt.sign({ userId: user.id }, APP_SECRET)
return {
token,
user,
}
}
catch (err) {
console.error(err)
return new ApolloError(`Error from server!`)
}
},

invalid json response body at http://localhost:3000/api/user/[object%20Object] reason: Unexpected token < in JSON at position 0

I'm using this example to create a simple authentication with Nextjs https://github.com/vvo/next-iron-session/tree/master/examples/next.js
but instead of fetching the user JSON object from Github (as the example does) im trying to do it from my mongodb database where i have some users.
I did this on my login.js file:
import fetchJson from "../../lib/fetchJson";
import withSession from "../../lib/session";
import { withIronSession } from "next-iron-session";
import { connectToDatabase } from "../../util/mongodb";
export default withSession(async (req, res) => {
const { db } = await connectToDatabase();
const { username } = await req.body;
const foundUser = await db.collection("users").findOne({"userName": username});
console.log(foundUser) // <--- this returns the user object on console just fine
const url = `http://localhost:3000/api/user/${foundUser}`;
try {
const { userName, email } = await fetchJson(url);
const user = { isLoggedIn: true, userName, email }
req.session.set("user", user);
await req.session.save();
res.json(user);
} catch (error) {
const { response: fetchResponse } = error;
res.status(fetchResponse?.status || 500).json(error.data);
}
});
And i have this code on my /api/user.js file:
import withSession from "../../lib/session";
import { connectToDatabase } from "../../util/mongodb";
export default withSession(async (req, res) => {
const user = req.session.get("user");
if (user) {
const { db } = await connectToDatabase();
const foundUser = await db.collection("users").findOne({"userName": user.userName, "email": user.email});
console.log("useri pi te user.js " + foundUser)
// in a real world application you might read the user id from the session and then do a database request
// to get more information on the user if needed
res.json({
isLoggedIn: true,
...user,
});
} else {
res.json({
isLoggedIn: false,
});
}
});
But i get "invalid json response body at http://localhost:3000/api/user/[object%20Object] reason: Unexpected token < in JSON at position 0" error even though i get the user object printed in the console just fine.
Any help would be appreciated!

JWT token removed from header on reload. How to fix it?

I'm trying to setup a Vue2js app with node.js/express, using JWT authentication.
When signing in token is generated (with bearer) and stored in the client-side (Vuex) successfully.
When reload token somehow dissapers from header and I don't know why?
So when calling fetchAccountFromToken function from helpers/token.js I have below error on the server side:
"TypeError: Cannot read property 'split' of undefined"
helpers/token.js
export function fetchAccountFromToken(token) {
return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString('utf-8'))['user']
}
And I have this code in server.js
app.post('/login', async (req, res) => {
if (req.body == null) {
res.status(401).json({ error: 'Invalid login. Please try again' })
} else {
const userService = new UserService()
const token = await userService.loginUser({
email: req.body.email,
password: req.body.password
})
console.log(token)
if (token) {
res.json({ token })
} else {
res.status(401).json({ error: 'Invalid login. Please try again' })
}
}
})
UserService.js
export default class UserService {
async loginUser(loginUserRequest) {
const { email, password } = loginUserRequest
const userRepository = new UserRepository()
const userDto = await userRepository.getUserByEmail(email)
if (userDto.email === email && userDto.password === password) {
let user = {
id: userDto.id,
email: userDto.email,
firstName: userDto.firstName,
lastName: userDto.lastName,
role: userDto.role
}
return jwt.sign({ user }, 'the_secret_key') //secret key je za validacijo tokena
}
return null
// return res.status(401).json({ error: 'Invalid login. Please try again.'}) // NEEDS to send error if credentials don't match !!!! //
}
UserRepository.js
export default class UserRepository {
async getUserByEmail(email) {
let dbContext = new DbContext()
try {
const query = 'SELECT id, email, password, firstName, lastName, role FROM accounts WHERE email = ?'
const users = await dbContext.query(query, [email])
return users[0]
} finally {
dbContext.close()
}
}
And I have this code in the VueX store module user.js:
export const state = {
user: null
}
export const mutations = {
SET_USER_DATA(state, data) {
console.log('logging in with data data:', data)
let { token } = data
localStorage.setItem('token', token)
let tokenPayloadJson = atob(token.split('.')[1])
let tokenPayload = JSON.parse(tokenPayloadJson)
let user = tokenPayload.user
state.user = user
localStorage.setItem('user', JSON.stringify(user))
console.log('called set user data')
axios.defaults.headers.common['Authorization'] = `Bearer ${data.token}`
},
CLEAR_USER_DATA() {
localStorage.removeItem('token')
localStorage.removeItem('user')
location.reload()
}
}
export const actions = {
login({ commit }, credentials) {
return axios
.post('//localhost:3000/login', credentials)
.then(({ data }) => {
commit('SET_USER_DATA', data)
})
},
fetchUser(id) {
return AccountService.getUser(id)
.then(response => {
return response.data
})
},
logout({ commit }) {
commit('CLEAR_USER_DATA')
}
}
export const getters = {
loggedIn(state) {
return !!state.user
}
}
I don't see storing the token to VueX, just saving it to localStorage. Additionally I don't see how you are reading it from it (neither localStorage nor VueX store). You can load it from localStorage when initializing the store like this:
export const state = {
user: localStorage.getItem('user'),
token: localStorage.getItem('token')
}

Why my password in the database is not equal to the password in the request body?

I try to make a login for my API in Nestjs, so when the user send the data through the request body, I catch the data and I use the query builder of typeorm, then I get the user with his properties, after comproving if user exists I create a new comparison block, I don´t know the reason why the code is not work, if I use https://bcrypt-generator.com/ for comparate the hash password in the database and the password of the request body, that throw true, but in my code it doesn't work
async login(userRO: UserRO) {
const { email, password } = userRO;
const user = await getRepository(User)
.createQueryBuilder('user')
.where('user.email = :email', {email})
.getOne();
if (!user) {
throw new HttpException(
'Usuario no es correcto',
HttpStatus.BAD_REQUEST,
);
}
// hashPassword = $2y$12$ZvWFRLVoS2gxyCjLkCbOZuN7NKfYrpT6cWxSJaeiVr0PnPBeoI8GS
// password = pepito09
const pass = await bcrypt.compare(password, user.password);
if (!pass) { // this always throw an error
throw new HttpException(
'Contraseña incorrecta',
HttpStatus.BAD_REQUEST,
);
}
const rol = await getRepository(Rol)
.createQueryBuilder('rol')
.select('rol.name')
.leftJoinAndSelect(User, 'user', 'user.rolId = rol.id')
.where('user.email = :email', { email })
.getOne();
if (!rol) {
throw new HttpException(
'Rol no encontrado',
HttpStatus.NOT_FOUND,
);
}
const type = this.typeUser(rol.name) ;
const payload = { email: user.email, id: user.id, rol: rol.name };
return {
access_token: this.jwtService.sign(payload),
type,
};
}
So, I expect the comparison block about the password throw true if the password in the database and the password in the request body are equals, and false if it doesn't.
At the moment, always throw true