The Token authentication mechanism is not explained here. If not, check out my article: Common Authentication mechanisms in Web development

The library

  • Bcrypt – Used to encrypt passwords
  • Koa-jwt-jwt middleware
  • Jsonwebtoken – Used to generate tokens and send them to browsers. Jsonwebtoken methods are not provided in later versions of KOA2, so you need to install them separately.

Implementation approach

The process and ideas of the whole program are very clear, which can be roughly divided into the following steps:

  • Custom 401 interception middleware for interception of non-existent or invalid tokens
  • Configuration of koa – JWT
  • Registered implementation
  • Login to realize

Run the project

The project requires that you have mongodb installed and started. See config/index.js for the mongodb configuration.

npm run startCopy the code

The project provides three apis

  • /api/register
  • /api/login
  • /api/users

The/API /register and/API /login apis are public apis that can be accessed without a token. /users is a private API that requires the correct token to access.

Custom 401 handler

The KOA-JWT middleware will give an error message if there is no token or the token is invalid. If there is no custom middleware, the error message exposed by KOA-JWT is returned directly to the user.

// server/middlewares/errorHandle.js
export default errorHandle = (ctx, next) = > {
  return next().catch((err) = > {
    if (err.status === 401) {
      ctx.status = 401;
      ctx.body = {
        error: err.originalError ? err.originalError.message : err.message,
      };
    } else {
      throwerr; }}); }Copy the code

This middleware is then used in index.js

app
  .use(errorHandle)Copy the code

Use koa – JWT

Add koA-JWT middleware to index.js.

const secert = 'jwt_secret'
  app
  .use(jwt({
    secret,
  }).unless({
    path: [/\/register/, /\/login/],
  }))Copy the code

Secret is the key used for encryption, not just a string, but also a file.

// https://github.com/koajs/jwt#token-verification-exceptions
var publicKey = fs.readFileSync('/path/to/public.pub');
app.use(jwt({ secret: publicKey }));Copy the code

Unless () is used to set which apis do not require token authentication. That’s what we call a public API, an API that you can access without logging in. In this example, the/Register and /login apis are set up without token checking.

After using KOA-JWT, all routes (except those set by Unless ()) check the token in the Header for existence and validity. Only after the correct access.

Registered implementation

Registration is very simple, here it is simple to encrypt the password and store the information in the database. In real projects, you also need to validate fields entered by users.

  /** * you can register with * curl -X POST http://localhost:3200/api/register -H 'cache-control: no-cache' -H 'content-type: application/x-www-form-urlencoded' -d 'username=superman2&password=123456' */
  async register(ctx) {
    const { body } = ctx.request;
    try {
      if(! body.username || ! body.password) { ctx.status =400;
        ctx.body = {
          error: `expected an object with username, password but got: ${body}`,}return;
      }
      body.password = await bcrypt.hash(body.password, 5)
      let user = await User.find({ username: body.username });
      if(! user.length) {const newUser = new User(body);
        user = await newUser.save();
        ctx.status = 200;
        ctx.body = {
          message: 'Registration successful',
          user,
        }
      } else {
        ctx.status = 406;
        ctx.body = {
          message: 'Username already exists',}}}catch (error) {
      ctx.throw(500)}}Copy the code

Login to realize

The user enters a user name and password to log in, and if the user name and password are correct, the token is generated using jsonWebtoken.sign () and returned to the client. Clients store tokens in local storage and add tokens to HTTP Header Authorazition: Bearer tokens in each HTTP request. The backend then validates the token each time. The resource can be accessed only after the token is correct.

  /** you can login with * curl -X POST http://localhost:3200/api/login/ -H 'cache-control: no-cache' -H 'content-type: application/x-www-form-urlencoded' -d 'username=superman2&password=123456' */
  async login(ctx) {
    const { body } = ctx.request
    try {
      const user = await User.findOne({ username: body.username });
      if(! user) { ctx.status =401
        ctx.body = {
          message: 'Wrong username',}return;
      }
      // Match whether the passwords are equal
      if (await bcrypt.compare(body.password, user.password)) {
        ctx.status = 200
        ctx.body = {
          message: 'Login successful'.user: user.userInfo,
          // Generate a token and return it to the client
          token: jsonwebtoken.sign({
            data: user,
            // Set the token expiration time
            exp: Math.floor(Date.now() / 1000) + (60 * 60), // 60 seconds * 60 minutes = 1 hour
          }, secret),
        }
      } else {
        ctx.status = 401
        ctx.body = {
          message: 'Password error',}}}catch (error) {
      ctx.throw(500)}}Copy the code

Note that when using jsonWebtoken.sign (), you need to pass in the secret parameter, which must be the same as secret in JWT ().

For more information about jsonWebToken methods, see: github.com/auth0/node-…

After logging in, with the token returned, go to/API /users and get the correct list of users.

curl -X GET http://localhost:3200/api/users -H 'authorization: Bearer token' -H 'cache-control: no-cache'Copy the code