Quantcast
Channel: Node.js – SitePoint
Viewing all 225 articles
Browse latest View live

Build a Node.js CRUD App Using React and FeathersJS

$
0
0
Build a Node.js CRUD App Using React and FeathersJS

Building a modern project requires splitting the logic into front-end and back-end code. The reason behind this move is to promote code re-usability. For example, we may need to build a native mobile application that accesses the back-end API. Or we may be developing a module that will be part of a large modular platform.

An operator, sat at an old-fashioned telephone switchboard - Build a CRUD App Using React, Redux and FeathersJS

The popular way of building a server-side API is to use Node.js with a library like Express or Restify. These libraries make creating RESTful routes easy. The problem with these libraries is that we'll find ourselves writing a ton of repetitive code. We'll also need to write code for authorization and other middleware logic.

To escape this dilemma, we can use a framework like Feathers to help us generate an API in just a few commands.

What makes Feathers amazing is its simplicity. The entire framework is modular and we only need to install the features we need. Feathers itself is a thin wrapper built on top of Express, where they've added new features — services and hooks. Feathers also allows us to effortlessly send and receive data over WebSockets.

Prerequisites

To follow along with this tutorial, you'll need the following things installed on your machine:

  • Node.js v12+ and an up-to-date version of npm. Check this tutorial if you need help getting set up.
  • MongoDB v4.2+. Check this tutorial if you need help getting set up.
  • Yarn package manager — installed using npm i -g yarn.

It will also help if you’re familiar with the following topics:

  • how to write modern JavaScript
  • flow control in modern JavaScript (e.g. async ... await)
  • the basics of React
  • the basics of REST APIs

Also, please note that you can find the completed project code on GitHub.

Scaffold the App

We're going to build a CRUD contact manager application using Node.js, React, Feathers and MongoDB.

In this tutorial, I'll show you how to build the application from the bottom up. We'll kick-start our project using the popular create-react-app tool.

You can install it like so:

npm install -g create-react-app

Then create a new project:

# scaffold a new react project
create-react-app react-contact-manager
cd react-contact-manager

# delete unnecessary files
rm src/logo.svg src/App.css src/serviceWorker.js

Use your favorite code editor and remove all the content in src/index.css. Then open src/App.js and rewrite the code like this:

import React from 'react';

const App = () => {
  return (
    <div>
      <h1>Contact Manager</h1>
    </div>
  );
};

export default App;

Run yarn start from the react-contact-manager directory to start the project. Your browser should automatically open http://localhost:3000 and you should see the heading “Contact Manager”. Quickly check the console tab to ensure that the project is running cleanly with no warnings or errors, and if everything is running smoothly, use Ctrl + C to stop the server.

Build the API Server with Feathers

Let's proceed with generating the back-end API for our CRUD project using the feathers-cli tool:

# Install Feathers command-line tool
npm install @feathersjs/cli -g

# Create directory for the back-end code
# Run this command in the `react-contact-manager` directory
mkdir backend
cd backend

# Generate a feathers back-end API server
feathers generate app

? Do you want to use JavaScript or TypeScript? JavaScript
? Project name backend
? Description contacts API server
? What folder should the source files live in? src
? Which package manager are you using (has to be installed globally)? Yarn
? What type of API are you making? REST, Realtime via Socket.io
? Which testing framework do you prefer? Mocha + assert
? This app uses authentication No

# Ensure Mongodb is running
sudo service mongod start
sudo service mongod status

● mongod.service - MongoDB Database Server
   Loaded: loaded (/lib/systemd/system/mongod.service; disabled; vendor preset: enabled)
   Active: active (running) since Wed 2020-01-22 11:22:51 EAT; 6s ago
     Docs: https://docs.mongodb.org/manual
 Main PID: 13571 (mongod)
   CGroup: /system.slice/mongod.service
           └─13571 /usr/bin/mongod --config /etc/mongod.conf

# Generate RESTful routes for Contact Model
feathers generate service

? What kind of service is it? Mongoose
? What is the name of the service? contacts
? Which path should the service be registered on? /contacts
? What is the database connection string? mongodb://localhost:27017/contactsdb

# Install email and unique field validation
yarn add mongoose-type-email

Let's open backend/config/default.json. This is where we can configure our MongoDB connection parameters and other settings. Change the default paginate value to 50, since front-end pagination won't be covered in this tutorial:

{
  "host": "localhost",
  "port": 3030,
  "public": "../public/",
  "paginate": {
    "default": 50,
    "max": 50
  },
  "mongodb": "mongodb://localhost:27017/contactsdb"
}

Open backend/src/models/contact.model.js and update the code as follows:

require('mongoose-type-email');

module.exports = function (app) {
  const modelName = 'contacts';
  const mongooseClient = app.get('mongooseClient');
  const { Schema } = mongooseClient;
  const schema = new Schema({
    name : {
      first: {
        type: String,
        required: [true, 'First Name is required']
      },
      last: {
        type: String,
        required: false
      }
    },
    email : {
      type: mongooseClient.SchemaTypes.Email,
      required: [true, 'Email is required']
    },
    phone : {
      type: String,
      required: [true, 'Phone is required'],
      validate: {
        validator: function(v) {
          return /^\+(?:[0-9] ?){6,14}[0-9]$/.test(v);
        },
        message: '{VALUE} is not a valid international phone number!'
      }
    }
  }, {
    timestamps: true
  });

  ...

  return  mongooseClient.model(modelName,  schema);
};

Mongoose introduces a new feature called timestamps, which inserts two new fields for you — createdAt and updatedAt. These two fields will be populated automatically whenever we create or update a record. We've also installed the mongoose-type-email plugin to perform email validation on the server.

Now, open backend/src/mongoose.js and change this line:

{ useCreateIndex: true, useNewUrlParser: true }

to:

{ useCreateIndex: true, useNewUrlParser: true, useUnifiedTopology: true }

This will squash an annoying deprecation warning.

Open a new terminal and execute yarn test inside the backend directory. You should have all the tests running successfully. Then, go ahead and execute yarn start to start the back-end server. Once the server has initialized, it should print 'Feathers application started on localhost:3030' to the console.

Launch your browser and access the URL http://localhost:3030/contacts. You should expect to receive the following JSON response:

{"total":0,"limit":50,"skip":0,"data":[]}

Test the API with Postwoman

Now let's use Postwoman to confirm all of our endpoints are working properly.

First, let's create a contact. This link will open Postwoman with everything set up to send a POST request to the /contacts endpoint. Make sure Raw input enabled is set to on, then press the green Send button to create a new contact. The response should be something like this:

{
  "_id": "5e36f3eb8828f64ac1b2166c",
  "name": {
    "first": "Tony",
    "last": "Stark"
  },
  "phone": "+18138683770",
  "email": "tony@starkenterprises.com",
  "createdAt": "2020-02-02T16:08:11.742Z",
  "updatedAt": "2020-02-02T16:08:11.742Z",
  "__v": 0
}

Now let's retrieve our newly created contact. This link will open Postwoman ready to send a GET request to the /contacts endpoint. When you press the Send button, you should get a response like this:

{
  "total": 1,
  "limit": 50,
  "skip": 0,
  "data": [
    {
      "_id": "5e36f3eb8828f64ac1b2166c",
      "name": {
        "first": "Tony",
        "last": "Stark"
      },
      "phone": "+18138683770",
      "email": "tony@starkenterprises.com",
      "createdAt": "2020-02-02T16:08:11.742Z",
      "updatedAt": "2020-02-02T16:08:11.742Z",
      "__v": 0
    }
  ]
}

We can show an individual contact in Postwoman by sending a GET request to http://localhost:3030/contacts/<_id>. The _id field will always be unique, so you'll need to copy it out of the response you received in the previous step. This is the link for the above example. Pressing Send will show the contact.

We can update a contact by sending a PUT request to http://localhost:3030/contacts/<_id> and passing it the updated data as JSON. This is the link for the above example. Pressing Send will update the contact.

Finally we can remove our contact by sending a DELETE request to the same address — that is, http://localhost:3030/contacts/<_id>. This is the link for the above example. Pressing Send will delete the contact.

Postwoman is an very versatile tool and I encourage you to use it to satisfy yourself that your API is working as expected, before moving on to the next step.

The post Build a Node.js CRUD App Using React and FeathersJS appeared first on SitePoint.


MEAN Stack: Build an App with Angular and the Angular CLI

$
0
0
MEAN Stack: Build an App with Angular and the Angular CLI

In this tutorial, we’re going to look at managing user authentication in the MEAN stack. We’ll use the most common MEAN architecture of having an Angular single-page app using a REST API built with Node, Express and MongoDB.

When thinking about user authentication, we need to tackle the following things:

  1. let a user register
  2. save user data, but never directly store passwords
  3. let a returning user log in
  4. keep a logged in user’s session alive between page visits
  5. have some pages that can only been seen by logged in users
  6. change output to the screen depending on logged in status (for example, a “login” button or a “my profile” button).

Before we dive into the code, let’s take a few minutes for a high-level look at how authentication is going to work in the MEAN stack.

The MEAN Stack Authentication Flow

So what does authentication look like in the MEAN stack?

Still keeping this at a high level, these are the components of the flow:

  • user data is stored in MongoDB, with the passwords hashed
  • CRUD functions are built in an Express API — Create (register), Read (login, get profile), Update, Delete
  • an Angular application calls the API and deals with the responses
  • the Express API generates a JSON Web Token (JWT, pronounced “Jot”) upon registration or login, and passes this to the Angular application
  • the Angular application stores the JWT in order to maintain the user’s session
  • the Angular application checks the validity of the JWT when displaying protected views
  • the Angular application passes the JWT back to Express when calling protected API routes.

JWTs are preferred over cookies for maintaining the session state in the browser. Cookies are better for maintaining state when using a server-side application.

The Example Application

The code for this tutorial is available on GitHub. To run the application, you’ll need to have Node.js installed, along with MongoDB. (For instructions on how to install, please refer to Mongo’s official documentation — Windows, Linux, macOS).

The Angular App

To keep the example in this tutorial simple, we’ll start with an Angular app with four pages:

  1. home page
  2. register page
  3. login page
  4. profile page

The pages are pretty basic and look like this to start with:

Screenshots of the app

The profile page will only be accessible to authenticated users. All the files for the Angular app are in a folder inside the Angular CLI app called /client.

We’ll use the Angular CLI for building and running the local server. If you’re unfamiliar with the Angular CLI, refer to the Building a Todo App with Angular CLI tutorial to get started.

The REST API

We’ll also start off with the skeleton of a REST API built with Node, Express and MongoDB, using Mongoose to manage the schemas. This API should initially have three routes:

  1. /api/register (POST), to handle new users registering
  2. /api/login (POST), to handle returning users logging in
  3. /api/profile/USERID (GET), to return profile details when given a USERID

Let's set that up now. We can use the express-generator tool to create a lot of the boiler plate for us. If this is new for you, we have a tutorial on using it here.

Install it with npm i -g express-generator. Then, create a new Express app, choosing Pug as the view engine:

express -v pug mean-authentication

When the generator has run, change into the project directory and install the dependencies:

cd mean-authentication
npm i

At the time of writing, this pulls in an outdated version of Pug. Let's fix that:

npm i pug@latest

We can also install Mongoose while we’re at it:

npm i mongoose

Next, we need to create our folder structure.

  • Remove the public folder: rm -rf public.
  • Create an api directory: mkdir api.
  • Create a controllers, a models, and a routes directory in the api directory: mkdir -p api/{controllers,models,routes}.
  • Create a authenication.js file and a profile.js file in the controllers directory: touch api/controllers/{authentication.js,profile.js}.
  • Create a db.js file and a users.js file in the models directory: touch api/models/{db.js,users.js}.
  • Create an index.js file in the routes directory: touch api/routes/index.js.

When you're done, things should look like this:

.
└── api
    ├── controllers
    │   ├── authentication.js
    │   └── profile.js
    ├── models
    │   ├── db.js
    │   └── users.js
    └── routes
        └── index.js

Now let's add the API functionality. Replace the code in app.js with the following:

require('./api/models/db');

const cookieParser = require('cookie-parser');
const createError = require('http-errors');
const express = require('express');
const logger = require('morgan');
const path = require('path');

const routesApi = require('./api/routes/index');

const app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

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

// catch 404 and forward to error handler
app.use((req, res, next) => {
  next(createError(404));
});

// 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;

Add the following to api/models/db.js:

require('./users');
const mongoose = require('mongoose');
const dbURI = 'mongodb://localhost:27017/meanAuth';

mongoose.set('useCreateIndex', true);
mongoose.connect(dbURI, {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

mongoose.connection.on('connected', () => {
  console.log(`Mongoose connected to ${dbURI}`);
});
mongoose.connection.on('error', (err) => {
  console.log(`Mongoose connection error: ${err}`);
});
mongoose.connection.on('disconnected', () => {
  console.log('Mongoose disconnected');
});

Add the following to api/routes/index.js:

const ctrlAuth = require('../controllers/authentication');
const ctrlProfile = require('../controllers/profile');

const express = require('express');
const router = express.Router();

// profile
router.get('/profile/:userid', ctrlProfile.profileRead);

// authentication
router.post('/register', ctrlAuth.register);
router.post('/login', ctrlAuth.login);

module.exports = router;

Add the following to api/controllers/profile.js:

module.exports.profileRead = (req, res) => {
  console.log(`Reading profile ID: ${req.params.userid}`);
  res.status(200);
  res.json({
    message : `Profile read: ${req.params.userid}`
  });
};

Add the following to api/controllers/authentication.js:

module.exports.register = (req, res) => {
  console.log(`Registering user: ${req.body.email}`);
  res.status(200);
  res.json({
    message : `User registered: ${req.body.email}`
  });
};

module.exports.login = (req, res) => {
  console.log(`Logging in user: ${req.body.email}`);
  res.status(200);
  res.json({
    message : `User logged in: ${req.body.email}`
  });
};

Ensure that Mongo is running and then, finally, start the server with npm run start. If everything is configured properly, you should see a message in your terminal that Mongoose is connected to mongodb://localhost:27017/meanAuth, and you should now be able to make requests to, and get responses from, the API. You can test this with a tool such as Postman.

The post MEAN Stack: Build an App with Angular and the Angular CLI appeared first on SitePoint.

Create New Express.js Apps in Minutes with Express Generator

$
0
0
Create New Express.js Apps in Minutes with Express Generator

Express.js is a Node.js web framework that has gained immense popularity due to its simplicity. It has easy-to-use routing and simple support for view engines, putting it far ahead of the basic Node HTTP server.

However, starting a new Express application requires a certain amount of boilerplate code: starting a new server instance, configuring a view engine, and setting up error handling.

Although there are various starter projects and boilerplates available, Express has its own command-line tool that makes it easy to start new apps, called the express-generator.

What is Express?

Express has a lot of features built in, and a lot more features you can get from other packages that integrate seamlessly, but there are three main things it does for you out of the box:

  1. Routing. This is how /home /blog and /about all give you different pages. Express makes it easy for you to modularize this code by allowing you to put different routes in different files.
  2. Middleware. If you’re new to the term, basically middleware is “software glue”. It accesses requests before your routes get them, allowing them to handle hard-to-do stuff like cookie parsing, file uploads, errors, and more.
  3. Views. Views are how HTML pages are rendered with custom content. You pass in the data you want to be rendered and Express will render it with your given view engine.

Getting Started

The first thing you’ll need is to get Node and npm installed on your machine. To do this, either head to the official Node download page and grab the correct binaries for your system, or use a version manager such as nvm. We cover installing Node using a version manager in our quick tip, “Install Multiple Versions of Node.js Using nvm”.

Starting a new project with the Express generator is as simple as running a few commands:

npm install express-generator -g

This installs the Express generator as a global package, allowing you to run the express command in your terminal:

express myapp

This creates a new Express project called myapp, which is then placed inside of the myapp directory:

cd myapp

If you’re unfamiliar with terminal commands, this one puts you inside of the myapp directory:

npm install

If you’re unfamiliar with npm, it’s the default Node.js package manager. Running npm install installs all dependencies for the project. By default, the express-generator includes several packages that are commonly used with an Express server.

Options

The generator CLI takes half a dozen arguments, but the two most useful ones are the following:

  • -v . This lets you select a view engine to install. The default is jade. Although this still works, it has been deprecated and you should always specify an alternative engine.
  • -c . By default, the generator creates a very basic CSS file for you, but selecting a CSS engine will configure your new app with middleware to compile any of the above options.

Now that we’ve got our project set up and dependencies installed, we can start the new server by running the following:

npm start

Then browse to http://localhost:3000 in your browser.

Application Structure

The generated Express application starts off with four folders.

bin

The bin folder contains the executable file that starts your app. It starts the server (on port 3000, if no alternative is supplied) and sets up some basic error handling. You don’t really need to worry about this file, because npm start will run it for you.

public

The public folder is one of the important ones: ​everything​ in this folder is accessible to people connecting to your application. In this folder, you’ll put JavaScript, CSS, images, and other assets that people need when they load your website.

routes

The routes folder is where you’ll put your router files. The generator creates two files, index.js and users.js, which serve as examples of how to separate out your application’s route configuration.

Usually, you’ll have a different file here for each major route on your website. So you might have files called blog.js, home.js, and/or about.js in this folder.

views

The views folder is where you have the files used by your templating engine. The generator will configure Express to look in here for a matching view when you call the render method.

Outside of these folders, there’s one file that you should know well.

app.js

The app.js file is special, because it sets up your Express application and glues all of the different parts together. Let’s walk through what it does. Here’s how the file starts:

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

These first six lines of the file are required. If you’re new to Node, be sure to read “Understanding module.exports and exports in Node.js”.

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

The next two lines of code require the different route files that the Express generator sets up by default: routes and users.

var app = express();

After that, we create a new app by calling express(). The app variable contains all of the settings and routes for your application. This object glues together your application.

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

Once the app instance is created, the templating engine is set up for rendering views. This is where you’d change the path to your view files if necessary.

After this, you’ll see Express being configured to use middleware. The generator installs several common pieces of middleware that you’re likely to use in a web application:

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
  • logger. When you run your app, you might notice that all the routes that are requested are logged to the console. If you want to disable this, you can just comment out this middleware.
  • express.json. You might notice that there are two lines for parsing the body of incoming HTTP requests. The first line handles when JSON is sent via a POST request and it puts this data in request.body.
  • express.urlencoded. The second line parses query string data in the URL (e.g. /profile?id=5) and puts this in request.query.
  • cookieParser. This takes all the cookies the client sends and puts them in request.cookies. It also allows you to modify them before sending them back to the client, by changing response.cookies.
  • express.static. This middleware serves static assets from your public folder. If you wanted to rename or move the public folder, you can change the path here.

Next up is the routing:

app.use('/', indexRouter);
app.use('/users', usersRouter);

Here, the example route files that were required are attached to our app. If you need to add additional routes, you’d do it here.

All the code after this is used for error handling. You usually won’t have to modify this code unless you want to change the way Express handles errors. By default, it’s set up to show the error that occurred in the route when you’re in development mode.

The post Create New Express.js Apps in Minutes with Express Generator appeared first on SitePoint.

Local Authentication Using Passport in Node.js

$
0
0
Local Authentication Using Passport in Node.js

A common requirement when building a web app is to implement a login system, so that users can authenticate themselves before gaining access to protected views or resources. Luckily for those building Node apps, there’s a middleware called Passport that can be dropped into any Express-based web application to provide authentication mechanisms in only a few commands.

In this tutorial, I’ll demonstrate how to use Passport to implement local authentication (that is, logging in with a username and password) with a MongoDB back end. If you’re looking to implement authentication via the likes of Facebook or GitHub, please refer to this tutorial.

As ever, all of the code for this article is available for download on GitHub.

Prerequisites

To follow along with this tutorial, you’ll need to have Node and MongoDB installed on your machine.

You can install Node by heading to the official Node download page and grabbing the correct binaries for your system. Alternatively, you can use a version manager — a program that allows you to install multiple versions of Node and switch between them at will. If you fancy going this route, please consult our quick tip, “Install Multiple Versions of Node.js Using nvm”.

MongoDB comes in various editions. The one we’re interested in is the MongoDB Community Edition.

The project’s home page has excellent documentation and I won’t try to replicate that here. Rather, I’ll offer you links to instructions for each of the main operating systems:

If you use a non-Ubuntu–based version of Linux, you can check out this page for installation instructions for other distros. MongoDB is also normally available through the official Linux software channels, but sometimes this will pull in an outdated version.

Note: You don’t need to enter your name and address to download MongoDB. If prompted, you can normally dismiss the dialog.

If you’d like a quick refresher on using MongoDB, check out our beginner’s guide, “An Introduction to MongoDB”.

Authentication Strategies: Session vs JWT

Before we begin, let’s talk briefly about authentication choices.

Many of the tutorials online today will opt for token-based authentication using JSON Web Tokens (JWTs). This approach is probably the simplest and most popular one nowadays. It relegates part of the authentication responsibility to the client and makes them sign a token that’s sent with every request, to keep the user authenticated.

Session-based authentication has been around longer. This method relegates the weight of the authentication to the server. It uses cookies and sees the Node application and database work together to keep track of a user’s authentication state.

In this tutorial, we’ll be using session-based authentication, which is at the heart of the passport-local strategy.

Both methods have their advantages and drawbacks. If you’d like to read more into the difference between the two, this Stack Overflow thread might be a good place to start.

Creating the Project

Once all of the prerequisite software is set up, we can get started.

We’ll begin by creating the folder for our app and then accessing that folder on the terminal:

mkdir AuthApp
cd AuthApp

To create the node app, we’ll use the following command:

npm init

You’ll be prompted to provide some information for Node’s package.json. Just keep hitting Return to accept the default configuration (or use the -y flag).

Setting up Express

Now we need to install Express. Go to the terminal and enter this command:

npm install express

We’ll also need to install the body-parser middleware which is used to parse the request body that Passport uses to authenticate the user. And we’ll need to install the express-session middleware.

Let’s do that. Run the following command:

npm install body-parser express-session

When that’s done, create an index.js file in the root folder of your app and add the following content to it:

/*  EXPRESS SETUP  */

const express = require('express');
const app = express();

app.use(express.static(__dirname));

const bodyParser = require('body-parser');
const expressSession = require('express-session')({
  secret: 'secret',
  resave: false,
  saveUninitialized: false
});

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressSession);

const port = process.env.PORT || 3000;
app.listen(port, () => console.log('App listening on port ' + port));

First, we require Express and create our Express app by calling express(). Then we define the directory from which to serve our static files.

The next line sees us require the body-parser middleware, which will help us parse the body of our requests. We’re also adding the express-session middleware to help us save the session cookie.

As you can, see we’re configuring express-session with a secret to sign the session ID cookie (you should choose a unique value here), and two other fields, resave and saveUninitialized. The resave field forces the session to be saved back to the session store, and the saveUninitialized field forces a session that is “uninitialized” to be saved to the store. To learn more about them, check out their documentation, but for now it’s enough to know that for our case we want to keep them false.

Then, we use process.env.PORT to set the port to the environment port variable if it exists. Otherwise, we’ll default to 3000, which is the port we’ll be using locally. This gives you enough flexibility to switch from development, directly to a production environment where the port might be set by a service provider like, for instance, Heroku. Right below that, we called app.listen() with the port variable we set up and a simple log to let us know that it’s all working fine and on which port is the app listening.

That’s all for the Express setup. Now it’s on to setting up Passport.

Setting up Passport

First, we install Passport with the following command:

npm install passport

Then we need to add the following lines to the bottom of the index.js file:

/*  PASSPORT SETUP  */

const passport = require('passport');

app.use(passport.initialize());
app.use(passport.session());

Here, we require passport and initialize it along with its session authentication middleware, directly inside our Express app.

Creating a MongoDB Data Store

Since we’re assuming you’ve already installed Mongo, you should be able to start the Mongo shell using the following command:

mongo

Within the shell, issue the following command:

use MyDatabase;

This simply creates a datastore named MyDatabase.

Leave the terminal there; we’ll come back to it later.

Connecting Mongo to Node with Mongoose

Now that we have a database with records in it, we need a way to communicate with it from our application. We’ll be using Mongoose to achieve this. Why don’t we just use plain Mongo? Well, as the Mongoose devs like to say on their website:

writing MongoDB validation, casting and business logic boilerplate is a drag.

Mongoose will simply make our lives easier and our code more elegant.

Let’s go ahead and install it with the following command:

npm install mongoose

We’ll also be using passport-local-mongoose, which will simplify the integration between Mongoose and Passport for local authentication. It will add a hash and salt field to our Schema in order to store the hashed password and the salt value. This is great, as passwords should never be stored as plain text in a database.

Let’s install the package:

npm install passport-local-mongoose

Now we have to configure Mongoose. Hopefully you know the drill by now: add the following code to the bottom of your index.js file:

/* MONGOOSE SETUP */

const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');

mongoose.connect('mongodb://localhost/MyDatabase',
  { useNewUrlParser: true, useUnifiedTopology: true });

const Schema = mongoose.Schema;
const UserDetail = new Schema({
  username: String,
  password: String
});

UserDetail.plugin(passportLocalMongoose);
const UserDetails = mongoose.model('userInfo', UserDetail, 'userInfo');

Here we require the previously installed packages. Then we connect to our database using mongoose.connect and give it the path to our database. Next, we’re making use of a Schema to define our data structure. In this case, we’re creating a UserDetail schema with username and password fields.

Finally, we add passportLocalMongoose as a plugin to our Schema. This will work part of the magic we talked about earlier. Then, we create a model from that schema. The first parameter is the name of the collection in the database. The second one is the reference to our Schema, and the third one is the name we’re assigning to the collection inside Mongoose.

That’s all for the Mongoose setup. We can now move on to implementing our Passport strategy.

Implementing Local Authentication

And finally, this is what we came here to do! Let’s set up the local authentication. As you’ll see below, we’ll just write the code that will set it up for us:

/* PASSPORT LOCAL AUTHENTICATION */

passport.use(UserDetails.createStrategy());

passport.serializeUser(UserDetails.serializeUser());
passport.deserializeUser(UserDetails.deserializeUser());

There’s quite some magic going on here. First, we make passport use the local strategy by calling createStrategy() on our UserDetails model — courtesy of passport-local-mongoose — which takes care of everything so that we don’t have to set up the strategy. Pretty handy.

Then we’re using serializeUser and deserializeUser callbacks. The first one will be invoked on authentication, and its job is to serialize the user instance with the information we pass on to it and store it in the session via a cookie. The second one will be invoked every subsequent request to deserialize the instance, providing it the unique cookie identifier as a “credential”. You can read more about that in the Passport documentation.

Routes

Now let’s add some routes to tie everything together. First, we’ll add a final package. Go to the terminal and run the following command:

npm install connect-ensure-login

The connect-ensure-login package is middleware that ensures a user is logged in. If a request is received that is unauthenticated, the request will be redirected to a login page. We’ll use this to guard our routes.

Now, add the following to the bottom of index.js:

/* ROUTES */

const connectEnsureLogin = require('connect-ensure-login');

app.post('/login', (req, res, next) => {
  passport.authenticate('local',
  (err, user, info) => {
    if (err) {
      return next(err);
    }

    if (!user) {
      return res.redirect('/login?info=' + info);
    }

    req.logIn(user, function(err) {
      if (err) {
        return next(err);
      }

      return res.redirect('/');
    });

  })(req, res, next);
});

app.get('/login',
  (req, res) => res.sendFile('html/login.html',
  { root: __dirname })
);

app.get('/',
  connectEnsureLogin.ensureLoggedIn(),
  (req, res) => res.sendFile('html/index.html', {root: __dirname})
);

app.get('/private',
  connectEnsureLogin.ensureLoggedIn(),
  (req, res) => res.sendFile('html/private.html', {root: __dirname})
);

app.get('/user',
  connectEnsureLogin.ensureLoggedIn(),
  (req, res) => res.send({user: req.user})
);

At the top, we’re requiring connect-ensure-login. We’ll come back to this later.

Next, we set up a route to handle a POST request to the /login path. Inside the handler, we use the passport.authenticate method, which attempts to authenticate with the strategy it receives as its first parameter — in this case local. If authentication fails, it will redirect us to /login, but it will add a query parameter — info — that will contain an error message. Otherwise, if authentication is successful, it will redirect us to the '/' route.

Then we set up the /login route, which will send the login page. For this, we’re using res.sendFile() and passing in the file path and our root directory, which is the one we’re working on — hence the __dirname.

The /login route will be accessible to anyone, but our next ones won’t. In the / and /private routes we’ll send their respective HTML pages, and you’ll notice something different here. Before the callback, we’re adding the connectEnsureLogin.ensureLoggedIn() call. This is our route guard. Its job is validating the session to make sure you’re allowed to look at that route. Do you see now what I meant earlier by “letting the server do the heavy lifting”? We’re authenticating the user every single time.

Finally, we’ll need a /user route, which will return an object with our user information. This is just to show you how you can go about getting information from the server. We’ll request this route from the client and display the result.

Talking about the client, let’s do that now.

The post Local Authentication Using Passport in Node.js appeared first on SitePoint.

How to Use SSL/TLS with Node.js

$
0
0
How to Use SSL/TLS with Node.js

In 2020, there’s no reason for your website not to use HTTPS. Visitors expect it, Google uses it as a ranking factor and browser makers will happily name and shame those sites not using it.

In this tutorial, I’ll walk you through a practical example of how to add a Let’s Encrypt–generated certificate to your Express.js server.

But protecting our sites and apps with HTTPS isn’t enough. We should also demand encrypted connections from the servers we’re talking to. We’ll see that possibilities exist to activate the SSL/TLS layer even when it’s not enabled by default.

Note: if you’re looking for instructions on how to set up SSL with NGINX when configuring it to work as a reverse proxy for a Node app, check out our quick tip, “Configuring NGINX and SSL with Node.js”.

Let’s start with a short review of the current state of HTTPS.

HTTPS Everywhere

The HTTP/2 specification was published as RFC 7540 in May 2015, which means at this point it’s a part of the standard. This was a major milestone. Now we can all upgrade our servers to use HTTP/2. One of the most important aspects is the backwards compatibility with HTTP 1.1 and the negotiation mechanism to choose a different protocol. Although the standard doesn’t specify mandatory encryption, currently no browser supports HTTP/2 unencrypted. This gives HTTPS another boost. Finally we’ll get HTTPS everywhere!

What does our stack actually look like? From the perspective of a website running in the browser (at the application level) we have to traverse the following layers to reach the IP level:

  1. Client browser
  2. HTTP
  3. SSL/TLS
  4. TCP
  5. IP

HTTPS is nothing more than the HTTP protocol on top of SSL/TLS. Hence all of HTTP’s rules still apply. What does this additional layer actually give us? There are multiple advantages: we get authentication by having keys and certificates; a certain kind of privacy and confidentiality is guaranteed, as the connection is encrypted in an asymmetric manner; and data integrity is also preserved, as transmitted data can’t be changed during transit.

One of the most common myths is that using SSL/TLS is computationally expensive and slows the server down. This is certainly not true anymore. We also don’t need any specialized hardware with cryptography units. Even for Google, the SSL/TLS layer accounts for less than 1% of the CPU load and the the network overhead of HTTPS as compared to HTTP is below 2%. All in all, it wouldn’t make sense to forgo HTTPS for the sake of a little overhead.

As Ilya Grigorik puts it, there is but one performance problem:

The most recent version is TLS 1.3. TLS is the successor of SSL, which is available in its latest release SSL 3.0. The changes from SSL to TLS preclude interoperability, but the basic procedure is, however, unchanged. We have three different encrypted channels. The first is a public key infrastructure for certificate chains. The second provides public key cryptography for key exchanges. Finally, the third one is symmetric. Here we have cryptography for data transfers.

TLS 1.3 uses hashing for some important operations. Theoretically, it’s possible to use any hashing algorithm, but it’s highly recommended to use SHA2 or a stronger algorithm. SHA1 has been a standard for a long time but has recently become obsolete.

HTTPS is also gaining more attention for clients. Privacy and security concerns have always been around, but with the growing amount of online accessible data and services, people are getting more and more concerned. For those sites that don’t implement it, there is a useful browser extension — HTTPS Everywhere from the EFF — which encrypts our communications with most websites.

HTTPS-everywhere

The creators realized that many websites offer HTTPS only partially. The plugin allows us to rewrite requests for those sites that offer only partial HTTPS support. Alternatively, we can also block HTTP altogether (see the screenshot above).

Basic Communication

The certificate’s validation process involves validating the certificate signature and expiration. We also need to verify that it chains to a trusted root. Finally, we need to check to see if it’s been revoked. There are dedicated, trusted authorities in the world that grant certificates. In case one of these were to become compromised, all other certificates from the said authority would get revoked.

The sequence diagram for an HTTPS handshake looks as follows. We start with the initialization from the client, which is followed by a message with the certificate and key exchange. After the server sends its completed package, the client can start the key exchange and cipher specification transmission. At this point, the client is finished. Finally the server confirms the cipher specification selection and closes the handshake.

HTTPS-sequence

The whole sequence is triggered independently of HTTP. If we decide to use HTTPS, only the socket handling is changed. The client is still issuing HTTP requests, but the socket will perform the previously described handshake and encrypt the content (header and body).

So what do we need to make SSL/TLS work with an Express.js server?

HTTPS

By default, Node.js serves content over HTTP. But there’s also an HTTPS module that we have to use in order to communicate over a secure channel with the client. This is a built-in module, and the usage is very similar to how we use the HTTP module:

const https = require("https"),
  fs = require("fs");

const options = {
  key: fs.readFileSync("/srv/www/keys/my-site-key.pem"),
  cert: fs.readFileSync("/srv/www/keys/chain.pem")
};

const app = express();

app.use((req, res) => {
  res.writeHead(200);
  res.end("hello world\n");
});

app.listen(8000);

https.createServer(options, app).listen(8080);

Ignore the /srv/www/keys/my-site-key.pem and and /srv/www/keys/chain.pem files for the moment. Those are the SSL certificates we need to generate, which we’ll do a bit later. This is the part that changed with Let’s Encrypt. Previously, we had to generate a private/public key pair, send it to a trusted authority, pay them and probably wait for a bit in order to get an SSL certificate. Nowadays, Let’s Encrypt instantly generates and validates your certificates for free!

Generating Certificates

Certbot

The TLS specification demands a certificate, which is signed by a trusted certificate authority (CA). The CA ensures that the certificate holder is really who they claim to be. So basically when you see the green lock icon (or any other greenish sign to the left side of the URL in your browser) it means that the server you’re communicating with is really who it claims to be. If you’re on facebook.com and you see a green lock, it’s almost certain you really are communicating with Facebook and no one else can see your communication — or rather, no one else can read it.

It’s worth noting that this certificate doesn’t necessarily have to be verified by an authority such as Let’s Encrypt. There are other paid services as well. You can technically sign it yourself, but then (as you’re not a trusted CA) the users visiting your site will likely see a large scary warning offering to get them back to safety.

In the following example, we’ll use the Certbot, which is used to generate and manage certificates with Let’s Encrypt.

On the Certbot site you can find instructions on how to install Certbot for almost any OS/server combination. You should choose the options that are applicable to you.

A common combination for deploying Node apps is NGINX on the latest LTS Ubuntu and that’s what I’ll use here.

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update

Webroot

Webroot is a Certbot plugin that, in addition to the Certbot default functionality (which automatically generates your public/private key pair and generates an SSL certificate for those), also copies the certificates to your webroot folder and verifies your server by placing some verification code into a hidden temporary directory named .well-known. In order to skip doing some of these steps manually, we’ll use this plugin. The plugin is installed by default with Certbot. In order to generate and verify our certificates, we’ll run the following:

certbot certonly --webroot -w /var/www/example/ -d www.example.com -d example.com

You may have to run this command as sudo, as it will try to write to /var/log/letsencrypt.

You’ll also be asked for your email address. It’s a good idea to put in a real address you use often, as you’ll get a notification if your certificate is about to expire. The trade-off for Let’s Encrypt issuing a free certificate is that it expires every three months. Luckily, renewal is as easy as running one simple command, which we can assign to a cron job and then not have to worry about expiration. Additionally, it’s a good security practice to renew SSL certificates, as it gives attackers less time to break the encryption. Sometimes developers even set up this cron to run daily, which is completely fine and even recommended.

Keep in mind that you have to run this command on a server to which the domain specified under the -d (for domain) flag resolves — that is, your production server. Even if you have the DNS resolution in your local hosts file, this won’t work, as the domain will be verified from outside. So if you’re doing this locally, it will most likely fail, unless you opened up a port from your local machine to the outside world and have it running behind a domain name which resolves to your machine. This is a highly unlikely scenario.

Last but not least, after running this command, the output will contain paths to your private key and certificate files. Copy these values into the previous code snippet — into the cert property for certificate, and the key property for the key:

// ...

const options = {
  key: fs.readFileSync("/var/www/example/sslcert/privkey.pem"),
  cert: fs.readFileSync("/var/www/example/sslcert/fullchain.pem") // these paths might differ for you, make sure to copy from the certbot output
};

// ...

The post How to Use SSL/TLS with Node.js appeared first on SitePoint.

Viewing all 225 articles
Browse latest View live