May 6, 2020

Como usar Passport JS con facebook, JWT, MongoDB, Express JS y Vue JS

Como usar Passport JS con facebook, JWT, MongoDB, Express JS y Vue JS

En este ejemplo te enseñare como crear un login social de una manera muy simple con 10 pasos.

enlace del repositorio de ejemplo

Indice

    1. Instalación de dependencias en el backend.
    1. Estructura del backend.
    1. Creación del servidor Express JS.
    1. Conexión a la base de datos MongoDB
    1. Creación de Schema de base de datos MongoDB.
    1. Configuracion de Passport JS con facebook.
    1. Creación de rutas de Registro e Inicio de Sesión.
    1. Creación de boton de de Registrarse e Inicio de Sesion con Vuejs
    1. Configuracion de Passport-JWT
    1. Creacion de ruta protegida

1) Instalación de dependencias en el backend.

npm install express passport passport-facebook passport-jwt cors jsonwebtoken && npm install nodemon -D

2) Estructura del backend.

Vamos a crear una estructura de carpetas para los archivos que usaremos en este ejemplo:

--src/
  models/
        user.js
  passport/
         facebook-auth.js
         verify-token.js
  routes/
       routes.js
       rutasProtegidas.js

--database.js
-server.js
-package.json

3) Creación del servidor Express JS.

crearemos un servidor muy sencillo con el framework de Node JS

server.js

const express = require('express');
const morgan = require('morgan')
const passport = require('passport')
const cors = require('cors')

const app = express()
//settings
//middlewares
app.use(passport.initialize());
app.use(morgan('dev'))// para ver los estados http de las peticiones
app.use(express.json());
app.use(express.urlencoded({extended:false}))
app.use(cors())

//importando rutas
app.use('/', require('./routes/routes'));

//Creando servidor
let puerto = process.env.PORT || 3000
app.listen(puerto, () => console.log(`server on port ${puerto}`))

4) Conexión a la base de datos MongoDB

database.js

const mongoose = require('mongoose')

mongoose.connect("mongodb://localhost:27017/covid-19",{
useNewUrlParser:true,
useUnifiedTopology:true
}).then(db => console.log('database is connected'))
.catch(err => console.log(err));  

5) Creación de Schema de base de datos MongoDB.

Para este ejemplo usaremos Mongoose como ORM.

models/user.js

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

const userSchema = new Schema({
    email: String,
    firstname: String,
    last_name: String,
    imagen:String,
    _id:String,
    incluido: {type: Boolean, default: false}
});

module.exports = mongoose.model('users', userSchema);

6) Configuracion de Passport JS con facebook.

Creamos un estrategia, para usar la autenticación de Facebook, primero debe crear una aplicación en Facebook Developers en el cual obtendremos:

  • App ID
  • App Secret.

passport/facebook-auth.js

const passport = require("passport");
const FacebookStrategy = require("passport-facebook").Strategy;
const User = require("../models/user");

passport.serializeUser((user, done) => {
  done(null, user.id);
});

passport.deserializeUser(async (id, done) => {
  const user = await User.findById(id);
  done(null, user);
});

// Estrategia para Registrarse

passport.use(
  "sign-in-facebook",
  new FacebookStrategy(
    {
      clientID: 11228228282, // coloca tu propio clientID
      clientSecret: "2222wssandndnehenf",// coloca tu propio secret
      callbackURL: "http://localhost:3000/auth/facebook/callback",
      profileFields: ["email", "name", "photos","profileUrl"],
    },
    async (accessToken, refreshToken, profile, done) => {
      const user = await User.findById(profile.id); //si no existe en la base de datos lo guarda
      if (user) {
        return done(null,false);
      } else {        
        const { email, first_name, last_name } = profile._json;
        const user = new User();
        (user.email = email),
          (user.firstname = first_name),
          (user.last_name = last_name);
        user._id = profile.id;
        user.imagen = profile.photos[0].value
        await user.save(); //guardamos en la base de datos
        done(null, profile); //devolvemos el profile con todos los datos del usuario
      }
    }
  )
);

// Estrategia para Iniciar Sesion

passport.use(
  "sign-up-facebook",
  new FacebookStrategy(
    {
      clientID: 2576357032629101111111117, // coloca tu propio clientID
      clientSecret: "3af7d2f7630645b43ef7791111111c148e3ce8d",// coloca tu propio secret
      callbackURL: "http://localhost:3000/auth/facebook/signin",
      profileFields: ["email", "name", "photos"],
    },
    async (accessToken, refreshToken, profile, done) => {
      const user = await User.findById(profile.id); // si existe en la base de datos
                                                    //  puede iniciar sesion
      if (user) {
        done(null, user);
      } else {
        return done(null, false);
      }
    }
  )
);

7) Creación de rutas de Registro e Inicio de Sesión.

Creamos las rutas y utilizaremos jsonwebtoken para poder crear un token con caducidad de 24 horas.

Para este ejemplo mandaremos al cliente el token por cookie lo recomendable es que le ponga otro nombre para hacer “un poco mas seguro el token” con fines educativos le coloque token

routes/routes.js

const router = require("express").Router();
const passport = require("passport");
const User = require('../models/user')
const jwt = require('jsonwebtoken') //importamos el modulo para crear el token
//rutas para facebook
router.get(
  "/auth/facebook",
  passport.authenticate("sign-in-facebook", {
    scope: ["email"],
  })
);
//ruta para Registrarse

router.get("/auth/facebook/callback",passport.authenticate("sign-in-facebook", { session: true }),
  function (req, res) {
    if (req.user) { 
      const token = jwt.sign({id: req.user._id}, 'top_secret', {
        expiresIn: 60 * 60 * 24 // equivalente a 24 horas
      })
      res.cookie('token', token) 
      console.log('el token es ',token);
          
      res.redirect(`http://localhost:8080/about`);
    } else {
      failureRedirect: `http://localhost:8080/`;
    }
  }
);

//rutas para Iniciar Sesion
router.get(
  "/auth/facebook/signin",
  passport.authenticate("sign-up-facebook", { session: true }),
  function (req, res) {
    if (req.user) {
      const token = jwt.sign({id: req.user._id}, 'top_secret', {
        expiresIn: 60 * 60 * 24 // equivalente a 24 horas
      })
      res.cookie('token', token) 
      res.redirect(`http://localhost:8080/animales`);
    } else {
      failureRedirect: `http://localhost:8080/`;
    } 
  }
);

module.exports = router;

8) Creación de boton de de Registrarse e Inicio de Sesion con Vuejs

Para este ejemplo muy sencillo solo utilizaremos una etiqueta para indicarle la ruta que creamos en el servidor

Enlace de Registro

<template>
  <div>
      <a href="http://localhost:3000/auth/facebook/callback"> Registrarse con facebook</a>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

Enlace de Inicio de Sesión

<template>
  <div>
    <a href="http://localhost:3000/auth/facebook/signin">
      Iniciar Sesion con facebook
    </a>
  </div>
</template>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

9) Configuracion de Passport-JWT

Esta estrategia nos servira para usarla como middleware ya que con esta verificaremos el token que nos pasa el usuario.

en secretOrKey pasamos el texto con el cual firmamos el token en la ruta

ExtractJWT.fromUrlQueryParameter(‘secret_token’) le indicamos a la estrategia que le pasaremos el token por un parametro llamado secret_token

passport/verify-token.js*

const JWTstrategy = require('passport-jwt').Strategy;
const passport = require('passport')
//Usamos esto para extraer el JWT enviado por el usuario
const ExtractJWT = require('passport-jwt').ExtractJwt;

//Esto verifica que el token enviado por el usuario es válido
passport.use(new JWTstrategy({
  //secreto que solíamos firmar nuestro JWT
  secretOrKey : 'top_secret',
  //esperamos que el usuario envíe el token como un parámetro de consulta con el nombre 'secret_token'
  jwtFromRequest : ExtractJWT.fromUrlQueryParameter('secret_token')
}, (token, done) => {
  try {
    //Pass the user details to the next middleware
    return done(null, token);
  } catch (error) {
    done(error);
  }
}))

Creamos la vista de animales

Obtenemos el token definimos en el backend con document.cookie

Si iniciamos sesion correctamente debemos ver el arreglo de animales por pantalla.

<template>
  <div>
    <h1>Animales</h1>
    <h1 v-for="a in animales" :key="a.id">{{a.nombre}}</h1>
  </div>
</template>

<script>
import axios from "axios";
export default {
  created() {
    let valor = document.cookie.split("token=");
    this.cookie = valor[1];
  },
  mounted(){
    this.obtenerAnimales()
  },
  data() {
    return {
      cookie: "",
      animales: []
    };
  },
  methods:{
    obtenerAnimales(){
        // haces la peticion a la ruta animal del servidor
   axios.get(`http://localhost:3000/api/animales?secret_token=${this.cookie}`)
   .then(res => {     
     this.animales = res.data.animales
     })

   .catch(err => console.log(err))
    }
  }
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

10) Creacion de ruta protegida

Para este ejemplo crearemos una ruta /animales la cual devuelve como json un arreglo de animales

todas las rutas que pongamos en este archivo seran protegidas

routes/rutasProtegidas

const router = require("express").Router();

router.get("/animales", (req, res, next) => {
  let animales = [
      { id: 1, nombre: "Elefante" },
      { id: 2, nombre: "Perro" },
      { id: 1, nombre: "Tigre" }
    ];

    res.json({"animales": animales})
  next();
});

module.exports = router;

Finalmente nuestro server.js

importamos los ficheros de las estrategias

app.use('/api’, passport.authenticate(‘jwt’, { session : false }), rutasProtegidas ); le estamos indicando que deben pasar por la estrategia para poder acceder a la ruta

const express = require('express');
const morgan = require('morgan')
const passport = require('passport')
const cors = require('cors')
//initializations
const app = express()
require('./database')
require('./passport/facebook-auth')
require('./passport/verify-token')
//settings
//middlewares
app.use(passport.initialize());
app.use(morgan('dev'))// para ver los estados http de las peticiones
app.use(express.json());
app.use(express.urlencoded({extended:false}))
app.use(cors())

//importando rutas
app.use('/', require('./routes/routes'));
const rutasProtegidas = require('./routes/rutasProtegidas')
app.use('/api', passport.authenticate('jwt', { session : false }), rutasProtegidas );

let puerto = process.env.PORT || 3000
app.listen(puerto, () => console.log(`server on port ${puerto}`))

enlace del repositorio de ejemplo