This post is also available in: English Português

Al crear una API, hay momentos en los que no desea que su API sea públicamente accesible para todos. Ya sea que tenga un nombre de usuario y contraseña simples, o un cheque que verifique que alguien es un cliente pagado, necesitará una forma de proteger sus rutas de usuarios que no estén autenticados. Una de las mejores maneras de hacerlo es usando JWT. JWT es un token sin estado que comprueba con su sistema back-end para validar sus solicitudes de usuario. Puede obtener acceso al video tutorial aquí.

Configuración del proyecto

Suponiendo que ya tiene una API en su lugar, querrá agregar dependencias para la autenticación.

npm install –save passport passport-local passport-local-mongoose

Passport es una herramienta para la autenticación de aplicaciones Node.js mientras que Passport-Local y Passport-Local-Mongoose ayudan a agregar la capacidad de utilizar un nombre de usuario simple y autenticación de contraseña. Passport-Local-Mongoose maneja específicamente el hash del pasaporte y la sal en su Documento de usuario en Mangoose.

Ahora que tenemos los instalados, caminaremos para extraer dependencias para manejar nuestra creación y autenticación JWT con Express.js.

npm install –save express-jwt jsonwebtoken passport-jwt

Passport-JWT agrega middleware al Passport para que acepte tokens web JSON como un tipo de autenticación válido. Ahora que tenemos todas nuestras dependencias agregadas, agregemos Passport a nuestra aplicación.

Primero, necesitamos crear un modelo de usuario para representar nuestro usuario almacenado y credencial en nuestra base de datos.

import mongoose from 'mongoose';
const Schema = mongoose.Schema;
import passportLocalMongoose from  'passport-local-mongoose';


let userSchema = new Schema({
    firstName: String,
    lastName: String,
    email: String,
    password: String
});

userSchema.plugin(passportLocalMongoose);

let User = mongoose.model('User',userSchema);

export default User;

Como puede ver, agregamos nuestro correo electrónico y pasaporte al modelo, y luego justo debajo de él, obtenemos el plugin passport-local-mongoose para manejar nuestro hash de contraseñas. Aunque esto se puede manejar manualmente, debes seguir los principios de DRY. Si hay un paquete para manejar una tarea común, no reinvente la rueda.

Ahora que hemos creado nuestro modelo de usuario, ahora podemos agregarlo a nuestra canalización de pasaportes y configurar el pasaporte para conectarse con Express.

Agregue estas dos instrucciones de importación a su archivo server.js (o app.js)

import passport from 'passport';
import User from './models/user';

A continuación, elija el método de autenticación y configuración JWT que desea utilizar.

const passportJWT = require("passport-jwt");
const JWTStrategy   = passportJWT.Strategy;
const ExtractJWT = passportJWT.ExtractJwt;

const LocalStrategy = require('passport-local').Strategy;
server.use(passport.initialize());
passport.use(new LocalStrategy({
        usernameField: 'email',
        passwordField: 'password'
    },
    User.authenticate()
));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
passport.use(new JWTStrategy({
        jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
        secretOrKey   : 'ILovePokemon'
    },
    function (jwtPayload, cb) {

        //find the user in db if needed. This functionality may be omitted if you store everything you'll need in JWT payload.
        return User.findById(jwtPayload.id)
            .then(user => {
                return cb(null, user);
            })
            .catch(err => {
                return cb(err);
            });
    }
));

Como puede ver aquí, inicializamos una instancia de Passport, establecemos la estrategia en local y, a continuación, establecemos los campos que queremos usar, así como el modelo de usuario. La estrategia local solo significa usar un nombre de usuario y una contraseña.

Justo después de eso, le contamos a Passport cómo planeamos realizar publicaciones seriales y deserializar a nuestro usuario, y cómo configurar nuestro JWT con nuestra estrategia JWT. ExtractJwt.FromAuthHeadErasBearerToken (), especifica que los tokens JWT se enviarán como tokens portador en las solicitudes HTTP entrantes y que nuestra clave secreta para encriptar nuestros tokens se almacena en SecretorKey.

Configuración de inicio de sesión de usuario y autenticación JWT

Ahora que hemos configurado Passport, vamos a querer crear controladores manejar nuestros registros de usuarios y nuestro inicio de sesión que devuelve un JWT válido.

Cree un controlador llamado auth.controller.js e importe lo siguiente

import User from '../models/user';
import bodyParser from 'body-parser';

import passport from 'passport';
const AuthController = {};
import jwt from 'jsonwebtoken';

Importamos Passport, nuestro modelo, así como jsonwebtoken como método para firmar nuestros tokens.

También creamos un objeto llamado AuthController para exportar más tarde a otros archivos.

Ahora que eso está hecho, cree la función para registrar al usuario.

AuthController.register = async (req, res) => {
    try{
        User.register(new User({ username: req.body.email,
            firstName: req.body.firstName,
            lastName: req.body.lastName,
            }), req.body.password, function(err, account) {
            if (err) {
                return res.status(500).send('An error occurred: ' + err);
            }

            passport.authenticate(
                'local', {
                    session: false
                })(req, res, () => {
                res.status(200).send('Successfully created new account');
            });
        });
    }
    catch(err){
        return res.status(500).send('An error occurred: ' + err);
    }
};

El método User.register () toma como parámetros un nuevo modelo de usuario, la contraseña y la madre de la autenticación.

Ahora crea el método para iniciar sesión

AuthController.login = async (req, res, next) => {
    try {
        if (!req.body.email || !req.body.password) {
            return res.status(400).json({
                message: 'Something is not right with your input'
            });
        }
        passport.authenticate('local', {session: false}, (err, user, info) => {
            if (err || !user) {
                return res.status(400).json({
                    message: 'Something is not right',
                    user   : user
                });
            }
            req.login(user, {session: false}, (err) => {
                if (err) {
                    res.send(err);
                }
                // generate a signed son web token with the contents of user object and return it in the response
                const token = jwt.sign({ id: user.id, email: user.username}, 'ILovePokemon');
                return res.json({user: user.username, token});
            });
        })(req, res);
    }
    catch(err){
        console.log(err);
    }
};

Esto comprueba que los campos de correo electrónico y contraseña no estén vacíos. Esto está usando la autenticación local del pasaporte para verificar si es un usuario válido. Luego firma un token usando la información de ese usuario y devuelve ese token como JSON.

Cómo proteger rutas

Ahora que tenemos los métodos de autenticación creados, vamos a tener que crear rutas para ellos.

Crear los dos archivos auth.routes.js y user.routes.js

import { Router } from 'express';
import AuthController from '../controllers/auth.controller';

const router = new Router();

router.post('/register', (req, res) => {
    AuthController.register(req, res);
});

router.post('/login', (req, res, next) => {
    AuthController.login(req, res, next);
});

export default router;

 

Aquí puede ver que importamos Express y conseguimos que el enrutador se exporte posteriormente. También creamos rutas Post para nuestro AuthController.

En nuestro archivo user.routes.js

import { Router } from 'express';
const router = new Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
    res.send('respond with a resource');
});

/* GET user profile. */
router.get('/profile', function(req, res, next) {
    res.send(req.user);
});

export default router;

Esto devuelve el usuario que realizó la solicitud.

Ahora, si volvemos a nuestro archivo server.js podemos agregar esos archivos de ruta a nuestra canalización express

import user from './routes/user.routes';
import auth from './routes/auth.routes';

server.use('/auth', auth);
server.use('/user', passport.authenticate('jwt', {session: false}), user);

Aquí pasamos en Passport como nuestro método de autenticación. El usuario ahora debe tener un token JWT en cada encabezado para realizar solicitudes a ese controlador.

User Creation

User Creation

User Login

User Login

JWT user profile

JWT user profile

Conclusion

Además de tener un token JWT en el encabezado, también puede usar esto como un método para crear documentos MongoDB que están relacionados con un usuario. Un ejemplo de esto es tener un ID de usuario autenticado asociado a una publicación o comentario en el momento de la creación. Echa un vistazo al repositorio de GitHub para obtener el código fuente completo de este tutorial.

Codebrains Newsletter

Get weekly dev news and tutorials.

Powered by ConvertKit

This post is also available in: English Português