

focus-budget-manager ) and, after creating package.json with the npm init command, run the following command: npm i --save express body-parser mongoose consign cors bcrypt jsonwebtoken morgan passport passport-jwt module-alias req.body property..gitignore file in the project root folder. Write the following into it: /node_modules/ BudgetManagerAPI/config file in the BudgetManagerAPI/config index.js and enter the following code into it: module.exports = { secret: 'budgetsecret', session: { session: false }, database: 'mongodb://127.0.0.1:27017/budgetmanager' } 127.0.0.1:27017 you can use localhost . If you want, you can work with the MongoDB cloud database created, for example, by means of MLabs.User model that will be used for JWT authentication. To do this, go to the BudgetManagerAPI/app folder and create the models directory in it, and the user.js file in it. At the beginning of the file connect dependencies: const mongoose = require('mongoose'), bcrypt = require('bcrypt'); mongoose package is needed here to create a User model, the bcrypt package tools will be used to hash user passwords. const Schema = mongoose.Schema({ username: { type: String, unique: true, required: true }, password: { type: String, required: true }, clients: [{}] }); User data schema. Thanks to this description, the following data will be assigned to the user of our system:password ).clients ).user.js , add the following code to it: // - Schema.pre('save', function (next) { const user = this; if (this.isModified('password') || this.isNew) { bcrypt.genSalt(10, (error, salt) => { if (error) return next(error); bcrypt.hash(user.password, salt, (error, hash) => { if (error) return next(error); user.password = hash; next(); }); }); } else { return next(); } }); Schema.methods.comparePassword = function (password, callback) { bcrypt.compare(password, this.password, (error, matches) => { if (error) return callback(error); callback(null, matches); }); }; User model: mongoose.model('User', Schema); User model is ready, create a file passport.js in the BudgetManagerAPI/config folder. Let's start working on this file with connecting dependencies: const PassportJWT = require('passport-jwt'), ExtractJWT = PassportJWT.ExtractJwt, Strategy = PassportJWT.Strategy, config = require('./index.js'), models = require('@BudgetManager/app/setup'); mongoose package is needed to work with the User model, and passport-jwt is needed for authentication. module.exports = (passport) => { const User = models.User; const parameters = { secretOrKey: config.secret, jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken() }; passport.use(new Strategy(parameters, (payload, done) => { User.findOne({ id: payload.id }, (error, user) => { if (error) return done(error, false); if (user) done(null, user); else done(null, false); }); })); } User model and find the user by performing a search on the JWT token received from the client.BudgetManagerAPI/config folder, create a database.js file that is responsible for working with the database. Add the following to this file: module.exports = (mongoose, config) => { const database = mongoose.connection; mongoose.Promise = Promise; mongoose.connect(config.database, { useMongoClient: true, promiseLibrary: global.Promise }); database.on('error', error => console.log(`Connection to BudgetManager database failed: ${error}`)); database.on('connected', () => console.log('Connected to BudgetManager database')); database.on('disconnected', () => console.log('Disconnected from BudgetManager database')); process.on('SIGINT', () => { database.close(() => { console.log('BudgetManager terminated, connection closed'); process.exit(0); }) }); }; mongoose to use the standard Promise object. If you do not do this, you may encounter warnings displayed in the console. Then we created a standard mongoose connection.services folder and open the index.js file already in it. Add the following to it: require('module-alias/register'); const http = require('http'), BudgetManagerAPI = require('@BudgetManagerAPI'), BudgetManagerServer = http.Server(BudgetManagerAPI), BudgetManagerPORT = process.env.PORT || 3001, LOCAL = '0.0.0.0'; BudgetManagerServer.listen(BudgetManagerPORT, LOCAL, () => console.log(`BudgetManagerAPI running on ${BudgetManagerPORT}`)); module_alias , which we will configure later (a step is optional, but this approach will help make the code cleaner). If you decide not to use the module_alias package, then instead of @BudgetManagerAPI you will need to write ./services/BudgetManagerAPI/config .node services command in the command line interpreter you use.BudgetManagerAPI/config directory, create an app.js file. First, let's connect the dependencies: const express = require('express'), app = express(), bodyParser = require('body-parser'), mongoose = require('mongoose'), morgan = require('morgan'), consign = require('consign'), cors = require('cors'), passport = require('passport'), passportConfig = require('./passport')(passport), jwt = require('jsonwebtoken'), config = require('./index.js'), database = require('./database')(mongoose, config); passportConfig = require('./passport')(passport) we import the configuration file for passport , passing passport as an argument, since the following command is available in passport.js :
passport inside the passport.js file without having to connect it.app.js file, we start working with packages and set the secret key: app.use(express.static('.')); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.use(morgan('dev')); app.use(cors()); app.use(passport.initialize()); app.set('budgetsecret', config.secret); cors package, you can do the following: app.use(function(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); next(); }); package.json and add to it, just before the dependencies block, the following: "homepage": "https://github.com/gdomaradzki/focus-gestor-orcamentos#readme", "_moduleAliases": { "@root": ".", "@BudgetManager": "./services/BudgetManagerAPI", "@BudgetManagerModels":"./services/BudgetManagerAPI/app/models", "@BudgetManagerAPI":"./services/BudgetManagerAPI/config/app.js", "@config": "./services/BudgetManagerAPI/config/index.js" }, "dependencies": { dependencies block already exists in the file, so you only need to add homepage and _moduleAliases .@root alias, the index.js configuration file using the @config alias, and so on.BudgetManagerAPI/app folder and create a new setup folder, and in it - the index.js file. Add the following to it: const mongoose = require('mongoose'), UserModel = require('@BudgetManagerModels/user');; const models = { User: mongoose.model('User') } module.exports = models; BudgetManagerAPI/app to the BudgetManagerAPI/app folder, create the api directory in it, and in it the auth.js file. We write the following in it: const mongoose = require('mongoose'), jwt = require('jsonwebtoken'), config = require('@config'); module_alias module module_alias we have made the code cleaner. Otherwise I would have to write something like this: config = require('./../../config); const api = {}; api.login = (User) => (req, res) => { User.findOne({ username: req.body.username }, (error, user) => { if (error) throw error; if (!user) res.status(401).send({ success: false, message: 'Authentication failed. User not found.' }); else { user.comparePassword(req.body.password, (error, matches) => { if (matches && !error) { const token = jwt.sign({ user }, config.secret); res.json({ success: true, message: 'Token granted', token }); } else { res.status(401).send({ success: false, message: 'Authentication failed. Wrong password.' }); } }); } }); } api object, in which we save all the necessary methods. In the login method, we first pass the User argument, since we need a method to access the User model, then we pass the arguments req and res .User object that matches the username . If the username is not recognized, we give an error, otherwise we check the password and token associated with the user.api method that will receive and parse the token: api.verify = (headers) => { if (headers && headers.authorization) { const split = headers.authorization.split(' '); if (split.length === 2) return split[1]; else return null; } else return null; } Authorization header. After all these steps, we can finally export the api object: module.exports = api; services/BudgetManagerAPI/app folder and create the routes directory in it, in which we will create the auth.js file with the following contents: const models = require('@BudgetManager/app/setup'); module.exports = (app) => { const api = app.BudgetManagerAPI.app.api.auth; app.route('/') .get((req, res) => res.send('Budget Manager API')); app.route('/api/v1/auth') .post(api.login(models.User)); } app object, so you can set routes. Here we set the constant api , which we use to work with the auth.js file in the api folder. Here we set the default route, '/' , when accessing which the string “Budget Manager API” is passed to the user. Right there we create the route '/api/v1/auth' (for which the POST request is used). To serve this route, we use the login method, passing the User model as an argument.app.js back to the app.js file, which is located in the BudgetManagerAPI/config folder and add the following (the app.set('budgetsecret', config.secret) line app.set('budgetsecret', config.secret) given as a guideline, you don’t need to add it to the file a second time): app.set('budgetsecret', config.secret); consign({ cwd: 'services' }) .include('BudgetManagerAPI/app/setup') .then('BudgetManagerAPI/app/api') .then('BudgetManagerAPI/app/routes') .into(app); module.exports = app; setup folder are downloaded, so that an instance of the model will be created first. Then we load the API methods, and finally the routes.BudgetManagerAPI/app/api back to the BudgetManagerAPI/app/api folder and create the user.js file in it. Put the following code in it: const mongoose = require('mongoose'); const api = {}; api.setup = (User) => (req, res) => { const admin = new User({ username: 'admin', password: 'admin', clients: [] }); admin.save(error => { if (error) throw error; console.log('Admin account was succesfully set up'); res.json({ success: true }); }) } setup method allows you to create an administrator account for debugging purposes. In the finished application of this account should not be. api.index = (User, BudgetToken) => (req, res) => { const token = BudgetToken; if (token) { User.find({}, (error, users) => { if (error) throw error; res.status(200).json(users); }); } else return res.status(403).send({ success: false, message: 'Unauthorized' }); } signup method that will be needed later. It is designed to register new users: api.signup = (User) => (req, res) => { if (!req.body.username || !req.body.password) res.json({ success: false, message: 'Please, pass a username and password.' }); else { const newUser = new User({ username: req.body.username, password: req.body.password, clients: [] }); newUser.save((error) => { if (error) return res.status(400).json({ success: false, message: 'Username already exists.' }); res.json({ success: true, message: 'Account created successfully' }); }) } } module.exports = api; username and password fields are filled in, and if so, then, provided that a valid username is entered, a new user is created.user.js file in the BudgetManagerAPI/app/routes folder and write the following code into it: const passport = require('passport'), config = require('@config'), models = require('@BudgetManager/app/setup'); module.exports = (app) => { const api = app.BudgetManagerAPI.app.api.user; app.route('/api/v1/setup') .post(api.setup(models.User)) app.route('/api/v1/users') .get(passport.authenticate('jwt', config.session), api.index(models.User, app.get('budgetsecret'))); app.route('/api/v1/signup') .post(api.signup(models.User)); } passport library to organize the authentication, connect the configuration file to configure the session parameters, and connect the models so that you can check whether the user has the right to work with the API endpoints.
route , which can be accessed at http: // localhost: 3001 / api / v1 / auth .
user routes by going to http: // localhost: 3001 / api / v1 / users . The server will report a GET method with a status of 401. This indicates that the request was not processed because we do not have enough privileges to work with the target resource. The client will display a page with the text “Unauthorized”.setup endpoint to create an administrator account. In the Postman interface, it will look like this:
http://localhost:3001/api/v1/setup , change the type of request to POST and click the Send button. The server's JSON response must contain the message "success": true .
http://localhost:3001/api/v1/auth , on the Body tab, specify the username and password keys with the same admin value and click the Send button.

token key, use the GET request, enter http://localhost:3001/api/v1/users in the address field, then add a new Authorization header on the Headers tab with a value of the form Bearer token (instead of token insert the token copied from the previously received server response). In the same place, add a Content-Type header with the value application/x-www-form-urlencoded and click Send .
signup .
http://localhost:3001/api/v1/signup , on the Body tab, select the x-www-form-urlencoded switch, enter the username and password keys with values ​​different from admin , and click Send . If everything works as it should, the following should come back:
Postman tab, which accessed http://localhost:3001/api/v1/users to get the list of users, and click Send , an array of two objects representing the administrator and the new user should come back.
Source: https://habr.com/ru/post/340750/
All Articles