I have an Adonis.js api-only app and my auth routes are not working.
Here is my signup route:
const Route = use('Route')
...
Route.post('/signup', 'UserController.signup')
Here is the action in the UserController:
'use strict'
const User = use('App/Models/User')
const Hash = use('Hash')
const Writ = use('App/Models/Writ')
class UserController {
async signup ({ request, auth, response }) {
// get user data from signup form
const userData = request.only(['name', 'username', 'email', 'password'])
console.log(userData);
try {
// save user to database
const user = await User.create(userData)
console.log(user);
// generate JWT token for user
const token = await auth.generate(user)
return response.json({
status: 'success',
data: token
})
} catch (error) {
return response.status(400).json({
status: 'error',
message: 'There was a problem creating the user, please try again later.'
})
}
}
...
module.exports = UserController
Using Postman, the console prints the request but returns:
{
"status": "error",
"message": "There was a problem creating the user, please try again later."
}
I hope you put all configuration right as mention in this document.
if your config right then this issue is user migration issue.
because user migration don't content name field so first check without send name into postman and not get name in controller like this
'use strict'
const User = use('App/Models/User')
const Hash = use('Hash')
const Writ = use('App/Models/Writ')
class UserController {
async signup ({ request, auth, response }) {
const userData =request.only(['username','email','password'])
console.log(userData);
try {
const user = await User.create(userData)
console.log(user);
// generate JWT token for user
const token = await auth.generate(user)
return response.json({
status: 'success',
data: token
})
} catch (error) {
return response.status(400).json({
status: 'error',
message: error
})
}
}
...
module.exports = UserController
and then try to generate token it's work
if you get success in response then change migration of user
try it:
'use strict'
const User = use('App/Models/User')
const Hash = use('Hash')
class UserController {
async signup ({ request, auth, response }) {
// get user data from signup form
const userData = request.only(['username', 'email', 'password'])
// ! Only existing fields in the database
console.log(userData);
try {
// save user to databas
const user = new User()
user.fill(userData)
await user.save()
// generate JWT token for user
const token = await auth.generate(user)
return response.json({
status: 'success',
data: token
})
} catch (error) {
return response.status(400).json({
status: 'error',
message: error
})
}
}
}
module.exports = UserController
It would be interesting to add a try/catch when creating the user to better target errors.
If it doesn't work, check the configuration files.
Have a nice day!
Related
I have created a MERN application in that application whenever try to login in application jwt token is generated each time whenever he/she tries to log in and stored inside the MongoDB atlas as well as browser cookies.
In the authorization part, if a user is authorized (cookies token matches with the MongoDB token) then he/she can see only the about-me page or else be redirected to the login page.
so whenever I clicked to about-me page I got err:
TypeError: Cannot read properties of undefined (reading 'jwtoken')
at Authenticate (/Users/apple/Desktop/projects/mern-auth-user/user-data-123/mern-july1/server/middleware/authenticate.js:8:35)
at Layer.handle [as handle_request] (/Users/apple/Desktop/projects/mern-auth-user/user-data-123/mern-july1/server/node_modules/express/lib/router/layer.js:95:5)
error so on....
here; How I created sign API and stored token into MongoDB and cookies. (in auth.js)
router.post("/signin", async (req, res) => {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({ err: "invalid details" });
}
try {
const userLogin = await User.findOne({ email: email });
// console.log(userLogin);
if (!userLogin) {
res.status(400).json({ err: "invalid email" });
}
else {
const isMatch = await bcrypt.compare(password, userLogin.password);
// jwt token
const token = await userLogin.generateAuthToken();
res.cookie("jwtoken", token, {
expires : new Date(Date.now()+25892000000), // after 30 days
httpOnly: true
});
console.log(token);
if(!isMatch){
res.status(400).json({err: 'invalid pass'})
}
else{
res.status(201).json({message:'signin successfully'})
console.log(userLogin)
}
}
} catch (err) {
console.log(err);
}
});
generateAuthToken defined inside the user schema modal used inside the signin API.
userSchema.methods.generateAuthToken = async function(){
try{
const token = jwt.sign({_id : this._id}, process.env.SECRET_KEY); // _id(asking unique id) : this._id (taken id from database of corresponding login email)
this.tokens = this.tokens.concat({token : token}) // storing jwt token into tokens. token(from userSchema) : token (value from generated token)
await this.save();
return token;
}
catch(err){
console.log(err);
}
}
this is my middleware "Authenticate"
const jwt = require('jsonwebtoken');
const User = require('../models/userSchema');
const Authenticate = async (req, res, next) => {
try {
//get jwt token from cookies
const token = req.cookies.jwtoken;
//verifying token with SECRET_KEY
const verifyToken = jwt.verify(token, process.env.SECRET_KEY);
// get user data from token, if token id(from cookies)===tokens.token
const rootUser = await User.findOne({ _id: verifyToken._id, "tokens.token": token });
if (!rootUser) { throw new Error('user not found') }
// if get user
req.token = token;
// get user's all data in rootUser
req.rootUser = rootUser;
// get id od rootUser
req.userID = rootUser._id;
next();
}
catch (err) {
res.status(401).send("unauthorized: no token provided")
console.log(err)
}
}
module.exports = Authenticate;
used inside the about-me API; auth.js
router.get('/about', authenticate, (req, res) => {
// console.log(Authenticate.token)
res.send('hello world from server side');
res.send(req.rootUser)
})
now code inside the Reactjs
About.js
const history = useHistory();
const callAboutPage = async () =>{
try{
const res = await fetch('/about', { //this res is backend response , not from call back function
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
credentials: "include"
});
const data = await res.json();
console.log(data)
if(! res.status === 200){
const error = new Error(res.error);
throw error;
}
}
catch(err){
console.log(err)
history.push('/signin');
}
};
useEffect(() => {
callAboutPage()
}, [])
These is my cookies stored in the browser
enter image description here
please help me to get the about-me page right now I am not able to see my page even I have cookies inside my application(browser).
Cannot read properties of undefined (reading 'jwtoken') indicates that req.cookies is undefined in Authenticate.js.
You may miss the middleware cookie-parser for express, check express doc (req.cookies) here.
To fix this, follow the example of cookie-parser (npm) would help. Code like below will allow you to read cookies from req.cookies.
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser());
I have a get request that should return all of the logged-in user's project they created, I believe the code was written well. when I run it on postman I consistently get a 401 unauthorised error, but when I change the request to patch for example, and I also run a patch request on the postman, it works properly. what could be the issue?
// get all logged in user's projects
router.get('/api/project/mine', auth, async (req, res) => {
try {
const id = req.user._id
const projects = await Project.find({owner: id})
res.status(200).send(projects)
} catch (e) {
res.status(401).send()
}
})
the auth middleware
const jwt = require('jsonwebtoken')
const User = require('../models/user')
const auth = async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ', '')
const decoded = jwt.verify(token, 'creativetoken')
const user = await User.findOne({ _id: decoded._id, 'tokens.token': token })
if (!user) {
throw new Error()
}
req.token = token
req.user = user
next()
} catch (e) {
res.status(401).send({ error: 'Please authenticate' })
}
}
module.exports = auth
Note: the auth makes sure the objectId of the logged-in user is returned through req.user.id
You have 2 try-catch that return 401 but you don't log the error or return the error to frontend.
You need to add console.log(e) in 2 catch block in your auth middleware and your get request.
try {
// some of your code
} catch (e) {
console.log(e); //Add this line
res.status(401).send({ error: 'Please authenticate' })
}
I'm developing a Node.JS & MongoDB app inserting articles and categories, with a signup and login users system. I need to add/fix a secret string on order to make Jsonwebtoken (JWT_KEY) work properly.
My authentication or authorization fails when I try to add an article with details (title, attached picture ect.) threw Postman, probably because I made a mistake installing or using the jsonwebtoken library. It maybe a mistake in the nodemon.json file that is should be hidden at the end (user, password, JWT_KEY), but maybe in another part of my code.
The Postman process connects with the article.js routes file, that seems to be fine. The relevant part is the createArticle POST, since the rest work fine so far:
const express = require('express');
const router = express.Router();
const upload = require('../middlewares/upload');
const checkAuth = require('../middlewares/checkAuth');
const {
getAllArticles,
createArticle,
getArticle,
updateArticle,
deleteArticle
} = require('../controllers/articles');
router.get('/', getAllArticles);
router.get('/:articleId', getArticle);
router.post('/', checkAuth, upload.single('image'), createArticle);
router.patch('/:articleId', checkAuth, updateArticle);
router.delete('/:articleId', checkAuth, deleteArticle);
module.exports = router;
Here is the authChek.js middleware that is responsible of the authorization process:
const jwt = require('jsonwebtoken');
const checkAuth = (req, res, next) => {
try {
const token = req.headers.authorization.split('')[1];
jwt.verify(token, process.env.JWT_KEY);
next();
} catch(error) {
res.status(401).json({
message: 'Auth failed'
})
}
}
module.exports = checkAuth;
The verify seems ok and should work fine connecting to nodemon. If it's all fine, Postman should return back a message that the authorization succeeded - but it returns failed auth. Here, in the article.js controller, the POST method seems fine to and should not catch an error of 500, 401 or 409:
const mongoose = require('mongoose');
const Article = require('../models/article');
const Category = require('../models/category');
module.exports = {
createArticle: (req, res) => {
const { path: image } = req.file;
const { title, description, content, categoryId } = req.body;
Category.findById(categoryId).then((category) => {
if (!category) {
return res.status(404).json({
message: 'Category not found'
})
}
const article = new Article({
_id: new mongoose.Types.ObjectId(),
title,
description,
content,
categoryId,
image: image.replace('\\','/')
});
return article.save();
}).then(() => {
res.status(200).json({
message: 'Created article'
})
}).catch(error => {
res.status(500).json({
error
})
});
}
}
Another file using the JWT_KEY is the users.js controller, in the login part. Look at the area of the if & result. It may fail to connect properly to the .env part of the nodemon.json file. See here "process.env.JWT_KEY":
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const User = require('../models/user');
module.exports = {
login: (req, res) => {
const { email, password } = req.body;
User.find({ email }).then((users) => {
if (users.length === 0) {
return res.status(401).json ({
message: 'Authentication failed'
});
}
const [ user ] = users;
bcrypt.compare(password, user.password, (error, result) => {
if (error) {
return res.status(401).json({
message: 'Authentication failed'
});
}
if (result) {
const token = jwt.sign({
id: user._id,
email: user.email,
},
process.env.JWT_KEY,
{
expiresIn: "1H"
});
return res.status(200).json({
message: 'Authentication successful',
token
})
}
res.status(401).json({
message: 'Authentication failed'
});
})
})
}
}
Is there something to fix here? Or how can I check if my JWT_KEY in nodemon.json is written properly or wrong? If the string is generated by a library or taken from somewhere else, I don't know where to search for it in my app or around the web.
I have issue as title say. I will show you code in NodeJS. Request is blogRouter.delete
controllers/blog.js (only delete method)
const blogsRouter = require('express').Router()
const jwt = require('jsonwebtoken');
const Blog = require('../models/blog')
const User = require('../models/user')
blogsRouter.delete('/:id', async (request, response, next) => {
const token = getTokenFrom(request)
console.log('token: ',token)
try {
const decodedToken = jwt.verify(token, process.env.SECRET)
if (!token || !decodedToken.id) {
return response.status(401).json({ error: 'token missing or invalid' })
}
const userid = await User.findById(decodedToken.id)
const blogs = await Blog.findById(request.params.id)
if(blogs.user.toString() === userid.toString()) {
await Blog.findByIdAndRemove(request.params.id)
response.status(204).end()
} else {
response.status(404).end()
}
}catch(exception){next(exception)}
})
When i console log token i get null via helper function getTokenFrom
getTokenFrom
const getTokenFrom = request => {
const authorization = request.get('authorization')
if (authorization && authorization.toLowerCase().startsWith('bearer ')) {
return authorization.substring(7)
}
return null
}
In post request token working perfectly fine. Im able to create a blog. But when i do the same thing with delete method it wont show token. It says its null. So it returning me my getTokenFrom function correctly but i want to be able to access token in delete method so i can be able to delete certain blog.
controller/login
const jwt = require('jsonwebtoken')
const bcrypt = require('bcryptjs')
const loginRouter = require('express').Router()
const User = require('../models/user')
loginRouter.post('/', async (request, response) => {
const body = request.body
const user = await User.findOne({username: body.username})
const passwordCorrect = user == null ?
false : await bcrypt.compare(body.password, user.passwordHash)
if(!(user && passwordCorrect)) {
return response.status(401).json({
error: "Invalid username or passowrd"
})
}
const userForToken = {
username: user.username,
id: user._id,
}
const token = jwt.sign(userForToken, process.env.SECRET)
response.status(200).send({token, username: user.username, name: user.name})
})
module.exports = loginRouter
https://prnt.sc/qfjgka --> This is a picture. I send http.delete request and i get token null. JWT must be provided. I dont know were is my mistake. I tried a lot of things but it wont work.
I tried to define token with token.request.jwt but then i get it undifined.
I just need to access that token somehow in blogRoute.delete method.
Thanks in forward
EDIT : This is my post method and when i console log token here it returns me value of the token but when i do same things in delete method it wont work
blogsRouter.post('/', async (request, response, next) => {
const body = request.body
console.log('body', body)
const token = getTokenFrom(request)
console.log('token: ', token)
try {
const decodedToken = jwt.verify(token, process.env.SECRET)
if (!token || !decodedToken.id) {
return response.status(401).json({ error: 'token missing or invalid' })
}
const user = await User.findById(decodedToken.id)
const blog = new Blog({
title: body.title,
author: body.author,
url: body.url,
likes: body.likes,
user: user._id
})
const savedBlog = await blog.save()
user.blogs = user.blogs.concat(savedBlog._id)
await user.save()
response.json(savedBlog.toJSON())
} catch(exception) {
next(exception)
}
})
In the screenshot where you logged the request headers, there seems no authorization header, so you are getting error. If you can succesfully be able to send the authorization header, the problem will be resolved.
By the way, checking the token and validating it in every route is not a good solution.
You had better to use an authentication middleware for token validation.
1-) Create an auth middleware like this:
middleware\auth.js
const jwt = require("jsonwebtoken");
module.exports = function(req, res, next) {
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith('Bearer')
) {
token = req.headers.authorization.split(' ')[1];
}
if (!token) {
return res.status(401).json({ error: 'token missing' })
}
try {
const decoded = jwt.verify(token, process.env.SECRET);
req.user = decoded;
next();
} catch (ex) {
return res.status(400).json({ error: 'token invalid' })
}
};
2-) use this auth middlware anywhere you need authentication. Now our routes are clenaer and shorter.
const blogsRouter = require("express").Router();
const jwt = require("jsonwebtoken");
const Blog = require("../models/blog");
const User = require("../models/user");
const auth = require("../middleware/auth");
blogsRouter.delete("/:id", auth, async (request, response, next) => {
try {
const userid = request.user.id; //we set the user in the auth middleware, so we can access it like this
const blogs = await Blog.findById(request.params.id);
if (blogs.user.toString() === userid.toString()) {
await Blog.findByIdAndRemove(request.params.id);
response.status(204).end();
} else {
response.status(404).end();
}
} catch (exception) {
next(exception);
}
});
blogsRouter.post("/", auth, async (request, response, next) => {
try {
const body = request.body
const user = await User.findById(request.user.id);
const blog = new Blog({
title: body.title,
author: body.author,
url: body.url,
likes: body.likes,
user: user._id
});
const savedBlog = await blog.save();
user.blogs = user.blogs.concat(savedBlog._id);
await user.save();
response.json(savedBlog.toJSON());
} catch (exception) {
next(exception);
}
});
In this way you can send your token in the authorization header in the form Bearer TOKEN.....
" if (blogs.user.toString() === userid.toString()) {" it is important to restrict user access to his own resources, so that the route is something like:
delete posts/:id
rather than, delete /users/:uid/posts/:id because hackers will be able to guess out the ids and delete other people's posts.
I have created a Node.js API and am making requests to it using Next.js
Here is my Node.js controller. I am using express validator for validation.
If I fill in the form correctly, it works and the data is saved in mongo as expected. However, I want to send the validation errors back to the client when the form isn't filled in correctly. If I look in console, I can see the errors in the network tab.
exports.register = async (req, res) => {
// check if user exists in the database already
const emailExists = await User.findOne({ email: req.body.email });
if (emailExists) return res.status(400).send("Email already exists");
// hash password
const salt = await bcrypt.genSalt(10);
// hash the password with a salt
const passwordhash = await bcrypt.hash(req.body.password, salt);
// create new user
var user = new User({
name: req.body.name,
email: req.body.email,
password: passwordhash
});
try {
user = await user.save();
res.send({ user: user._id });
} catch {
res.status(400).send(err);
}
};
In Next.js, here is the code for making the http request
handleSubmit = event => {
const { name, email, password } = this.state;
event.preventDefault();
const user = {
name,
email,
password
};
try {
register(user);
} catch (ex) {
console.log(ex);
}
};
export const register = async user => {
const data = await http.post("http://localhost:8000/api/user/register", user);
console.log(data);
return data;
};
In console all I see is the below. So the console.log I am doing in the catch isn't working.
POST http://localhost:8000/api/user/register 422 (Unprocessable Entity)
Uncaught (in promise) Error: Request failed with status code 422
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.handleLoad (xhr.js:59)
That's because the catch statement isn't being run because the function isn't throwing an exception by itself. You should add the error handling inside the function like this:
try {
register(user);
} catch (ex) {
console.log(ex);
}
};
export const register = async user => {
const data = await http.post("http://localhost:8000/api/user/register", user).catch((e) {
throw new Error(e);
});
console.log(data);
return data;
};
I managed to get it working like this:
try {
const response = await register(user);
console.log(response);
} catch (ex) {
if (ex.response && ex.response.status === 422) {
const errors = ex.response.data.errors;
this.setState({ errors });
}
}