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