1

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)
  }
})
Veljko Kukic
  • 107
  • 1
  • 4
  • 12
  • And are you including the JWT token in the headers of the DELETE request? Given the token is passed down as part of the login request as part of the body and not a cookie, this will need to be manually done. – James Dec 25 '19 at 12:23
  • Im including it via this function const getTokenFrom = request => { const authorization = request.get('authorization') if (authorization && authorization.toLowerCase().startsWith('bearer ')) { return authorization.substring(7) } return null } – Veljko Kukic Dec 25 '19 at 12:25
  • But it returns null instead of token – Veljko Kukic Dec 25 '19 at 12:26
  • In delete request, are you sending the token same as in post request? Can you show the request headers in your delete request? – SuleymanSah Dec 25 '19 at 12:37
  • How to show request headers? – Veljko Kukic Dec 25 '19 at 12:44
  • Click the data -> headers in the browser console log – SuleymanSah Dec 25 '19 at 12:45
  • I edited post..Check post method that i showed u. Almost the same and i get the token from it but when i do the same in delete method i get null – Veljko Kukic Dec 25 '19 at 12:46
  • https://prnt.sc/qfjtsq -- this is in headers – Veljko Kukic Dec 25 '19 at 12:47
  • Hmm, this looks like response headers, can you log request headers? – SuleymanSah Dec 25 '19 at 12:48
  • When i lost request.headers i get this on backend server https://prnt.sc/qfjv60 – Veljko Kukic Dec 25 '19 at 12:50
  • Somehow i need to get token. Im not autorized to delete without token. If i delete token code everthing works fine but i can delete all blog posts and I want to restrict that only user who made a blog can delete blog. Problem is that im constantly getting NULL when i try to define token variable – Veljko Kukic Dec 25 '19 at 12:52
  • @VeljkoKukic this type of token checking in every route is not good. Do you want a better solution? – SuleymanSah Dec 25 '19 at 12:55
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/204833/discussion-between-suleymansah-and-veljko-kukic). – SuleymanSah Dec 25 '19 at 13:00
  • @VeljkoKukic you need to be including the Authorization token in the DELETE request sent from the client, are you doing this? Show the client code you use to send the request – James Dec 25 '19 at 13:31

2 Answers2

3

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.....

SuleymanSah
  • 17,153
  • 5
  • 33
  • 54
0

" 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.

Frank Guo
  • 547
  • 7
  • 8