2

It's my first post so if anything is missing please let me know. I am working on a blog app and I have a issue with token identification, precisely when I try to create a new blog. The user token is supposed to be sent in the request in order to identify the creator of the blog. It works fine on development mode but when I try in production mode I get "401 unauthorised" and "token undefined".

I know something is wrong with my token but I can’t find where is my mistake and how to fix it. I have searched solutions I saw on similar posts but in vain. I would be grateful for your help.

Below are the parts of my code where I think the problem is, if needed the full project is on my github: https://github.com/AkS-ASJ/blogapp

Here is the frontend request to create a blog

import axios from 'axios'
const baseUrl = '/api/blogs'

let token = null

const setToken = newToken => {
token = `Bearer ${newToken}`
}

const create = async newObject => {
const config = {
headers: { Authorization: token },
}

const response = await axios.post(baseUrl, newObject, 
config)
return response.data
}

Next is the frontend function in app.js

const blogFormRef = useRef()

const createBlog = (blogObject) => {
    blogFormRef.current.toggleVisibility()
    blogService
        .create(blogObject)
        .then(returnedBlog => {
            setBlogs(blogs.concat(returnedBlog))
            setErrorMessage(`A new blog ${blogObject.title} by ${blogObject.author} added`)
            setTimeout(() => {
                setErrorMessage(null)
            }, 5000)
        })
}

Then the function on app.js

const blogsRouter = require('express').Router()
const Blog = require('../models/blog')
const User = require('../models/user')
const jwt = require('jsonwebtoken')

const getTokenFrom = request => {
const authorization = request.get('authorization')
if (authorization && authorization.startsWith('Bearer ')) 
{
    return authorization.replace('Bearer ', '')
}
return null
}

blogsRouter.post('/', async (request, response) => {
const body = request.body

const decodedToken = jwt.verify(getTokenFrom(request), 
process.env.SECRET)
if (!decodedToken.id) {
    return response.status(401).json({ error: 'token invalid' })
}

const user = await User.findById(decodedToken.id)

const blog = new Blog ({
    title: body.title,
    author: body.author,
    url: body.url,
    likes: body.likes || 0,
    user: user._id
})

const savedBlog = await blog.save()
user.blogs = user.blogs.concat(savedBlog._id)
await user.save()


response.status(201).json(savedBlog)
})

And here is the login function with the setToken

 const handleLogin = async (handleLogin) => {
    const username = handleLogin.username;
    const password = handleLogin.password;

  try {
      const user = await loginService.login({
          username, password,
      })

      window.localStorage.setItem(
          'loggedBlogappUser', JSON.stringify(user)
      )
      blogService.setToken(user.token)
      setUser(user)
      setErrorMessage('You are logged in!')
      setTimeout(() => {
          setErrorMessage(null)
      }, 5000)
  } catch (exception) {
      setErrorMessage('Wrong credentials')
      setTimeout(() => {
          setErrorMessage(null)
      }, 5000)
  }
  }

If I forgot something please let me know, and again I am trying to find why my token is undefined when I try to create a new blog. Thanks!

aks.asj
  • 21
  • 3

1 Answers1

3

From what you mentioned the token is saved to localstorage. This answer Set a token in localStorage before test shows the pattern - poster is or was a Cypress team member.

Cypress.Commands.add('login', () => { 
  cy.request({
    method: 'POST',
    url: 'http://localhost:3000/api/users/login',
    body: {
      user: {
        email: 'jake@jake.jake',
        password: 'jakejake',
      }
    }
  })
  .then((resp) => {
    window.localStorage.setItem('jwt', resp.body.user.token)
  })
})

The exact pattern you need to use depends on how the front-end does it:

  • name of token in localstorage (above example is "jwt")
  • property on response that gives you the token (can figure that out by inspection of resp with console.log(resp))

BTW you don't need a custom command, the test structure you have looks fine - just need to follow the cy.request() with a callback to store the value.

  • With your guidance I have found the problem, in "blogs" from "services" folder in frontend, the "setToken" function doesn't work. However I still can't fix it – aks.asj Apr 14 '23 at 11:05