Sunday, 4 June 2017

TypeError: Cannot read property '_id' of undefined in MEAN Stack

What I'm trying to accomplish is to write a message in a database when the user sends a message. I'm using the MEAN stack with angular-cli.

The error I am receiving is:

TypeError: Cannot read property '_id' of undefined
    at JwtStrategy._verify (/Volumes/SSD/Documents/WebProjects/MEANTest/config/passport.js:11:38)
    at /Volumes/SSD/Documents/WebProjects/MEANTest/node_modules/passport-jwt/lib/strategy.js:110:26
    at /Volumes/SSD/Documents/WebProjects/MEANTest/node_modules/jsonwebtoken/verify.js:27:18
    at _combinedTickCallback (internal/process/next_tick.js:73:7)
    at process._tickCallback (internal/process/next_tick.js:104:9)

Here is every bit of code I use to see if someone can figure out what I'm doing wrong.

Node with Express located in '/users':

router.post('/newmessages', passport.authenticate('jwt', {session: false}), function(req, res, next){

var msgID = getMessageID(req.body.user.username);

  let newMessage = Message({
    createdBy:req.body.message.createdBy,
    recipients:req.body.message.recipients,
    dateCreated:getTodayDate(),
    subject:req.body.message.subject,
    message:req.body.message.message,
    read: false,
    previousMessage:req.body.message.previousMessage,
    nextMessage:req.body.message.nextMessage,
    messageID:msgID
  });

  console.log(newMessage);

  Message.checkMessageID(newMessage, function(err, isFound){
    if(err) throw err;
    if(isFound)
    {
      newMessage.messageID = getMessageID(req.body.username);
    }
    else
    {
      Message.createMessage(newMessage, function(err, isCreated){
        if(err) throw err;
        if(isCreated)
        {
          var token = jwt.sign(req.body, config.secret, {
            expiresIn: 604800 // A week worth of seconds
          });

          res.json({
            success: true,
            token: "JWT " + token,
            user: {
              id: req.body._id,
              name: req.body.name,
              username: req.body.username,
              email: req.body.email
            },
            msg: "Your password has been changed."
          });
        }
        else
        {
          var token = jwt.sign(req.body, config.secret, {
            expiresIn: 604800 // A week worth of seconds
          });

          res.json({
            success: true,
            token: "JWT " + token,
            user: {
              id: req.body._id,
              name: req.body.name,
              username: req.body.username,
              email: req.body.email
            },
            msg: "Your password has been changed."
          });
        }
      });
    }
  });
});

Mongoose schema and code:

var mongoose = require('mongoose');
var config = require('../config/messages');

// Message Schema
var messageSchema = mongoose.Schema({
  createdBy: {
    type: String,
    required: true
  },
  recipients:{
    type: String,
    required: true
  },
  dateCreated: {
    type: String,
    required: true
  },
  subject: {
    type: String,
    required: true
  },
  message: {
    type: String,
    required: true
  },
  previousMessage: {
    type: String,
    required: true
  },
  nextMessage: {
    type: String,
    required: true
  },
  read: {
    type:Boolean,
    required: true
  },
  messageID: {
    type: String,
    required: true
  }
});

var secondConn = mongoose.createConnection(config.database);

// On Connection
secondConn.on('connected', function(){
    console.log("Connected to database "+ config.database);
});

// On Error
secondConn.on('error', function(err){
    console.log("Database Error " + err);
});

var Messages = module.exports = secondConn.model('Messages', messageSchema);

module.exports.getAllUserMessages = function(user, callback){
  var query = {createdBy: user};

  Messages.find(query, callback);
};

module.exports.getAllRecpMessages = function(user, callback){
  var query = {recipients: user};

  Messages.find(query, callback);
};

module.exports.deleteMessage = function(list, callback){
  var query, i;

  for(i = 0; i < list.length; i++)
  {
      query = { messageID: list[i].messageID};
      Messages.remove(query, function(err){
      });
  }
};

module.exports.createMessage = function(message, callback){
  message.save(function(err, doc){
    if(err) throw err;
    if(doc === null)
    {
      callback(null, false);
    }
    else
    {
      callback(null, true);
    }
  });
};

// Returns false if no other message has the same ID
module.exports.checkMessageID = function(message, callback){
  var query = {messageID: message.messageID};

  Messages.findOne(query, function(err, message){
    if(err) throw err;
    if(message === null)
    {
      callback(null, false);
    }
    else
    {
      callback(null, true);
    }
  });
};

Angular typescript for '/users/messages':

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../services/auth.service';
import { Router } from '@angular/router';
import { FlashMessagesService } from 'angular2-flash-messages';

@Component({
  selector: 'app-messages',
  templateUrl: './messages.component.html',
  styleUrls: ['./messages.component.css']
})
export class MessagesComponent implements OnInit {
  createdBy: String;
  recipients: String;
  subject: String;
  message: String;
  previousMessage: String;
  nextMessage: String;
  user: Object;

  constructor(private authService: AuthService,
    private router: Router,
    private flashMessage: FlashMessagesService
    ) { }

  ngOnInit() {
    this.authService.getProfile().subscribe(profile => {
      this.user = profile.user;
      this.createdBy = profile.user.username;
    },
    err => {
      console.log(err);
      return false;
    });
  }

// Need to create Message object and pass to Back End
  newMessage(){
    const newMessage = {
      createdBy: this.createdBy,
      recipients: this.recipients,
      subject: this.subject,
      message: this.message,
      previousMessage: " ",
      nextMessage: " "
    }

    this.authService.newMessage(this.user, newMessage).subscribe(data => {
      if(data.success){
        this.authService.storeUserData(data.token, data.user);
        console.log(data);
        this.router.navigate(['profile']);
      }else{
        console.log(data);
        this.router.navigate(['dashboard']);
      }
    });
  }

  getMessages(){

    this.authService.getMessages(this.user).subscribe(data => {
      if(data.success){
        this.authService.storeUserData(data.token, data.user);
        this.router.navigate(['messages']);
        console.log(data);
      }else{
        this.router.navigate(['dashboard']);
      }
    });
  }
}

Angular auth service:

import { Injectable } from '@angular/core';
import { Http, Headers } from '@angular/http';
import 'rxjs/add/operator/map';
import { tokenNotExpired } from 'angular2-jwt';

@Injectable()
export class AuthService {
authToken: any;
user: any;
message: any;

  constructor(private http: Http) { }

// Need to pass Message object to Front End
  newMessage(user, message){
    console.log(user.username + " " + message.createdBy);
    let headers = new Headers();
    this.loadToken();
    headers.append('Authorization', this.authToken);
    headers.append('Content-Type', 'application/json');
    var info = {user, message};
    return this.http.post('/users/newmessages', user, {headers: headers, body: info})
      .map(res => res.json());
  }

  getMessages(user){
    let headers = new Headers();
    this.loadToken();
    headers.append('Authorization', this.authToken);
    headers.append('Content-Type', 'application/json');
    return this.http.post('/users/messages', user, {headers: headers})
      .map(res => res.json());
  }

  registerUser(user){
    let headers = new Headers();
    headers.append('Content-Type', 'application/json');
    return this.http.post('/users/register', user, {headers: headers})
      .map(res => res.json());
  }

  authenticateUser(user){
    let headers = new Headers();
    headers.append('Content-Type', 'application/json');
    return this.http.post('/users/authenticate', user, {headers: headers})
      .map(res => res.json());
  }

  changePassword(user){
    let headers = new Headers();
    this.loadToken();
    headers.append('Authorization', this.authToken);
    headers.append('Content-Type', 'application/json');
    return this.http.post('/users/changepassword', user, {headers: headers})
      .map(res => res.json());
  }

  getProfile(){
    let headers = new Headers();
    this.loadToken();
    headers.append('Authorization', this.authToken);
    headers.append('Content-Type', 'application/json');
    return this.http.get('/users/profile', {headers: headers})
      .map(res => res.json());
  }

  storeUserData(token, user){
    localStorage.setItem('id_token', token);
    localStorage.setItem('user', JSON.stringify(user));
    this.authToken = token;
    this.user = user;
  }

  loadToken(){
    const token = localStorage.getItem('id_token');
    this.authToken = token;
  }

  loggedIn(){
    return tokenNotExpired('id_token');
  }

  logout(){
    this.authToken = null;
    this.user = null;
    localStorage.clear();
  }
}

Just in case, here's my partial user model from the Mongoose Schema:

var mongoose = require('mongoose');
var bcrypt = require('bcryptjs');
var config = require('../config/database');

// User Schema
var userSchema = mongoose.Schema({
  name: {
    type: String
  },
  email:{
    type: String,
    required: true
  },
  username: {
    type: String,
    required: true
  },
  password: {
    type: String,
    required: true
  },
  verify: {
    type: Boolean,
    required: true
  }
});



via A. Angee

No comments:

Post a Comment