For, an user verification now I hardcoded the username and password directly on my code. But I want this dynamically using database username and password. As, i'm new to hapi.js it seems quite difficult for me. This is my code :
app.js
const auth = require('hapi-auth-basic');
const hapi = require('hapi');
mongoose.connect('mongodb://localhost:27017/db', {
useNewUrlParser: true }, (err) => {
if (!err) { console.log('Succeeded.') }
else { console.log(`Error`)}
});
const StudentModel = mongoose.model('Student', {
username: String,
password: String
});
const user = {
name: 'jon',
password: '123'
};
const validate = async (request, username, password, h) => {
let isValid = username === user.name && password === user.password;
return {
isValid: isValid,
credentials: {
name: user.name
}
};
};
const init = async () => {
await server.register(auth);
server.auth.strategy('simple', 'basic', {validate});
server.auth.default('simple');
server.route({
method: 'GET',
path: '/',
handler: async (request, h) => {
return 'welcome';
}
});
}
I tried to do this by changing the validate as below :
const validate = async (request, username, password, h) => {
let isValid = username === request.payload.name && password === request.payload.password;
return {
isValid: isValid,
credentials: {
name: request.payload.name
}
};
};
but i got the type error "name" as it's natural. How can I modify this?
Here, fetch user and check in the validation method
const validate = async (request, username, password, h) => {
// fetch user here
const user = await StudentModel.findOne({username, password}).exec();
// user doesn't exist
if(!user) return {isValid: false}
// just make sure here user really exists
return {
isValid: true,
credentials: {
name: user.name
}
}
}
Related
I am currently using the credentials provider to verify a user on an LDAP. I am also using Hasura as a backend which requires certain claims in the jwt. So a custom encode and decode function is required. This is where i run into my issues. If i used the default encoding for Next-Auth everything works and I am able to log into my app. When i create my own encode and decode function i cannot get past the login screen. I am also using the NextJs middleware to ensure a valid session is present.
Here is my[...nextauth].ts file
export default NextAuth({
providers: [
CredentialsProvider({
id: 'credentials',
name: 'credentials',
credentials: {
username: { label: 'Username', type: 'text', placeholder: '' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials, req) {
console.log(
`Authorize Function called with creds - ${JSON.stringify(
credentials
)}`
)
//Developement Authentication
if (process.env.RUNNING_ENV == 'DEVELOPMENT')
return {
id: process.env.DEV_USER as string,
group: process.env.DEV_GROUP as string,
}
// const { username, password } = credentials
const username = credentials?.username
const password = credentials?.password
if (!username || !password) {
throw new Error('Enter Username and Password')
}
try {
const response = await authenticate(username, password)
//console.log(`Response: ${JSON.stringify(response)}`)
return { username, group: 'cyberlab' }
} catch (err) {
//console.log(`Error authenticating: ${err}`)
}
return null
},
}),
],
secret: process.env.NEXTAUTH_SECRET,
pages: {
signIn: '/login',
},
//Specifies JSON web tokens will be used.
jwt: {
secret: process.env.JWT_SECRET,
async encode({ token, secret, maxAge }) {
console.log(`Before Encode function token: ${JSON.stringify(token)}`)
const jwtClaims = {
sub: token.username,
name: token.username,
iat: Date.now() / 1000,
exp: Math.floor(Date.now() / 1000) + 3600,
hasura: {
'x-hasura-allowed-roles': [
'CRU',
'Forensic',
'EvSpecialist',
'Preview',
'dany',
],
'x-hasura-default-role': 'dany',
'x-hasura-user-id': token.username,
},
}
const encodedToken = jwt.sign(jwtClaims, secret, {
algorithm: 'HS512',
})
console.log(`Encoded Token: ${JSON.stringify(encodedToken)}`)
return encodedToken
},
async decode({ token, secret }) {
const decodedToken = jwt.verify(token, secret, {
algorithms: ['HS512'],
})
//console.log(`Decoded Token: ${JSON.stringify(decodedToken)}`)
return decodedToken
},
},
callbacks: {
async jwt({ token, user }) {
console.log(`Callback token: ${JSON.stringify(token)}`)
console.log(`Callback user: ${JSON.stringify(user)}`)
// * Hasura required claims will be added here
// TODO check to see if hasura claims exist, if not assign them else just pass the token.
if (user) {
console.log(`Adding User in callback`)
token.username = user.id
token.group = user.group
// token.hasura = {
// 'x-hasura-allowed-roles': [
// 'cru',
// 'forensic',
// 'evSpecialist',
// 'preview',
// 'dany',
// ],
// 'x-hasura-default-role': 'dany',
// 'x-hasura-user-id': token.username,
// }
}
return token
},
async session({ session, token, user }) {
// session.type = token.type
session.name = token.username
session.user = user
session.group = token.group
//console.log(`Session in Callback: ${JSON.stringify(session)}`)
//console.log(`Session type in callback: ${session.type}`)
//console.log(`Session User in callback: ${session.name}`)
return session
},
},
session: {
//Sets the session to use JSON Web Token
strategy: 'jwt',
//Sets the max idle time before token expires in seconds - Currently 1hr
maxAge: 3600,
},
})
Here is my terminal output
Callback token: {"sub":"JDoe"}
Callback user: {"id":"JDoe","group":"DEVELOPMENT"}
Adding User in callback
Before Encode function token: {"sub":"JDoe","username":"JDoe","group":"DEVELOPMENT"}
Encoded Token: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJKRG9lIiwibmFtZSI6IkpEb2UiLCJpYXQiOjE2NDYzMTg2NTMuODA4LCJleHAiOjE2NDYzMjIyNTMsImhhc3VyYSI6eyJ4LWhhc3VyYS1hbGxvd2VkLXJvbGVzIjpbIkNSVSIsIkZvcmVuc2ljIiwiRXZTcGVjaWFsaXN0IiwiUHJldmlldyIsImRhbnkiXSwieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoiZGFueSIsIngtaGFzdXJhLXVzZXItaWQiOiJKRG9lIn19.lMkiBj6eIKT0CH-6sullN3qO9pDZimKLNfsUSR6G8WUrdtK_DD1kmtmu_nmwpE-RWSkEwSQC-u3g-ocRtrSinQ"
Callback token: {"sub":"JDoe","name":"JDoe","iat":1646318653.808,"exp":1646322253,"hasura":{"x-hasura-allowed-roles":["CRU","Forensic","EvSpecialist","Preview","dany"],"x-hasura-default-role":"dany","x-hasura-user-id":"JDoe"}}
Callback user: undefined
Before Encode function token: {"sub":"JDoe","name":"JDoe","iat":1646318653.808,"exp":1646322253,"hasura":{"x-hasura-allowed-roles":["CRU","Forensic","EvSpecialist","Preview","dany"],"x-hasura-default-role":"dany","x-hasura-user-id":"JDoe"}}
Encoded Token: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDYzMTg2NTMuODcyLCJleHAiOjE2NDYzMjIyNTMsImhhc3VyYSI6eyJ4LWhhc3VyYS1hbGxvd2VkLXJvbGVzIjpbIkNSVSIsIkZvcmVuc2ljIiwiRXZTcGVjaWFsaXN0IiwiUHJldmlldyIsImRhbnkiXSwieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoiZGFueSJ9fQ.lPao8tCFq7z0Pb8tIO7sm0L91fkwajA-Uuu_OgG6rIgo4sC3z6Zd07q1XaKNQ0P3-xt2c1bF0up6tVab3djG-g"
Callback token: {"sub":"JDoe","name":"JDoe","iat":1646318653.808,"exp":1646322253,"hasura":{"x-hasura-allowed-roles":["CRU","Forensic","EvSpecialist","Preview","dany"],"x-hasura-default-role":"dany","x-hasura-user-id":"JDoe"}}
Callback user: undefined
Before Encode function token: {"sub":"JDoe","name":"JDoe","iat":1646318653.808,"exp":1646322253,"hasura":{"x-hasura-allowed-roles":["CRU","Forensic","EvSpecialist","Preview","dany"],"x-hasura-default-role":"dany","x-hasura-user-id":"JDoe"}}
Encoded Token: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDYzMTg2NTUuMjk3LCJleHAiOjE2NDYzMjIyNTUsImhhc3VyYSI6eyJ4LWhhc3VyYS1hbGxvd2VkLXJvbGVzIjpbIkNSVSIsIkZvcmVuc2ljIiwiRXZTcGVjaWFsaXN0IiwiUHJldmlldyIsImRhbnkiXSwieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoiZGFueSJ9fQ.wnaes7CejSGCjHTzwNKTsNV3pfhy4iYqyRmfaNhpELQvJDciYMfTyFc2O4byq8cLAP2brUpfDwyQZIFZNMAGPA"
Callback token: {"iat":1646318655.297,"exp":1646322255,"hasura":{"x-hasura-allowed-roles":["CRU","Forensic","EvSpecialist","Preview","dany"],"x-hasura-default-role":"dany"}}
Callback user: undefined
Before Encode function token: {"iat":1646318655.297,"exp":1646322255,"hasura":{"x-hasura-allowed-roles":["CRU","Forensic","EvSpecialist","Preview","dany"],"x-hasura-default-role":"dany"}}
Encoded Token: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDYzMTg2NTUuNzQ3LCJleHAiOjE2NDYzMjIyNTUsImhhc3VyYSI6eyJ4LWhhc3VyYS1hbGxvd2VkLXJvbGVzIjpbIkNSVSIsIkZvcmVuc2ljIiwiRXZTcGVjaWFsaXN0IiwiUHJldmlldyIsImRhbnkiXSwieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoiZGFueSJ9fQ.6saQlySc7rgHikj85iejpz6nRm9fEtEdupz_j1hcwZ0TcZiQlLvY1-M-9xkju2F-0MlWmQwIj-bfEU7BmuEc5w"
Callback token: {"iat":1646318655.297,"exp":1646322255,"hasura":{"x-hasura-allowed-roles":["CRU","Forensic","EvSpecialist","Preview","dany"],"x-hasura-default-role":"dany"}}
Callback user: undefined
Before Encode function token: {"iat":1646318655.297,"exp":1646322255,"hasura":{"x-hasura-allowed-roles":["CRU","Forensic","EvSpecialist","Preview","dany"],"x-hasura-default-role":"dany"}}
Encoded Token: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDYzMTg2NTUuOTgsImV4cCI6MTY0NjMyMjI1NSwiaGFzdXJhIjp7IngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsiQ1JVIiwiRm9yZW5zaWMiLCJFdlNwZWNpYWxpc3QiLCJQcmV2aWV3IiwiZGFueSJdLCJ4LWhhc3VyYS1kZWZhdWx0LXJvbGUiOiJkYW55In19.0tqw2fg_GjQGY55sHCKxscipHaGj9IHrdgNKBETsOzMPn5Rai3CFx1TMJP9ZOcyAzbSBmrXN31uWKCrda37X_g"
Here is what my middleware currently looks like.
import { NextApiRequest } from 'next'
import { getToken } from 'next-auth/jwt'
import { NextResponse, NextRequest } from 'next/server'
//Groups and Pages Associated with groups
// const userGroups = [
// 'CRU',
// 'Forensic',
// 'Evidence Specialist',
// 'Preview',
// 'Dany',
// 'DEVELOPMENT',
// 'TEST',
// ]
//Group access pages.
const CRU_PAGES: string[] = ['']
const FORENSIC_PAGES: string[] = ['']
const EVSPEVIALIST_PAGES: string[] = ['']
const PREVIEW_PAGES: string[] = ['']
const DANY_PAGES: string[] = ['']
const DEV_PAGES: string[] = ['/testpages', '/testpage', '/cases']
const TEST_PAGES: string[] = ['/testpage']
//Default Access Routes
const DEFAULT_PAGES: string[] = [
'/',
'/unauthorized',
'/api',
'/favicon.ico',
'/login',
]
//The following function will protect the routes or redirect to unauthorized if not allowed to access a page.
function checkAuthPath(pathname: string, group: string, req: NextApiRequest) {
var authorizedPages: string[] = ['']
//Based on group assign authorized pages to the authorizedPages array.
switch (group) {
case 'CRU':
authorizedPages = CRU_PAGES
break
case 'Forensic':
authorizedPages = FORENSIC_PAGES
break
case 'Evidence Specialist':
authorizedPages = EVSPEVIALIST_PAGES
break
case 'Preview':
authorizedPages = PREVIEW_PAGES
break
case 'Dany':
authorizedPages = DANY_PAGES
break
case 'DEVELOPMENT':
authorizedPages = DEV_PAGES
break
default:
authorizedPages = TEST_PAGES
}
authorizedPages = authorizedPages.concat(DEFAULT_PAGES)
//Determine if request path is in the authorized paths Array. If not redirect to unauthorized.
if (authorizedPages.includes('/' + pathname.split('/')[3])) {
//console.log(`pathname in checkauth function: ${pathname}`)
//console.log('Authorized')
return NextResponse.next()
} else {
//console.log('Unauthorized')
return NextResponse.redirect(new URL('/unauthorized', req.url))
}
}
export async function middleware(req: NextApiRequest) {
const token = await getToken({ req, secret: process.env.JWT_SECRET })
// console.log(`Middleware Token: ${JSON.stringify(token)}`)
const pathname = req.url
if (typeof pathname === 'string') {
if (pathname.includes('/api/auth') || token) {
if (token) {
//console.log('Token Found')
//console.log(`pathname: ${pathname}`)
const group = token.group as string
return checkAuthPath(pathname, group, req)
} else return NextResponse.next()
}
if (
(!token && pathname !== 'http://localhost:3000/login') ||
pathname.includes('/api/auth')
) {
//&& !pathname.includes('/api/auth/signin'
//console.log('Token Not Found')
return NextResponse.redirect(new URL('/login', req.url))
}
} else {
throw new Error('Pathname Undefined')
}
}
As you can see by the last output i am missing then user and group from the JWT. Also why does it loop so many times?
Solved by including the custom encoding and decoding in the middleware too.
y'all, I'm trying to create a signup and login local-Strategy with passport and have been seeing a PROXY ERROR:. I thought I was returning all possible endings for the functions but maybe I'm missing something? I'm new to development and am going crazy haha... Any help is much appreciated, also let me know if I missed adding pertinent code. (I did not add the return block from the react component, I assumed it unnecessary). I am importing my strategies through a strategy index.js (also did not include).
SignupStratagy.js / local strategy
const Strategy = require("passport-local").Strategy;
const User = require("../models/User");
// for password encryption;
const bcrypt = require("bcrypt");
const saltRounds = 10;
const SignupStrategy = new Strategy({ passReqToCallback: true }, function (
req,
username,
password,
done
) {
// console.log(username, password);
const encryptedPass = bcrypt.hashSync(password, saltRounds);
// console.log("encryptedPassword", encryptedPass);
const phone = req.body.phone;
const email = req.body.email;
const street = req.body.street;
const city = req.body.city;
const state = req.body.state;
const zip = req.body.zip;
const isAdmin = req.body.isAdmin;
User.findOne({ username: username }, (err, user) => {
// console.log("SignupStrategy.js / req:", req.body);
if (err) {
return done(err, user);
}
if (user) {
return done("User Name is already taken:", user);
}
})
// .lean()
// .exec();
// console.log("SignupStrategy.js / encrypted password:", encryptedPass);
let newUser = {
username,
password: encryptedPass,
phone,
email,
street,
city,
state,
zip,
isAdmin,
};
User.create(newUser, (error, newUser) => {
if (error) {
return done(error, null);
}
// delete the user password before it is sent back to the front-end;
newUser.password = undefined;
delete newUser.password;
return done(null, newUser);
});
});
module.exports = SignupStrategy;
apiRoute.js / route for signup
const router = require("express").Router();
const db = require("../models");
const passport = require("../config");
// const isAuthenticated = require("../config/middleware/isAuthenticated");
// USER SIGN-UP ROUTE
router.post("/api/signup", function (req, res, next) {
// console.log(req.body);
passport.authenticate("local-signup", (error, user) => {
// console.log("apiRoutes.js / error:", error, "apiRoutes.js / user:", user)
if (error)
return res.status(500).json({
message: error,
});
else {
return res.status(200).json(user);
}
})(req, res, next);
});
Signup / react component
import React, { useState } from "react";
import axios from "axios";
import { Redirect, Link } from "react-router-dom";
import chip from "../../images/chipper/chipperOne.png";
import "./style.css";
function Signup() {
const [signupState, setSignupState] = useState({
username: "",
password: "",
phone: "",
email: "",
street: "",
city: "",
state: "",
zip: "",
key: "",
redirect: false,
adminRedirect: false,
isAdmin: false,
});
const onChange = (e) => {
// console.log("working")
// console.log(typeof e.target.type)
if (e.target.type === "checkbox") {
if (signupState.isAdmin === false) {
setSignupState({
...signupState,
isAdmin: true,
})
} else {
setSignupState({
...signupState,
isAdmin: false,
});
}
} else {
setSignupState({
...signupState,
[e.target.name]: e.target.value,
});
}
};
const onSubmit = async (e) => {
e.preventDefault();
if (signupState.isAdmin === true) {
const response = await axios.post("/api/admin-sign-up", {
key: signupState.key,
});
console.log(response.status);
if (response.status === 500) {
console.log(response);
return;
}
if (response.status === 200) {
console.log(response.status);
setSignupState({
...signupState,
adminRedirect: true,
});
}
}
// console.log(`onSubmit ${signupState}`);
axios.post("/api/signup", {
username: signupState.username,
password: signupState.password,
phone: signupState.phone,
email: signupState.email,
street: signupState.street,
city: signupState.city,
state: signupState.state,
zip: signupState.zip,
isAdmin: signupState.isAdmin,
})
.then((res) => {
console.log(res);
if (res.data) {
console.log(`Sign-in Successful`);
setSignupState({
...signupState,
redirect: true,
});
}
})
.catch((err) => {
if (err) console.log(`Sign-Up server error ${err}`);
});
};
ERROR:
Proxy error: Could not proxy request /api/signup from localhost:3000 to http://localhost:3001.
[1] See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNREFUSED).
What I am trying to achieve is the following:
I have got a register screen in which the user enters his email and a password. When "register" ist pressed, I want the user to receive an email with a random 6-digit-code which allows him to enter this code on the next page which is the verification screen and with that verify the email.
Everything is set up, but I can only find ways via a generated verification link.
The way I would like to go is:
User enters credentials (email, password)
When pressed on "register" this data is saved to the database under the users collection with an ID and an email with the code is sent.
When this is done, the code can be entered and is somehow compared to the code that is saved somewhere in the database?? Because just sending the generated code to the next screen and store it in a class variable wouldn't be the best approach of achieving this I guess.
After success the users "verified" field is set to true in the database.
I don't really know the best way of programming this.
Please do not bother me for the naming of the routes ;) It's just like that for the moment for simplicity. Because of the same reason I only send response codes of 400 when something went wrong.
Flutter (registration screen)
child: RawMaterialButton(
onPressed: () async {
setState(() => isLoading = true);
if (email.length < 6) setState(() => isLoading = false);
else if (password.length < 6) setState(() => isLoading = false);
else {
var registerRes = await _auth.registerWithEmailAndPassword(email, password);
if (registerRes == 200) {
var code = randomNumeric(6);
var sendRes = await _auth.sendCode(email, code);
if (sendRes == 200) {
Routes.sailor.navigate(
'/confirmation',
params: {
'email': email,
'password': password,
'code': code
}
);
} else {
setState(() => isLoading = false);
}
} else {
setState(() => isLoading = false);
}
}
},
...
)
Flutter (Auth class)
import 'package:http/http.dart' as http;
import 'dart:convert';
class AuthService {
Future<String> loginWithEmailAndPassword(String email, String password) async {
Map data = {
'email': email,
'password': password
};
String body = json.encode(data);
var res = await http.post(
'http://10.0.2.2:3000/api/users/login',
headers: { 'Content-Type' : 'application/json'},
body: body
);
if (res.statusCode == 200) return res.body;
return null;
}
Future<int> registerWithEmailAndPassword(String email, String password) async {
Map data = {
'email' : email,
'password': password
};
String body = json.encode(data);
var res = await http.post(
'http://10.0.2.2:3000/api/users/register',
headers: { 'Content-Type' : 'application/json'},
body: body
);
return res.statusCode;
}
Future<int> sendCode(String email, String code) async {
Map data = {
'email' : email,
'code': code
};
String body = json.encode(data);
var res = await http.post(
'http://10.0.2.2:3000/api/users/send',
headers: { 'Content-Type' : 'application/json'},
body: body
);
return res.statusCode;
}
}
Node JS (auth.js)
const router = require('express').Router();
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const { registerValidation, loginValidation } = require('../validation');
const User = require('../models/User');
const nodemailer = require('nodemailer');
router.post('/register', async (req, res) => {
const { error } = registerValidation(req.body);
if (error) return res.sendStatus(400);
const emailExists = await User.findOne({ email : req.body.email });
if (emailExists) return res.sendStatus(400);
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(req.body.password, salt);
const user = new User({
email : req.body.email,
password : hashedPassword
});
try {
const savedUser = await user.save();
} catch(err) {
res.sendStatus(400);
}
});
router.post('/send', async (req, res) => {
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.EMAIL_ADDRESS,
pass: process.env.EMAIL_PASSWORD
},
tls: {
rejectUnauthorized: false
}
});
const mailOptions = {
from: '"coderave" <ccoderave#gmail.com>',
to: req.body.email,
subject: 'Bestätigungscode',
text: req.body.code
};
transporter.sendMail(mailOptions, function (err, info) {
if (err) res.sendStatus(400);
else res.sendStatus(200);
});
});
module.exports = router;
Kindly use the NPM Package (two-step-auth)
Use this package in your server index.js and create a route for it, and directly pass in the variables and you can do the rest,
In your app get the data from the form and pass it to your server and then to the package and it will return you an OTP to work with,
Kindly check the full procedures with the example here
Usage
const {Auth} = require('two-step-auth');
async function login(emailId){
const res = await Auth(emailId);
// You can follw the above approach, But we recommend you to follow the one below, as the mails will be treated as important
const res = await Auth(emailId, "Company Name");
console.log(res);
console.log(res.mail);
console.log(res.OTP);
console.log(res.success);
}
const app = express();
app.get('./auth/mailId/CompanyName', async(req, res)=>{
const {emailId} = req.params;
const data = login(emailId);
})
Output
This will help you a lot taking care of the process of verification under the HOOD.
it seems that the create method does not return any promise that then can handle
I tried different things but nothing worked
this is my routes file
const express = require("express")
const router = express.Router();
const controller = require("./controller")
router.post("/signup", controller.create);
module.exports = router;
and this is my model file
const mongoose = require('mongoose');
const User = new mongoose.Schema(
{
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
picture: {
type: String
},
password: {
type: String,
select: false
},
email: {
required: true,
type: String,
unique: true
}
},
{
timestamps: true
}
);
User.index({
firstName: 'text',
lastName: 'text',
});
module.exports = mongoose.model('User', User);
and this is the controller file
const User = require('./model');
const { hash, compareHash } = require('../lib/util');
const { createToken, findUserByToken } = require('../lib/auth');
const cookieIsSecure = process.env.ENVIRONMENT === 'production';
exports.create = async (req, res) => {
const password = await hash(req.body.password);
const rawUser = {
...req.body,
password,
};
User.create(rawUser)
.then(async user => {
return user.save();
})
.then(async user => {
const newUser = user.toObject();
res.send(newUser);
})
.catch(err => {
if (err.code === 11000) {
res.status(400).send({ message: 'A user with this email address has already registered.' });
return;
}
res.status(500).send({ message: 'An unexpected error occurred' });
});
};
it always return the 500 error "an unexpected error occurred"
which is not really specific. and i do not know what is the problem exactly. but I am sure it has something to do with the model.create() it does not return any promise.
Here you are mixing methods. create doesn't want save in it as it's implicit:
https://mongoosejs.com/docs/api.html#model_Model.create
Please try this, I've refactored your code a bit and added much easier to read and use try/catch:
const rawUser = new User({ ...req.body, password});
try {
await rawUser.save();
res.status(201).send(newUser);
} catch(err) {
if (err.code === 11000) return res.status(400).send({ message: 'A user with this email address has already registered.' });
res.status(500).send({ message: 'An unexpected error occurred' });
}
You need to use async/await like this:
exports.create = async (req, res) => {
try {
const password = await hash(req.body.password);
const rawUser = {
...req.body,
password
};
const user = await User.create(rawUser);
const newUser = user.toObject();
res.send(newUser);
} catch (err) {
console.log("ERROR: ", err);
if (err.code === 11000) {
return res.status(400).send({
message: "A user with this email address has already registered."
});
}
res.status(500).send({ message: "An unexpected error occurred" });
}
};
I'm trying to update an invitation when the invited user registers. The invitation has an auth property which is a nested object, which itself has a property with the key "used." I'm just trying to explicitly declare the value to be true, and save, using async/await. But it's not updating. Is there a better way to do this?
My function:
exports.invitedSignup = async (req, res, next) =>
{
const { firstName, lastName, company, password, email, companyCode, token } = req.body;
console.log(email);
try
{
const user = await User.findOne({ email });
const invitation = await Invitation.findOne({ email }).sort({ field: 'asc', _id: -1 }).limit(1);
if (user) { return res.status(422).send({ error: "User is already registered" }); };
if (!invitation) { return res.status(422).send({ error: "No invitation on record" }); };
if (token !== invitation.auth.token)
{
return res.status(422).send({ error: "Something has gone wrong, please sign up again" });
}
try
{
invitation.auth.used = true;
const updateInvitation = await invitation.save();
console.log("authorization: " + invitation.auth.used);
} catch (e)
{
return next(e);
}
try
{
const saveUser = new User({
firstName: firstName,
lastName: lastName,
email: req.body.email,
password: password,
company: company,
companyCode: companyCode,
role: 1,
auth: { used: true }
});
const newUser = await saveUser.save();
const { email, firstname, lastname } = newUser;
res.json({ token: tokenForUser(newUser), email, firstName, lastName });
}
catch (e)
{
return next(e);
}
}
catch (e)
{
return next(e);
}
};
The invitation schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bcrypt = require('bcrypt-nodejs');
//define model
const invitationSchema = new Schema({
email: { type: String, unique: true, lowercase: true, unique: true },
inviter: String,
company: String,
companyCode: String,
created: Date,
auth: {
token: String,
used: Boolean,
expires: Date,
}
});
invitationSchema.pre('save', function (next)
{
const invitation = this;
bcrypt.genSalt(10, (err, salt) =>
{
const tomorrow = new Date();
invitation.created = tomorrow;
tomorrow.setDate(tomorrow.getDate() + 1);
if (err) { return next(err); };
invitation.auth = { token: salt, used: 0, expires: tomorrow };
next();
});
});
//create model class
const ModelClass = mongoose.model('invitation', invitationSchema);
//export model
module.exports = ModelClass;
http://mongoosejs.com/docs/schematypes.html#mixed
person.anything = { x: [3, 4, { y: "changed" }] };
person.markModified('anything');
person.save(); // anything will now get saved