MEAN Angular2 JWT Setup

January 15, 2017 by Stuart Kuentzel

Now that Angular 2 is out, I wanted to set up a site using NG2 and Node. The project I made required users to create accounts, login, logout, etc. I did some reading and pulled from some great articles, like this, and this, which I recommend reading. For this tutorial, we’ll create a project using Express, Node, and JSON Web Tokens (JWT). You can check out the full code at Github.

Setup

To begin, we’ll need a few things. You’ll need to have Node installed, which you can do here. You’ll need MongoDB installed to use as our database. Lastly, we’ll need the Angular CLI to generate an Angular 2 project. Once those are downloaded, we’re good to go.

  • Mongo
  • Angular CLI
  • Node

Express Setup & Dependencies

Start by creating this folder structure.

| - /bin
| - /config
| - /http
| -- /controllers
| -- /middleware
| - /models
| - app.js
| - package.json

Open the project in a text editor of your choice, and open the package.json file. Inside of it, paste…

{
  "name": "mea2n",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "bcrypt-nodejs": "0.0.3",
    "body-parser": "~1.15.2",
    "chai": "~1.8.1",
    "debug": "~2.2.0",
    "express": "~4.14.0",
    "jsonwebtoken": "^7.2.1",
    "mocha": "~1.14.0",
    "mongoose": "^4.7.0",
    "morgan": "~1.7.0",
    "serve-favicon": "~2.3.0",
    "should": "~2.1.0"
  }
}

Back in the terminal, run npm install.

We’ll also have to set up a way for Node to run. Inside of the bin folder, create a file www. Inside of it, paste…

#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('../app');
var debug = require('debug')('mea2n:server');
var http = require('http');

/**
 * Get port from environment and store in Express.
 */

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
 * Create HTTP server.
 */

var server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
 * Normalize a port into a number, string, or false.
 */

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
}

This is code lifted from the Express generator, which we’re not using in this project. It will allow us to run our project on localhost:3000 when we’re ready. Let’s move on to create the Angular2 side of this project.

Angular 2 Setup

In the terminal, run ng new my-app. This will create a basic ng2 app for us. Too easy. Let’s run ng build to create a compiled version of the basic app. The CLI will spit out a dist folder, which is what Express will be reading from. All we have to do is hook this stuff up!

Optional Bootstrap

I’m using bootstrap so this demo doesn’t look like balls. Totally optional. In the terminal, cd into my-app. Once inside, run npm install bootstrap --save-dev. Once that is installed, inside of the my-app/angular-cli.json file, add bootstrap to the styles array…

// angular-cli.json

"styles": [
    "../node_modules/bootstrap/dist/css/bootstrap.min.css",
    "styles.css"
],

Wiring up

We’ll have to point to our app in Node. In a text editor, open app.js. Inside of this we’ll write…

//  app.js

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var bodyParser = require('body-parser');

var user = require('./http/controllers/user');

var app = express();

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'my-app/dist')));

// Add headers
app.use(function (req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type,x-access-token');
    res.setHeader('Access-Control-Allow-Credentials', true);
    next();
});

// api
app.use('/api/user', user);

app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'my-app/dist/index.html'));
});

// catch 404 and forward to error handler
app.use( (req, res, next) => {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use( (err, req, res, next) => {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

If this looks familiar, it’s because it’s an altered version of the app.js file the Express generator spits out. Let’s go into a bit of detail to see what’s going on here.

At the top there, we’re pulling in everthing we need for our app. Next, we’re using our logger and bodyParser, so we can log things out to the terminal and parse requests.

We set some headers, which I recommend you customize yourself if you use this for a live site. It allows Angular2 to connect with Express on localhost:3000.

We then tell Express where to look for index when someone hits our page. After that, we create a controller for the Angular app to hit when handling user stuff. Then, we create a catch-all, so if the user hits some random page they’ll be routed back to index. Lastly, we have some error handlers, to make our lives easier when things go awry.

Token config

One aim of our app is to allow users to make accounts, login, and logout. We’ll approach this by using JSON Web Tokens. We already imported what we need to use them in our application. Inside of the config folder, create a file called token.js.

//  token.js

module.exports = {
	'secret': 'doppiaeast',
	'database': 'mongodb://127.0.0.1:27017/test'
}

This does two things, it sets the secret to use for JWT, and points to the database we’ll use. Test is the default Mongo database, feel free to set up a fresh DB and point this to it.

Creating A User Model

We’ll create a User model so Mongoose can help us store and retrieve data in a smooth, organize fashion. Inside of the models folder, create a file called user.js

// user.js

var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');

var userSchema = mongoose.Schema({
    email: {
        type: String,
        required: true,
        unique: true
    },
    password: String
});

userSchema.methods.generateHash = function(password) {
    return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};

userSchema.methods.validPassword = function(password) {
    return bcrypt.compareSync(password, this.password);
};

module.exports = mongoose.model('User', userSchema);

At this point, if you wanted to run npm start from the base of the project, and open localhost:3000 in your browser, you’d see that Express is pointing to our Angular app.

Generating components

In terminal, cd into my-app. From there, run ng g component login. Once that completes, run ng g component register. That’s Angular CLI magic, creating us two new components we can use for this project.

Angular Routing

Inside of the my-app folder, open the src/app folder. Create a new file called app-routing.module.ts. Inside of it, copy and paste…

// app-routing.module.ts

import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import{ LoginComponent } from './login/login.component';
import{ RegisterComponent } from './register/register.component';

const appRoutes: Routes = [
  { 
    path: 'login', 
    component: LoginComponent
  },
  { 
    path: 'register', 
    component: RegisterComponent
  }
];

@NgModule({
  imports: [
    RouterModule.forRoot(appRoutes)
  ],
  exports: [
    RouterModule
  ]
})
export class AppRoutingModule {}

This will create basic routing for the app. We created the file, but now we need to tie it to the Angular app. Open app.module.ts in the same folder…

// app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { AuthService } from './auth.service';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    RegisterComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    AppRoutingModule
  ],
  providers: [AuthService],
  bootstrap: [AppComponent]
})
export class AppModule { }

This is the base of our app, where we import everything we need. As you can see, the CLI automatically added the login and register components for us, as well as the auth service. All we have to do is import the AppRoutingModule and include it in the imports array.

Base App HTML

We’ll leave this sparse so y’all can play with this.

// app.component.html

<header class="container">
  <h1><a routerLink="/">Doppia East</a></h1>
  <nav>
    <ul class="nav nav-pills">
      <li *ngIf="!user">
        <a routerLink="/login">Login</a>
      </li>
      <li *ngIf="!user">
        <a routerLink="/register">Register</a>
      </li>
      <li *ngIf="user"><button (click)="logout()" class="btn">Logout</button></li>
    </ul>
  </nav>
</header>

<div class="container">

  <p *ngIf="user">Logged in as: {{user.email}}</p>
  <p *ngIf="!user">Not logged in</p>

  <router-outlet></router-outlet>
</div>

If we save and run ng build --watch, then navigate to localhost:3000, you can navigate around between components.

Let’s also build some functionality out for the app.component. Inside app.component.ts

// app.component.ts

import { Component, OnInit, OnDestroy } from '@angular/core';
import { AuthService } from './auth.service';
import { User } from './users/user';
import { Subscription }   from 'rxjs/Subscription';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [AuthService]
})
export class AppComponent implements OnInit, OnDestroy {
  articles;
  user: User;
  message: String;
  subscription: Subscription;

  constructor( private authService: AuthService) {
    this.articles = [];

    this.subscription = authService.user$.subscribe( (user) => this.user = user )

  }

  ngOnInit() {
    this.user = JSON.parse(localStorage.getItem('currentUser'));

    //example of verification
    this.authService.verify().subscribe( (res) => this.message = res['message']);
  }

  ngOnDestroy() {
    // prevent memory leak when component destroyed
    this.subscription.unsubscribe();
  }

  logout() {
    this.authService.logout();
    this.user = null;
    this.message = "Logged out";
  }

}

A few things to note here, we’ll be building a authService, and subscribing to the user$ Observable. This is a way for components to pass data around. Basically, when we register a user in the register component, we want to pass the user back up to this app component. That way we can pass the user around anywhere in the app, block routes, etc. Also, we create a logout function, which also hits the auth service we will be building.

Creating a User class

We’ll want a User class to keep our code organized. When we create a new instance of a user, we’ll get useful error handling from TypeScript. Inside of my-app/src/app create a users folder. Once you’ve created that folder, in the terminal run ng g class users/user.

Open the user.ts file inside of our Angular app and paste…

// user.ts

export class User {
  email: string;
  password: string;

  constructor()
  {
    this.email = '';
    this.password = '';
  }

}

The Auth Service

We’ll also need an auth service, so let’s create that in the terminal. Run ng g service auth to create a service to handle authentication.

// auth.service.ts

import { Injectable } from '@angular/core';
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { Observable }     from 'rxjs/observable';
import { User } from './users/user';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/map';

@Injectable()
export class AuthService {

  private base_url = 'http://127.0.0.1:3000/api/user';
  token: string;
  private userSource = new Subject<User>();
  user$ = this.userSource.asObservable();

  constructor(public http: Http) { }

  setUser(user: User) {
    this.userSource.next(user);
  }

  registerUser(user: User): Observable<boolean> {
    let body = JSON.stringify(user);
    let headers = new Headers();
		headers.append('Content-Type', 'application/json');
    let options = new RequestOptions({ headers: headers });
    return this.http.post(`${this.base_url}/register`, body, options).map( (res) => this.setToken(res) );
  }

  loginUser(user): Observable<Object> {
    let body = JSON.stringify(user);
    let headers = new Headers();
		headers.append('Content-Type', 'application/json');
    let options = new RequestOptions({ headers: headers });

    return this.http.post(`${this.base_url}/login`, body, options).map( (res) => this.setToken(res) );
  }

  logout() {
    this.token = null;
    localStorage.removeItem('currentUser');
  }

  verify(): Observable<Object> {

    let currUser = JSON.parse(localStorage.getItem('currentUser')); 
    let token = ( currUser && 'token' in currUser) ? currUser.token : this.token;
    let headers = new Headers({ 'x-access-token': token });
    let options = new RequestOptions({ headers: headers });
    return this.http.get(`${this.base_url}/check-state`, options).map( res => this.parseRes(res) );
    
  }

  setToken(res){
    let body = JSON.parse(res['_body']);
    if( body['success'] == true ){
      this.token = body['token'];
      localStorage.setItem('currentUser', JSON.stringify({ 
        email: body['user']['email'], 
        token: this.token 
      }));
    }
    return body;
  }

  parseRes(res){
    let body = JSON.parse(res['_body']);
    return body;
  }

}

We’re doing a lot here, so let’s break it apart a bit. After we import everything we need, inside the AuthService class, we set up our variables. One of them is creating a new Observable which we’ll use to pass the auth and user info up to the head App component. More on that later. Below that, we have our functions which we will be calling inside of the Login, Register, and head App components. Inside of the last function there, setToken(res), we are setting the user and JWT token inside the browser’s local storage.

Register a User

Let’s try registering a user. Inside of the register component, open register.component.html

// register.component.html

<main>
  <div class="centered-block">
    <h2>Register</h2>
    <form (ngSubmit)="registerUser(user)">

      <div class="form-group">
        <label for="email">Email</label>
        <input type="email" id="email" [(ngModel)]="user.email" name="email" required>
      </div>

      <div class="form-group">
        <label for="email">Password</label>
        <input type="password" id="password" [(ngModel)]="user.password" name="password" required>
      </div>

      <button type="submit" class="btn btn-default">Submit</button>
    </form>

    <p>{{message}}</p>
  </div>
</main>

Now that we have the HTML, it’s time to write the component. Inside of register.component.ts, we’ll add…

// register.component.ts

import { Component } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { User } from '../users/user';
import { AuthService} from '../auth.service';

@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent {

  user: User;
  message: string = '';

  constructor(private authService: AuthService, private router: Router ) {
    this.user = new User;
  }

  registerUser(user) {
    this.authService.registerUser(user).subscribe( (res) => {
      if( res['success'] == true ) {
        this.authService.setUser(res['user']);
        this.router.navigate(['']);
      } else {
        this.message = res['message'];
      }
    })
  }

}

The magic here is the registerUser function. It passes the new user to our authService. When we get a response from our API, we do a couple things.

If it was successful, we pass the user we get from the response to the setUser function in the authService. Remember in app.component when we subscribed to user$? By passing a user to authService.setUser, we’re allowing the components to pass data between eachother. Now the head app component has an instance of user that we can do anything with. If successful, we navigate back to index.

If not successfuly, our API will provide us with a message which we display in the HTML.

Angular Login

Once registered, we’ll log the user in. Let’s write out this HTML and functionality next. Inside of login.component.html, we’ll write…

// login.component.html

<main>
  <h2>Login</h2>
  <form (ngSubmit)="loginUser(user)">
    <div class="form-group">
      <label for="username">Email</label>
      <input type="email" id="email" [(ngModel)]="user.email" name="email" required>
    </div>

    <div class="form-group">
      <label for="password">Password</label>
      <input type="password" id="password" [(ngModel)]="user.password" name="password" required>
    </div>

    <button type="submit" class="btn btn-default">Submit</button>

  </form>

  <p *ngIf="message">{{message}}</p>

</main>

After the HTML, we’ll have to write out the functionality inside of login.component.ts.

// login.component.ts

import { Component } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { User } from '../users/user';
import { AuthService } from '../auth.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent {

  loginForm: FormGroup;
  message: String;
  user: User;
  user_status: boolean;

  constructor(private router: Router, private authService: AuthService) {
    this.user = new User;
  }

  loginUser(user){
    this.authService.loginUser(user).subscribe( res => {
      this.user_status = res['success']; 
      if(res['success'] == true) {
        this.authService.setUser(res['user']);
        this.router.navigate(['']);
      } else {
        this.message = res['message'];
      }
    });
  }

}

This is very similar to the RegisterComponent functionality. When the submit button is clicked in the HTML, we hit the loginUser function and pass it the user. This passes the user to the service, where we will hit our Node API. If it returns successfully, we set the user using an observable to pass the data up to the head app, then navigate back tot he index page. On fail, we display a message.

Node Auth & JWT

Now that the front end is sending user data, we’ll need to handle it on the Node end. Back to the root of your project, if you haven’t yet created http/controllers/user.js, go ahead and do that now. This is where our Angular calls will be handled.

// http/controllers/user.js

var express = require('express');
var app = express();
var router = express.Router();
var User = require('../../models/user');
var jwt = require('jsonwebtoken');
var config = require('../../config/token');
var auth = require('../middleware/auth');

router.get('/check-state', auth.verifyToken, (req, res) => {

  let content = {
    success: true,
    message: 'Successfully logged in'
  }
  res.send(content);

});

router.post('/register', (req, res) => {

  var reqUser = req.body;

  process.nextTick( () => {
    User.findOne({ 'email': reqUser.email }, (err, user) => {
      if(err)
        return done(err);
      
      if(user){
        let content = {
          success: false,
          message: 'user already exists'
        };
        res.send(content);
        return;
      } else {
        var newUser = new User();
        newUser.email = reqUser.email;
        newUser.password = newUser.generateHash(reqUser.password);
        newUser.save( (err) => {
            if( err )
                throw err;

            let token = jwt.sign(newUser, config.secret, {
              expiresIn : 60*60*24
            });
            let content = {
              user: newUser,
              success: true,
              message: 'You created a new user',
              token: token
            };
            res.send(content);
            return;
        })
      }
    })
  })
});

router.post('/login', (req, res) => {

  var reqUser = req.body;

  User.findOne({'email' : reqUser.email}, (err, user) => {

    if( err )
      return done(err);

    if( !user ) {
      let content = {
        success: false,
        message: 'User does not exists'
      };
      res.send(content);
      return;
    }

    if( !user.validPassword(reqUser.password) ){
      let content = {
        success: false,
        message: 'Incorrect password'
      };
      res.send(content);
      return;
    }

    let token = jwt.sign(user, config.secret, {
      expiresIn : 60*60*24
    });
    let content = {
      user: user,
      success: true,
      message: 'You logged in',
      token: token
    };
    res.send(content);

  })

});

module.exports = router;

Here we are setting up the login, register, and check-state routes. In this case, we’re using the check-state route as a test and proof of concept. When someone hits the Angular app, the front-end hits this route to make a request with or without a token, and validates that token if the user is logged in. The check-state route isn’t important, but what is important here is using middleware to validate whether or not a user is logged in. We can add this middleware to any route we want if we want that route protected to logged in users only.

We also have the login and register routes. Note that if successful, we’ll create a token using JWT and send it back with the response. When we get back that response, the Angular stores it in localStorage. Now, anytime we want to make a request restricted to logged-in users, we just get that token from localStore and pass it in the headers.

Auth Middleware for Routes

In the http/middleware folder, create a new file named auth.js.

// auth.js

var config = require('../../config/token');
var jwt = require('jsonwebtoken');

module.exports = {

    verifyToken: ( (req, res, next) => {
        let token = req.body.token || req.query.token || req.headers['x-access-token'];

        if( token ) {

            jwt.verify(token, config.secret, (err, decoded) => {
   
                if (err) {
                    return res.json({ success: false, message: 'Failed to authenticate token.' });    
                } else {
                    // all good, continue
                    req.decoded = decoded; 
                    next();
                }
            });

        }  else {

            res.send({ success: false, message: 'No token exists.' });
            
        }
    })

}

This will check if a token exists and validate it for us using the JWT library. If either a token doesn’t exist or it can’t be validated, we stop then from continuing on to the route, and respond wth an error message.

Wrapping up

That’s it! The flow is simple, our angular app makes calls for our data, we respond with some JSON, and use JWT to authorize our users. Build something cool. Check out the code for the project at Github.

© 2018