Skip to content

Latest commit

 

History

History
262 lines (220 loc) · 9.83 KB

user-management.md

File metadata and controls

262 lines (220 loc) · 9.83 KB

User Management

In the previous section we set up a Message service with a /messages endpoint. The app we generated also comes with a /users endpoint and local authentication. Now we're going to learn how we can create users and authenticate them.

Creating and authenticating users

Although a new user can be created by POSTing to the /users endpoint, we're going to create a separate /signup endpoint. This will keep things explicit and also show how you can use regular Express middleware with Feathers.

In this example the fields required for creating a user are email and password. Feathers automatically hashes passwords using bcrypt.

Feathers Authentication uses JSON webtoken (JWT) for secure authentication between a client and server as opposed to cookies and sessions. After we create a user, we'll be able to login. All we'll need to do is POST the email and password to the http://localhost:3030/auth/local endpoint set up by Feathers authentication. The response will return the authenticated user and their token.

This token needs to be set in the Authorization header for any subsequent requests that require authentication. You can find more details in the authentication chapter.

When we create a front-end for our chat API this will all be done automatically for us when using the Feathers client by calling app.authenticate().

Adding HTML pages

For our chat app we will create a static signup.html and login.html page that shows a form. The form in signup.html will POST to the /signup endpoint which we will create later and login.html will submit to auth/local which already exists.

Let's replace public/index.html with the following welcome page:

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0">
    <title>Feathers Chat</title>
    <link rel="shortcut icon" href="favicon.ico">
    <link rel="stylesheet" href="//cdn.rawgit.com/feathersjs/feathers-chat/v0.1.0/public/base.css">
    <link rel="stylesheet" href="//cdn.rawgit.com/feathersjs/feathers-chat/v0.1.0/public/chat.css">
  </head>
  <body>
    <main class="home container">
      <div class="row">
        <div class="col-12 col-8-tablet push-2-tablet text-center">
          <img class="logo center-item"
            src="http://feathersjs.com/img/feathers-logo-wide.png"
            alt="Feathers Logo">
          <h3 class="title">Chat</h3>
        </div>
      </div>
      <div class="row">
        <div class="col-12 push-4-tablet col-4-tablet">
          <div class="row">
            <div class="col-12">
              <a href="login.html" class="button button-primary block login">
                Login
              </a>
            </div>
          </div>
          <div class="row">
            <div class="col-12">
              <a href="signup.html" class="button button-primary block signup">
                Signup
              </a>
            </div>
          </div>
        </div>
      </div>
    </main>
  </body>
</html>

public/login.html looks like this:

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0">
    <title>Feathers Chat</title>
    <link rel="shortcut icon" href="favicon.ico">
    <link rel="stylesheet" href="//cdn.rawgit.com/feathersjs/feathers-chat/v0.1.0/public/base.css">
    <link rel="stylesheet" href="//cdn.rawgit.com/feathersjs/feathers-chat/v0.1.0/public/chat.css">
  </head>
  <body>
    <main class="login container">
      <div class="row">
        <div class="col-12 col-6-tablet push-3-tablet text-center">
          <h1 class="font-100">Welcome Back</h1>
        </div>
      </div>
      <div class="row">
        <div class="col-12 col-6-tablet push-3-tablet col-4-desktop push-4-desktop text-center">
          <form class="form" method="post" action="/auth/local">
            <fieldset>
              <input class="block" type="email" name="email" placeholder="email">
            </fieldset>
            <fieldset>
              <input class="block" type="password" name="password" placeholder="password">
            </fieldset>
            <button type="submit" class="button button-primary block login">
              Login
            </button>
          </form>
        </div>
      </div>
    </main>
  </body>
</html>

Finally, public/signup.html looks like this:

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0">
    <title>Feathers Chat</title>

    <link rel="shortcut icon" href="img/favicon.png">
    <link rel="stylesheet" href="//cdn.rawgit.com/feathersjs/feathers-chat/v0.1.0/public/base.css">
    <link rel="stylesheet" href="//cdn.rawgit.com/feathersjs/feathers-chat/v0.1.0/public/chat.css">
  </head>
  <body>
    <main class="login container">
      <div class="row">
        <div class="col-12 col-6-tablet push-3-tablet text-center">
          <h1 class="font-100">Create an Account</h1>
        </div>
      </div>
      <div class="row">
        <div class="col-12 col-6-tablet push-3-tablet col-4-desktop push-4-desktop text-center">
          <form class="form" method="post" action="/signup">
            <fieldset>
              <input class="block" type="email" name="email" placeholder="email">
            </fieldset>
            <fieldset>
              <input class="block" type="password" name="password" placeholder="password">
            </fieldset>
            <button type="submit" class="button button-primary block signup">
              Signup
            </button>
          </form>
        </div>
      </div>
    </main>
  </body>
</html>

Signing up new users

Now we have a welcome, login and signup page and we can create a signup endpoint that creates a new user from the signup.html form submission and then redirects to login.html. Because Feathers works just like Express we can just create an Express middleware called signup that does that.

$ feathers generate middleware

Generating the signup middleware

src/middleware/signup.js takes the users service and creates a new user from the submitted form data. Then redirects to login.html:

'use strict';

module.exports = function(app) {
  return function(req, res, next) {
    const body = req.body;

    // Get the user service and `create` a new user
    app.service('users').create({
      email: body.email,
      password: body.password
    })
    // Then redirect to the login page
    .then(user => res.redirect('/login.html'))
    // On errors, just call our error middleware
    .catch(next);
  };
};

Now we have to add the /signup POST route to src/middleware/index.js:

'use strict';

const signup = require('./signup');

const handler = require('feathers-errors/handler');
const notFound = require('./not-found-handler');
const logger = require('./logger');

module.exports = function() {
  // Add your custom middleware here. Remember, that
  // just like Express the order matters, so error
  // handling middleware should go last.
  const app = this;

  app.post('/signup', signup(app));
  app.use(notFound());
  app.use(logger(app));
  app.use(handler());
};

ProTip: Just like Express, most middleware should be registered before the notFound middleware.

The last step is to change the standard success redirect to /chat.html. This page will contain the actual frontend for our chat application. Because there are so many different frameworks we won't create that page yet and instead implement it in the framework guides for your favourite framework after finishing this tutorial. We set up the redirect by adding successRedirect to the auth section in config/default.json:

{
  "host": "localhost",
  "port": 3030,
  "nedb": "../data/",
  "public": "../public/",
  "auth": {
    "token": {
      "secret": "<your secret>"
    },
    "local": {},
    "successRedirect": "/chat.html"
  }
}

After stopping (CTRL + C) and starting the server (npm start) again we can go to the signup page at http://localhost:3030/signup.html, sign up with an email and password which will redirect us to the login page. There we can log in with the information we used to sign up and will get redirected to chat.html which currently shows authentication success page with the JWT.

Authorization

Now that we can authenticate we want to restrict the Message service to only authenticated users. We could have done that already in the service generator by answering "yes" when asked if we need authentication but we can also easily add it manually by changing src/services/message/hooks/index.js to:

'use strict';

const globalHooks = require('../../../hooks');
const auth = require('feathers-authentication').hooks;

exports.before = {
  all: [
    auth.verifyToken(),
    auth.populateUser(),
    auth.restrictToAuthenticated()
  ],
  find: [],
  get: [],
  create: [],
  update: [],
  patch: [],
  remove: []
};

exports.after = {
  all: [],
  find: [],
  get: [],
  create: [],
  update: [],
  patch: [],
  remove: []
};

That's it for authentication! We now have a home, login and signup page. We can sign up as a new user and log in with their email and password. In the next section we will look at creating new messages and adding additional information to them using the authenticated user information.