Error with setting hasOne function in Node js - node.js

I have created 2 table Token and users and i want to associate it. After associating i want to set the token value to my user and associate my userId with the token table. for this i have used setUser & addToken but my setUser is throwing error while addToken is working fine.
const { nanoid } = require('nanoid/async')
const argon2 = require('argon2')
const jwt = require('../lib/jwt')
module.exports = (sequelize, Sequelize, Token, Task) => {
const User = sequelize.define("users", {
age: {
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
}
}, {
defaultScope: {
attributes: { exclude: ['password'] },
},
scopes: {
withPassword: {
attributes: {}
}
}
});
User.beforeCreate(async (user, options) => {
const hashedPassword = await argon2.hash(user.password)
user.password = hashedPassword
})
User.prototype.generateToken = async function generateToken() {
const jwtid = await nanoid()
const token = jwt.sign({ sub: this.id }, { jwtid })
const userToken = await Token.create({ jti: jwtid })
await this.setUser(token)
await this.addToken(userToken)
return token
}
User.prototype.verifyPassword = async function verifyPassword(password) {
console.log('verify Password instance method', { password, hash: this.password })
return argon2.verify(this.password, password)
}
User.hasMany(Token)
Token.belongsTo(User)
User.hasMany(Task)
Task.belongsTo(User)
return User;
};
Please help me fix this so that my token table associate with user table

Related

Nodemailer verification using jwt

I'm trying to make e-mail verification using nodemailer and jwt key. The problem is that when I'm trying to verify jwt key, id of user is always undefined.
What am I doing wrong?
app.post("/api/createAccount", async (req, res) => {
const { login, password } = req.body;
const newUser = await user.create({
login: login,
password: password,
});
jwt.sign(
{ userId: newUser.id },
"SECRETKEY",
{
expiresIn: "7d",
},
(err, token) => {
const url = `http://localhost:5000/api/confirmMail/${token}`;
const options = {
from: "xxx",
to: login,
subject: "verifyacc",
html: `${url} `,
};
transporter.sendMail(options, function (err, info) {
if (err) {
console.log(err);
} else {
console.log(info);
}
});
}
);
});
app.get("/api/confirmMail/:token", async (req, res) => {
try {
const {
userId: { id },
} = jwt.verify(req.params.token, "SECRETKEY");
await user.update({ confirmed: 1 }, { where: { id: id } });
} catch (err) {
console.log(err);
}
return res.redirect("http://localhost:3000/login");
});
The error :
err: Error: WHERE parameter "id" has invalid "undefined" value
The payload of the token you are creating is
{
userId: "foobar"
}
But in the deconstruction during verification
const { userId: { id } } = jwt.verify(...);
you expect it to be
{
userId: {
id: "foobar"
}
}
because the deconstruction you used, roughly translates to
const tmp = jwt.verify(...);
const id = tmp.userId.id;
Thus id is of course undefined, as tmp.userId is (probably) a string or a number, which doesn't have an id property.
Use
const { userId: id} = jwt.verify(...);
await user.update({ confirmed: 1 }, { where: { id } });
which roughly translates to
const tmp = jwt.verify(...);
const id = tmp.userId;
or alternatively
const { userId} = jwt.verify(...);
await user.update({ confirmed: 1 }, { where: { id: userId } });

Have problem is fetching all users. Apollo-Server-Express-Sequelize-GetAllUsers

I am trying to implement signup, signin, getcurrentuser, getallusers functionalities.
except getllusers everything is working fine.
I have used JWT Token verification
help me out to find, how to query all users if a user is logged in
Resolvers
const { ApolloError, AuthenticationError } = require("apollo-server-express");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcrypt");
const { generateToken } = require("../utils/index");
module.exports = {
Query: {
me(parent, args, { models, authUser }) {
return models.User.findByPk(authUser.id);
},
getAllUsers: async (parent, args, { models }) => {
const allUsers = await models.User.findAll();
console.log(allUsers);
return allUsers;
},
users: async (parent, args, { models }) => {
await models.User.findAll();
},
},
Mutation: {
async signUp(parent, { username, email, password }, { models }) {
const userExists = await models.User.findOne({ where: { email } });
if (userExists) {
throw new ApolloError("Email is already in use");
}
const user = await models.User.create({ username, email, password });
// ?console.log(token);
return { token: generateToken(user) };
},
async signIn(parent, { email, password }, context) {
const user = await context.models.User.findOne({ where: { email } });
if (!user) {
throw new AuthenticationError("Invalid Email");
}
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
throw new AuthenticationError("Invalid Password");
}
return { token: generateToken(user) };
},
// async signIn(parent, { email, password }, { models }) {
// const user = await models.User.findOne({ where: { email } });
// if (!user) {
// throw new AuthenticationError("Invalid Email");
// }
// const isPasswordValid = await bcrypt.compare(password, user.password);
// if (!isPasswordValid) {
// throw new AuthenticationError("Invalid Password");
// }
// return { token: generateToken(user) };
// },
},
};
Type Defs
const { gql } = require("apollo-server-express");
module.exports = gql`
type User {
id: ID!
username: String!
email: String!
role: Role!
avatar: String
createdAt: DateTime!
updatedAt: DateTime!
}
enum Role {
ADMIN
USER
}
type Token {
token: String!
}
extend type Mutation {
signUp(username: String!, email: String!, password: String!): Token!
signIn(email: String!, password: String!): Token!
}
extend type Query {
getAllUsers: User
users: User
me: User!
}
`;
Here is my Index.JS file
My Headers
const express = require("express");
const { ApolloServer, gql } = require("apollo-server-express");
const models = require("./models");
const typeDefs = require("./typedefs");
const resolvers = require("./resolvers");
const { getAuthUser } = require("./utils");
const app = express();
const port = 5000;
Server Initializaation
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
const authUser = getAuthUser(req);
console.log("Auth User", authUser);
return { models, authUser };
},
});
server.applyMiddleware({ app, cors: true })
app.listen(port, () => console.log(`started at ${port} ${server.graphqlPath}`));

Sequelize model custom function cannot be called from controller

I have a sequelize model this custom functions like so:
'use strict';
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const config = require('../../config');
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
id: {
type: DataTypes.STRING,
primaryKey: true
},
name: DataTypes.STRING,
email: DataTypes.STRING,
bio: DataTypes.STRING,
phone: DataTypes.STRING,
username: DataTypes.STRING,
password: {
type: DataTypes.STRING,
set(value){
this.setDataValue('password', bcrypt.hashSync(value, 10));
}
}
}, {});
User.generateJWT = function(id, username) {
return jwt.sign({
id: id,
username: username,
expiresIn: config.auth.exp
}, config.secret);
};
User.toAuthJson = async function() {
return {
name: this.name,
email: this.email,
bio: this.bio,
phone: this.phone,
username: this.username
};
};
User.validatePassword = function(password, passwordHash){
return bcrypt.compareSync(password, passwordHash);
};
User.isUniqueEmail = async function(email) {
return await User.findOne({where: {email}}) === null;
};
User.isUniqueUsername = async function(username) {
return await User.findOne({where: {username}}) === null;
};
User.isUniquePhone = async function(phone) {
return await User.findOne({where: {phone}}) === null;
};
User.associate = function(models) {
// associations can be defined here
};
return User;
};
and a controller like so:
const {User} = require('../database/models/');
module.exports.register = async (req, res, next) => {
try {
const isUniqueEmail = await User.isUniqueEmail(req.body.email);
if (!isUniqueEmail) return res.status(422).json({'message': 'email already exists'});
const isUniquePhone = await User.isUniquePhone(req.body.phone);
if (!isUniquePhone) return res.status(422).json({'message': 'phone already exists'});
const isUniqueUsername = await User.isUniqueUsername(req.body.username);
if (!isUniqueUsername) return res.status(422).json({'message': 'username already exists'});
const user = await User.create(req.body);
console.log(user.toAuthJson()); //an error occurs here
return res.status(201).json({user: user.toAuthJson()});
}catch (e) {
next(e);
}
};
when i try to access the toAuthJson function from this controller like this user.toAuthJson. "notice the small u." it throws an error TypeError: User.toAuthJson is not a function. I should be able to access it normally. help. thanks
User.toAuthJson is currently a class method. Like with the other functions, you'd need to call it like User.toAuthJson(user).
You're probably looking for an instance method, so you'd want to define it in the prototype instead:
User.prototype.toAuthJson = function() {
return {
name: this.name,
email: this.email,
bio: this.bio,
phone: this.phone,
username: this.username
};
};
Now you can call it on a User instance, like you were attempting to do:
console.log(user.toAuthJson());
Notice also that I omitted the async since this function doesn't do anything asynchronous.

Array in User Schema - mongoose

After building an app using the MERN Stack and finished a simple CRUD API for the items of the users I wanted to add a 'categories' property to the user model which he then can add to his items...
To explain the app I planned to attach some default data i.e categories to each user while posting his data to MongoDB. Unfortunately, I am failing to 'post' those categories to the database.
Here are different request and schema combinations I have tried out
As its own schema
user route
const User = require("../models/User")
// #route POST api/users
// #desc Regiter a user
// #access Public
router.post(
"/",
[
check("name", "Please add name")
.not()
.isEmpty(),
check("email", "Please include a valid email").isEmail(),
check(
"password",
"Please enter a password with 6 or more characters"
).isLength({ min: 6 })
],
async (req, res) => {
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() })
}
const { name, email, password } = req.body
console.log(categories)
try {
let user = await User.findOne({ email })
if (user) {
return res.status(400).json({ msg: "User already exists" })
}
user = new User({
name,
email,
password,
})
const salt = await bcrypt.genSalt(10)
user.password = await bcrypt.hash(password, salt)
await user.save()
const payload = {
user: {
id: user.id
}
}
jwt.sign(
payload,
config.get("jwtSecret"),
{
expiresIn: 360000
},
(err, token) => {
if (err) throw err
res.json({ token })
}
)
} catch (err) {
console.error(err.message)
res.status(500).send("Server Error")
}
}
)
module.exports = router
request in AuthState
// Register User
const register = async formData => {
console.log(formData)
const expandedFormData = {
...formData,
categories: [
{ name: "Testcategory1", test: 1 },
{ name: "Testcategory2", test: 2 }
]
}
const config = {
headers: {
"Content-Type": "application/json"
}
}
try {
const res = await axios.post("/api/users", expandedFormData, config)
console.log(expandedFormData)
dispatch({
type: REGISTER_SUCCESS,
payload: res.data
})
loadUser()
} catch (err) {
dispatch({
type: REGISTER_FAIL,
payload: err.response.data.msg
})
}
}
schema
const mongoose = require("mongoose")
const categorieSchema = mongoose.Schema({
label: String,
test: Number
})
const UserSchema = mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
categories: [categorieSchema]
})
module.exports = mongoose.model("user", UserSchema)
2.
request in AuthState
....
const expandedFormData = {
...formData,
categories: [{ name: "Testcategory1" }, { name: "Testcategory2" }]
}
....
schema
....
categories: [
{
name: String
}
]
....
3.
request in AuthState
schema
same as 2.
....
categories: [
{
name: {
type: String
}
}
]
....
4.
request
schema
same as 2.
....
categories: [String]
....
I've also read these threads but they did not provide new information:
- Mongoose schema array of objects
- Save arrays in Mongoose schema
The full application can be viewed at https://github.com/mortizw/Repio-2.0
Next to some ideas on how to make this model work, I would be happy about some tips on how to iteratively test /approach such a 'schema-problem' as you can with console logging something.
When you are creating new User, you are not passing Categories that's why its not being saved.
First of all this Schema will work perfectly fine
categories: [
{
name: {
type: String
}
}
]
Then you need to update your user route to this
const { name, email, password,categories } = req.body
user = new User({
name,
email,
password,
categories
})
Also make sure you only pass name in categories from frontend because your schema only have name.
Your frontend should be like this
const expandedFormData = {
...formData,
categories: [
{ name: "Testcategory1"},
{ name: "Testcategory2"}
]
}

my nodejs bcrypt compare not working properly

am building an app with nodes qraphQl using apollo, am trying to do a login page, but ater signing up and and i try to sign in, my bcrypt would always return false,
in my user model
import bcrypt from 'bcryptjs';
const user = (sequelize, DataTypes) => {
const User = sequelize.define('user', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
unique: true,
primaryKey: true,
field: 'id'
},
fullname: DataTypes.STRING,
username: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: true,
},
},
email: {
type: DataTypes.STRING,
allowedNull: false,
validate: {
notEmpty: true,
isEmail: true,
}
},
password: {
type: DataTypes.STRING,
allowedNull: false,
validate: {
notEmpty: true,
len: [7, 42],
},
},
role: {
type: DataTypes.ENUM,
values: ['ADMIN', 'INSTRUCTOR', 'STUDENT'],
defaultValue: 'STUDENT'
}
});
User.beforeCreate(async user => {
user.password = await user.generatePasswordHash()
});
User.beforeSave(async user => {
user.password = await user.generatePasswordHash()
});
User.prototype.generatePasswordHash = async function() {
const saltRounds = 10;
return await bcrypt.hash(this.password, saltRounds)
};
User.prototype.validatePassword = async function(password) {
return await bcrypt.compare(password, this.password);
};
User.associate = models => {
User.hasMany(models.Message, { onDelete: 'CASCADE' });
};
User.findByLogin = async login => {
let user = await User.findOne({
where: { username: login },
});
if (!user) {
user = await User.findOne({
where: { email: login },
});
}
return user;
};
return User;
};
export default user;
And in my users resolver, here is the code
import { combineResolvers } from 'graphql-resolvers';
import Joi from 'joi'
import { isAuthenticated, isAdmin } from './authorization';
import {SignUp, SignIn} from '../functions/joi'
import {createToken} from '../functions/jwt'
export default {
Mutation: {
signUp: async (parent, { username, fullname, email, password, Rpassword}, { models, secret }) => {
if(password !== Rpassword){
return new Error('Password did not match')
}
var thejoi = { username, fullname, email, password }
const checkUserEm = await models.User.find({ where: { email: email }})
if (checkUserEm) {
return new Error('Email address already Exist')
}
const checkUserUs = await models.User.find({ where: { username: username }})
if (checkUserUs) {
return new Error('Username already Exist')
}
await Joi.validate(thejoi, SignUp, {abortEarly:false})
const user = await models.User.create({
username,
fullname,
email,
password,
role:'STUDENT'
});
return { token: createToken(user) };
},
signIn: async (parent, { login, password }, { models, secret }, ) => {
var varrh = { password }
await Joi.validate(varrh, SignIn, {abortEarly:false})
const user = await models.User.findByLogin(login);
if (!user) {
return new Error('No user found with this login credentials.');
}
const isValid = await user.validatePassword(password);
if (!isValid) {
return new Error('Invalid password .');
}
return { token: createToken(user) };
}
},
User: {
messages: async (user, args, { models }) => {
return await models.Message.findAll({
where: {
userId: user.id
}
});
},
},
}
pls am really confused because its it suppose to work, i have searched google but it didnt help me, pls how can i solve this issue thanks
inside generatePasswordHash you referencing to this, but you also using arrow functions for beforeCreate and beforeSave hooks.
Two options:
Do not use arrow function for beforeCreate and beforeSave hooks.
User.beforeCreate(async function (user) {
user.password = await user.generatePasswordHash()
});
User.beforeSave(async function (user) {
user.password = await user.generatePasswordHash()
});
Provide user object to generatePasswordHash and replace this with user.
User.beforeCreate(async user => {
user.password = await user.generatePasswordHash(user)
});
User.beforeSave(async user => {
user.password = await user.generatePasswordHash(user)
});
User.prototype.generatePasswordHash = async function(user) {
const saltRounds = 10;
return await bcrypt.hash(user.password, saltRounds)
};

Resources