Сегодня мы создадим безопасную серверную часть аутентификации, используя передовые методы обеспечения безопасности TRIDE и TRUE, такие как веб-токены JSON (JWT), обновление JWT, хеширование паролей, проверку ввода, очистку ввода, электронную почту SMTP и проверку электронной почты.

Начать проект

Установите Node.JS

Убедитесь, что у вас установлен Node.JS.



Node.js
Node.js® – это среда выполнения JavaScript, созданная на базе JavaScript-движка Chrome V8.nodejs.org



Инициируйте свой проект

Создайте каталог проекта, я назову его secure-express-auth.

Затем инициируйте создание файла package.json с помощью:

npm init

Выполните настройку package.json.

Это создаст package.json, который содержит конфигурацию и сведения о вашем проекте.

Установить Экспресс

Убедитесь, что у вас установлен Экспресс.



Установить экспресс:

npm install --save express

Установите пакет ведения журнала в зависимости от разработчиков: morgan

Теперь создайте файл с именем app.js в каталоге верхнего уровня и вставьте следующий код:

const express = require('express');
const logger = require('morgan');

// Create an express app
const app = express();

// Setup middleware
// Use a logger for dev purposes
app.use(logger("dev"));

// Set the port
const port = 3000;
app.listen(port, () => {
    console.log(`Server is listening on port ${port}`);
})

Теперь мы должны иметь возможность запускать сервер в терминале через:

node app.js

Большой! Теперь, когда у нас есть это сообщение, мы знаем, что наше приложение по крайней мере запускается. Теперь давайте попробуем открыть соединение с нашим приложением в браузере:

https://локальный:3000/

Как видите, мы получаем сообщение об ошибке, говорящее о том, что мы не можем сделать запрос GET. Это связано с тем, что в настоящее время у нас нет настроенных маршрутов. Мы решим эту проблему за несколько минут!

Настройка MongoDB

MongoDB — это программное обеспечение базы данных, ориентированное на работу с документами, без SQL.



  1. Создайте учетную запись MongoDB.
  2. Создайте организацию MongoDB.
  3. Создайте проект MongoDB.
  4. Создайте бесплатный общий кластер.
  5. Создайте имя пользователя и пароль.
  6. Установите роль пользователя на администратора Atlas.
  7. Создайте БД.

Подключение к вашей базе данных

Сначала установите mongoose, плагин, созданный для облегчения нашей жизни при работе с любой MongoDB.

npm install mongoose

Получите строку подключения из MongoDB и обязательно замените все переменные в скобках соответствующими учетными данными.

Это должно выглядеть примерно так:

mongodb+srv://<username>:<password>@<cluster-name>.<cluster-id>.mongodb.net/<database-name>?retryWrites=true&w=majority

Пример: приведенная выше строка подключения после замены переменных предоставленными учетными данными.

mongodb+srv://admin:[email protected]/test?retryWrites=true&w=majority

Теперь создайте новую папку с именем middleware, а затем создайте в ней файл с именем db.js.

const mongoose = require('mongoose');

// This function is used to connect to the given MongoDB URI
module.exports = async function connectDB(mongoUri) {
    try {
        const conn = await mongoose.connect(mongoUri, { useNewUrlParser: true, useUnifiedTopology: true });
        console.log(`MongoDB Connected: ${conn.connection.host}`)
    } catch (err) {
        console.error(err);
        process.exit(1);
    }
}

Теперь, требуя db.js и вызывая функцию connectDB в app.js:

const express = require('express');
const logger = require('morgan');

// Create an express app
const app = express();

// Setup middleware
// Use a logger for dev purposes
app.use(logger("dev"));

// Connect to the database
const connectDB = require('./middleware/db');
const mongoUri = "mongodb+srv://admin-brandon:[email protected]/test?retryWrites=true&w=majority";
connectDB(mongoUri);

// Set the port
const port = 3000;
app.listen(port, () => {
    console.log(`Server is listening on port ${port}`);
})

Мы должны увидеть сообщение об успешном подключении.

Настройка пользовательской модели/схемы

Схемы позволяют создавать структуру данных, которая представляет собой модель.

Создайте новую папку с именем models в каталоге верхнего уровня, а затем внутри models создайте файл с именем user.js и вставьте следующее :

const {Schema, model} = require('mongoose');

const userSchema = new Schema({
    name: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true,
        unique: true
    },
    password: {
        type: String,
        required: true
    },
    joined: {
        type: Date,
        default: Date.now
    },
    lastLoggedIn: {
        type: Date,
        default: Date.now
    }
});

module.exports = User = model("User", userSchema);

Маршруты и контроллеры

Здесь начинается знакомство с маршрутами и контроллерами. Маршруты – это способ настроить расположение на сервере, доступное тем или иным способом с помощью HTTP-метода (GET, POST, PUT, PATCH и т. д.). Контроллер — это часть маршрута, который обычно принимает запрос и ответ в качестве параметров и предоставляет место для размещения вашей логики для обработки запроса и возврата некоторой важной информации. исходя из маршрута.

Создайте папку routes и папку controller в каталоге верхнего уровня.

Настройка Регистрация

Создайте маршрут регистрации

В папке routes создайте файл с именем auth.js:

const express = require('express');
const router = express.Router();
const {register} = require('../controllers/auth');

router.post("/register", register);

Настройте маршрут аутентификации в приложении в файле app.js.

const express = require('express');
const logger = require('morgan');

// Create an express app
const app = express();

// Setup middleware
// Use a logger for dev purposes
app.use(logger("dev"));

// Use this middleware for configuring json requests
app.use(express.json({limit: '1kb', extended: false}));

// Setup routes
const auth = require('./routes/auth');
app.use('/v1/auth', auth);

// Connect to the database
const connectDB = require('./db');
const mongoUri = "mongodb+srv://admin-brandon:[email protected]/test?retryWrites=true&w=majority";
connectDB(mongoUri);

// Set the port
const port = 3000;
app.listen(port, () => {
    console.log(`Server is listening on port ${port}`);
})

Это добавит все маршруты, экспортированные в наш маршрутизатор авторизации, по URL-адресу:

http://локальный: 3000/v1/аутентификация

Таким образом, URL-адрес нашего регистрационного маршрутизатора будет следующим:

http://localhost:3000/v1/auth/register

И, как вы можете видеть, в настоящее время мы испытываем ошибку. Это просто устанавливает маршрут для посещения пользователем; он пока не включает в себя функциональность/логику для предоставления ответа, и именно здесь в игру вступают контроллеры.

Создайте контроллер регистрации

В папке controllers создайте файл с именем auth.js и вставьте следующий код:

const User = require("../models/User");

exports.register = async (req, res) => {
    try {
        // Destructure the request body
        const {name, email, password, confirmPassword} = req.body;

        // Check if the user already exists
        let user = await User.findOne({email: email});

        // If the user already exists, return an error
        if (user) {
            return res.status(400).json({errors: [{message: "User already exists"}]});
        }

        // Check if the passwords match
        if (password !== confirmPassword) {
            return res.status(400).json({errors: [{message: "Passwords do not match"}]});
        }

        // Create and save a new user
        user = new User({
            name: name,
            email: email,
            password: password,
            joined: Date.now(),
            lastLoggedIn: Date.now(),
        });
        await user.save();
        user.password = undefined;

        // Return a success message
        return res.status(200).json({message: "Registration successful", data: user});
    }
    catch (err) {
        console.error(err.message);
        return res.status(500).send({message: "Server error"});
    }
}

Вход в систему

Обновите routes/auth.js, чтобы включить маршрут входа.

const express = require('express');
const router = express.Router();
const {register, login} = require('../controllers/auth');

router.post("/register", register);

router.post("/login", login);

Создайте контроллер входа в систему

В нашей папке controllers обновите файл с именем auth.js, указав следующий код:

const User = require("../models/User");

exports.register = async (req, res) => {
    try {
        // Destructure the request body
        const {name, email, password, confirmPassword} = req.body;

        // Check if the user already exists
        let user = await User.findOne({email: email});

        // If the user already exists, return an error
        if (user) {
            return res.status(400).json({errors: [{message: "User already exists"}]});
        }

        // Check if the passwords match
        if (password !== confirmPassword) {
            return res.status(400).json({errors: [{message: "Passwords do not match"}]});
        }

        // Create and save a new user
        user = new User({
            name: name,
            email: email,
            password: password,
            joined: Date.now(),
            lastLoggedIn: Date.now(),
        });
        await user.save();

        // Return a success message
        return res.status(200).json({message: "Registration successful", user: user});
    }
    catch (err) {
        console.error(err.message);
        return res.status(500).send({message: "Server error"});
    }
}

exports.login = async (req, res) => {
    try {

        // Destructure the request body
        const {email, password} = req.body;

        // Check if the user exists
        let user = await User.findOne({email: email});

        // If the user does not exist, return an error
        if (!user) {
            return res.status(400).json({errors: [{message: "Invalid email/password combination"}]});
        }

        // Check if the password is correct
        if (password !== user.password) {
            return res.status(400).json({errors: [{message: "Invalid email/password combination"}]});
        }
        // Update and save the lastLoggedIn field
        user.lastLoggedIn = Date.now();
        await user.save();
        user.password = undefined;

        // Return a success message
        return res.status(200).json({message: "Login successful", user: user});
    } catch (err) {
        console.error(err.message);
        return res.status(500).send({message: "Server error"});
    }
}

Ура, теперь, когда все работает... Подождите, разве я не упоминал, что это приложение было безопасным? На текущей итерации это все, НО безопасно! Ниже мы подробно рассмотрим реализацию безопасности в вашем новом API аутентификации пользователей.

Безопасность

Как вы, возможно, уже заметили, в нашем приложении сильно не хватает безопасности, кроме наших ручных проверок. Безопасность чрезвычайно важна для любого веб-сервиса, особенно для тех, которые имеют дело с конфиденциальными данными, такими как любые пользовательские данные.

Переменные среды

Переменные среды обычно состоят из конфигурации проекта, ключей API, имен пользователей и паролей для служб и т. д. В краткой форме переменные среды обычно хранят любые конфиденциальные учетные данные и конфигурацию, необходимые для вашего сервера.

mkdir config
cd config
touch .env
MONGO_URI=mongodb+srv://<username>:<password>@<cluster-name>.<cluster-id>.mongodb.net/<database-name>?retryWrites=true&w=majority

PORT=3000
const express = require('express');
const {xss} = require('express-xss-sanitizer');
const bodyParser = require('body-parser');
const logger = require('morgan');
const dotenv = require('dotenv');

// Load environment variables
dotenv.config({path: './config/.env'});

// Create an express app
const app = express();

// Use a logger for dev purposes
app.use(logger("dev"));

// Setup middleware
app.use(express.json({limit: '1kb', extended: false));

// Setup routes
const authRouter = require('./routes/auth');
app.use("/v1/auth", authRouter);

// Connect to the database
const connectDB = require('./middleware/db');
connectDB(process.env.MONGO_URI);

// Set the port
app.listen(process.env.PORT, () => {
    console.log(`Server is listening on port ${process.env.PORT}`);
}) 

Проверка ввода

Учитывая вышеизложенное, нам необходимо проверить входные данные, которые мы получаем из тела запроса, чтобы мы могли убедиться, что получаемые нами данные соответствуют структуре созданных нами моделей. Это проверка ввода.

Сначала установите пакет joi.

npm install --save joi

Затем мы создадим папку с именем валидаторы.

Затем мы создадим файл с именем auth.js.

const joi = require('joi');

module.exports = registerValidator = (data) => {
    const schema = joi.object({
        name: joi.string().required(),
        email: joi.string().email().required(),
        password: joi.string().min(6).required(),
        confirmPassword: joi.ref('password'),
    });
    return schema.validate(data);
}

module.exports = loginValidator = (data) => {
    const schema = joi.object({
        email: joi.string().email().required(),
        password: joi.string().min(6).required(),
    });
    return schema.validate(data);
}

Вызовите эти функции в соответствующих контроллерах.

const User = require("../models/User");

// @route   POST api/auth/register
// @desc    Register a user
// @access  Public
exports.register = async (req, res) => {
    try {
        // Validate the request body
        const data = registerValidator(req.body);

        // Check if the user already exists
        let user = await User.findOne({email: data.email});

        // If the user already exists, return an error
        if (user) {
            return res.status(400).json({errors: [{message: "User already exists"}]});
        }

        // Create and save a new user
        user = new User(data);
        await user.save();
        user.password = undefined;

        // Return a success message
        return res.status(200).json({message: "Registration successful", user: user});
    }
    catch (err) {
        console.error(err.message);
        return res.status(500).send({message: "Server error"});
    }
}

// @route   POST api/auth/login
// @desc    Login a user
// @access  Public
exports.login = async (req, res) => {
    try {

        // Validate the input
        const data = loginValidator(req.body);
        if (!data) {
          return res.status(401).send({errors

        // Check if the user exists
        let user = await User.findOne({email: data.email});

        // If the user does not exist, return an error
        if (!user) {
            return res.status(400).json({message: "Invalid email/password combination"});
        }

        // Check if the password is correct
        if (password !== user.password) {
            return res.status(400).json({errors: [{message: "Invalid email/password combination"}]});
        }
        
        // Update and save the lastLoggedIn field
        user.lastLoggedIn = Date.now();
        await user.save();

        // Return a success message
        return res.status(200).json({message: "Login successful", user: user});
    } catch (err) {
        console.error(err.message);
        return res.status(500).send({message: "Server error"});
    }
}

Очистка ввода

Очистка входных данных — еще одна важная практика безопасности.

Предотвращение SQL-инъекций и межсайтовых сценариев (xss).

npm i --save express-xss-sanitizer

Затем мы либо передаем xss в качестве промежуточного программного обеспечения для определенных маршрутов, на которых он нам нужен, либо мы передаем его приложению в целом.

const express = require('express');
const logger = require('morgan');

// Create an express app
const app = express();

// Setup middleware
// Use a logger for dev purposes
app.use(logger("dev"));

// Use this middleware for configuring json requests
app.use(express.json({limit: '1kb', extended: false}));

// Setup input sanitization
app.use(xss());

// Setup routes
const auth = require('./routes/auth');
app.use('/v1/auth', auth);

// Connect to the database
const connectDB = require('./db');
const mongoUri = "mongodb+srv://admin-brandon:[email protected]/test?retryWrites=true&w=majority";
connectDB(mongoUri);

// Set the port
const port = 3000;
app.listen(port, () => {
    console.log(`Server is listening on port ${port}`);
})

Хэширование пароля

Это большой. В экспрессе самый популярный проект для хэширования пароля — через bcrypt.

npm i --save bcrypt

Теперь обновите файл controllers/auth.js.

контроллеры/auth.js

const User = require("../models/user");
const {validator} = require("../validators/auth");
const bcrypt = require("bcrypt");

// @route   POST api/auth/register
// @desc    Register a user
// @access  Public
exports.register = async (req, res) => {
    try {
        // Validate the request body 
        const valid = validator.register(req.body);
        if (!valid) {
            return res.status(400).send({message: valid.error.map(err => err.message)});
        }
        let data = req.body;

        // Check if the user already exists
        let user = await User.findOne({email: data.email});

        // If the user already exists, return an error
        if (user) {
            return res.status(400).send({message: "User already exists"});
        }

        // Check if the passwords match
        if (data.password !== data.confirmPassword) {
            return res.status(400).send({message: "Passwords do not match"});
        } else {
            delete data.confirmPassword;
            data.password = bcrypt.hashSync(data.password, 10);
        }

        // Create and save a new user
        user = new User(data);
        await user.save();

        // Return a success message
        return res.status(200).send({message: "Registration successful", user: user});
    }
    catch (err) {
        console.log(err.message);
        return res.status(500).send({message: err.toString()});
    }
}

// @route   POST api/auth/login
// @desc    Login a user
// @access  Public
exports.login = async (req, res) => {
    try {

        // Validate the request body
        const valid = validator.login(req.body);
        if (!valid) {
            return res.status(400).send({message: valid.error.map(err => err.message)});
        }
        let data = req.body;

        // Check if the user exists
        let user = await User.findOne({email: req.body.email});

        // If the user does not exist, return an error
        if (!user) {
            return res.status(400).send({message: "Invalid email/password combination"});
        }

        // Check if the password is correct
        if (await bcrypt.compare(user.password, data.password)) {
            console.log(data.password, user.password);
            return res.status(400).send({message: "Invalid email/password combination"});
        }

        // Update and save the lastLoggedIn field
        user.lastLoggedIn = Date.now();
        await user.save();

        // Return a success message
        return res.status(200).send({message: "Login successful", user: user});
    } catch (err) {
        console.error(err.message);
        return res.status(500).send({message: "Server error"});
    }
}

Веб-токены JSON (JWT)

JWT великолепны. Они проверяют, что машина, делающая запрос, ДЕЙСТВИТЕЛЬНО является проверенным пользователем. Как правило, JWT реализуются как таковые:

У этих токенов обычно есть дата истечения срока действия, при этом дата истечения срока действия обычного токена наступает до даты истечения срока действия токена обновления.

Для успешного входа в систему мы генерируем токен и токен обновления, срок действия которых соответствует вышеуказанным требованиям. Затем мы отправляем эти токены обратно пользователю, где он будет хранить их безопасно и локально. Теперь, когда пользователь отправляет запрос к одной из наших конечных точек (которая использует промежуточное ПО), у нас будет промежуточное ПО JWT для проверки того, что запрос действительно выполняется с допустимым JWT.

Если срок действия обычного токена пользователя истек, а токена обновления нет. Тогда обычная проверка токена завершится неудачно, и токен обновления пройдет. Затем оба токена сбрасываются, добавляются к полезной нагрузке ответа, а затем запрос обрабатывается как обычно.

Однако, если срок действия обоих токенов пользователя истек, запрос будет отклонен из-за отсутствия авторизации, и пользователю потребуется снова войти в систему, чтобы успешно выполнить запрос.

Начните с установки express-jwt:

npm i - save express-jwt

Во-первых, давайте добавим в наш .env секретные ключи для токенов (они могут быть любой строкой, просто сделайте их уникальными):

MONGO_URI=mongodb+srv://admin-brandon:[email protected]/test?retryWrites=true&w=majority
PORT=3000
ACCESS_TOKEN_SECRET=abcdefghijklm
REFRESH_TOKEN_SECRET=nopqrstuvwxyz

Затем мы обновим наш контроллер входа в controllers/auth.js:

const User = require("../models/user");
const {validator} = require("../validators/auth");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
// @route POST api/auth/register
// @desc Register a user
// @access Public
exports.register = async (req, res) => {
    try {
        // Validate the request body
        const valid = validator.register(req.body);
        if (!valid) {
            return res.status(400).send({
                message: valid.error.map(err => err.message)
            });
        }
        let data = req.body;
        // Check if the user already exists
        let user = await User.findOne({
            email: data.email
        });
        // If the user already exists, return an error
        if (user) {
            return res.status(400).send({
                message: "User already exists"
            });
        }
        // Check if the passwords match
        if (data.password !== data.confirmPassword) {
            return res.status(400).send({
                message: "Passwords do not match"
            });
        } else {
            delete data.confirmPassword;
            data.password = bcrypt.hashSync(data.password, 10);
        }
        // Create and save a new user
        user = new User(data);
        await user.save();
        user.password = undefined;
        // Return a success message
        return res.status(200).send({
            message: "Registration successful",
            user: user
        });
    } catch (err) {
        console.log(err.message);
        return res.status(500).send({
            message: err.toString()
        });
    }
}
// @route POST api/auth/login
// @desc Login a user
// @access Public
exports.login = async (req, res) => {
    try {
        // Validate the request body
        const valid = validator.login(req.body);
        if (!valid) {
            return res.status(400).send({
                message: valid.error.map(err => err.message)
            });
        }
        let data = req.body;
        // Check if the user exists
        let user = await User.findOne({
            email: req.body.email
        });
        // If the user does not exist, return an error
        if (!user) {
            return res.status(400).send({
                message: "Invalid email/password combination"
            });
        }
        // Check if the password is correct
        if (!await bcrypt.compare(data.password, user.password)) {
            return res.status(400).send({
                message: "Invalid email/password combination"
            });
        }
        // Sign new tokens
        const accessToken = jwt.sign({
            _id: user._id
        }, process.env.ACCESS_TOKEN_SECRET, {
            expiresIn: "15m",
        });
        const refreshToken = jwt.sign({
            _id: user._id
        }, process.env.REFRESH_TOKEN_SECRET, {
            expiresIn: "90d",
        });
        await res.header("access-token", accessToken);
        await res.header("refresh-token", refreshToken);
        // Update and save the lastLoggedIn field
        user.lastLoggedIn = Date.now();
        await user.save();
        user.password = undefined;
        // Return a success message
        return res.status(200).send({
            message: "Login successful",
            user: user
        });
    } catch (err) {
        console.error(err.message);
        return res.status(500).send({
            message: "Server error"
        });
    }
}

Теперь создайте промежуточное ПО jwt в файле middleware/jwt.js.

const jwt = require("jsonwebtoken");
const {
    TokenExpiredError,
    JsonWebTokenError
} = jwt;
module.exports = verifyTokens = async (req, res, next) => {
    // Grab the tokens from the header
    const accessToken = req.header("access-token");
    const refreshToken = req.header("refresh-token");
    // Make sure the user added the tokens to the request payload
    if (!accessToken || !refreshToken) {
        return res
            .status(401)
            .send({
                message: "Unauthorized. Access token is required."
            });
    }
    try {
        // Verify the auth token
        const authVerified = jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET);
        // If the auth token is valid, pass the request to the next middleware
        if (authVerified) {
            console.log("Token verified for user: " + authVerified._id);
            req.user = authVerified;
            await res.header("access-token", accessToken);
            await res.header("refresh-token", refreshToken);
            next();
        }
    } catch (error) {
        // If the auth token is expired, try to refresh it
        return this.jwtMiddleware.catchError(jwt.decode(accessToken)["_id"], error, req, res, next);
    }
};
module.exports = catchError = async (_id, err, req, res, next) => {
    if (err instanceof TokenExpiredError) {
        // Verify refresh token
        try {
            let refreshToken = req.header("refresh-token");
            const refreshVerified = jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET);
            if (refreshVerified) {
                const accessToken = jwt.sign({
                    _id: _id
                }, process.env.ACCESS_TOKEN_SECRET, {
                    expiresIn: "15m",
                });
                refreshToken = jwt.sign({
                    _id: _id
                }, process.env.REFRESH_TOKEN_SECRET, {
                    expiresIn: "90d",
                });
                await res.header("access-token", accessToken);
                await res.header("refresh-token", refreshToken);
                next();
            }
        } catch (err) {
            return res.status(401).send({
                message: "Invalid refresh token"
            });
        }
    } else if (err instanceof JsonWebTokenError) {
        console.log(err.stack);
        return res.status(401).send({
            message: "Invalid token"
        });
    } else {
        return res.status(500).send({
            message: "Unauthorized"
        });
    }
};

Наконец, добавьте это промежуточное ПО к своим частным маршрутам. Или, другими словами, любые маршруты, для которых требуется авторизация. На данный момент в этом проекте нет никого, кто мог бы это проверить. Давайте создадим маршрутизатор для некоторых методов чтения и обновления для конкретного пользователя.

проверки/user.js

const Joi = require("joi");
module.exports.validator = {
    update: data => {
        const schema = Joi.object({
            name: Joi.string().optional(),
        });
        return schema.validate(data)
    },
}

контроллеры/user.js

const User = require('../models/user');
const {validator} = require('../validators/user');

// @route   GET api/user/:id
exports.getUser = async (req, res) => {
    try {
        const id = req.params.id;
        if (!id) {
            return res.status(400).send({message: "Invalid user id"});
        }

        // Check if the user exists
        let user = await User.findById({_id: id});
        if (!user) {
            return res.status(404).send({message: "User not found"});
        }
        // Remove hashed password from the response
        user.password = undefined;
        return res.status(200).send({message: "User found", user: user});
    } catch (err) {
        console.log(err.message);
        return res.status(500).send({message: err.toString()});
    }
}

exports.updateUser = async (req, res) => {
    try {
        // Get the user id from the request params
        const id = req.params.id;

        // Check if the given id is the same as the id in the access token
        if (id !== req.user.id) {
            return res.status(401).send({message: "Unauthorized"});
        }

        // Get the request body
        let data = req.body;
        // Add id to the data object for validation of the id
        data.id = id;
        // Validate the request body
        const valid = validator.update(data);
        if (!valid) {
            return res.status(400).send({message: valid.error.map(err => err.message)});
        }

        // Check if the user exists
        let user = await User.findById({_id: id});
        if (!user) {
            return res.status(404).send({message: "User not found"});
        }

        // Update the user
        user.set(data);
        await user.save();
        user.password = undefined;

        return res.status(200).send({message: "User successfully updated", user: user});
    }
    catch (err) {
        console.log(err.message);
        return res.status(500).send({message: err.toString()});
    }
}

маршруты/user.js

const express = require('express');
const router = express.Router();
const {
    getUser,
    updateUser
} = require('../controllers/user');
const {
    jwtMiddleware
} = require('../middleware/jwt');

// @route GET api/auth
// @desc Get user profile
// @access Public
router.get("/:id", getUser);

// @route PATCH api/auth
// @desc Update user
// @access Private
router.patch("/:id", verifyTokens, updateUser);

module.exports = router;

app.js

const express = require('express');
const {
    xss
} = require('express-xss-sanitizer');
const logger = require('morgan');
const dotenv = require('dotenv');
// Load environment variables
dotenv.config({
    path: './config/.env'
});
// Create an express app
const app = express();
// Setup middleware
app.use(xss());
app.use(logger("dev"));
app.use(express.json({
    limit: '1kb',
    extended: false
}));
// Setup routes
const authRouter = require('./routes/auth');
app.use("/v1/auth", authRouter);
const userRouter = require('./routes/user');
app.use("/v1/user", userRouter);
// Connect to the database
const connectDB = require('./middleware/db');
connectDB(process.env.MONGO_URI);
// Set the port
app.listen(process.env.PORT, () => {
    console.log(`Server is listening on port ${process.env.PORT}`);
})