May 11, 2020

Como usar Passport JS con Google, JWT, MongoDB, Express JS y React JS

Como usar Passport JS con Google, JWT, MongoDB, Express JS y React 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.
  2. Estructura del backend.
  3. Creación del servidor Express JS.
  4. Conexión a la base de datos MongoDB
  5. Creación de Schema de base de datos MongoDB.
  6. Configuracion de Passport JS con Google.
  7. Creación de rutas de Registro e Inicio de Sesión.
  8. Creación de boton de de Registrarse e Inicio de Sesion con React JS
  9. Configuracion de Passport-JWT
  10. Creacion de ruta protegida

1) Instalación de dependencias en el backend.

npm install express passport passport-google-oauth passport-jwt cors jsonwebtoken && npm install nodemon -D

2) Estructura del backend.

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

.babelrc
-package.json

Extra

Para este ejemplo decidi usar Babel JS para tener las ultimas caracteristicas de JavaSCript

Instalacion de dependencias de Babel JS

npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node

Configuracion de Babel JS para el backend .babelrc

{
    "presets": [
        "@babel/preset-env"
    ]
}

package.json

{
  "name": "passportjs-google-jwt",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "build": "babel src -d dist --source-maps",
    "dev": "nodemon src/server.js --exec babel-node"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/polyfill": "^7.8.7",
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "jsonwebtoken": "^8.5.1",
    "mongoose": "^5.9.12",
    "morgan": "^1.10.0",
    "passport": "^0.4.1",
    "passport-google-oauth": "^2.0.0",
    "passport-jwt": "^4.0.0"
  },
  "devDependencies": {
    "@babel/cli": "^7.8.4",
    "@babel/core": "^7.9.6",
    "@babel/node": "^7.8.7",
    "@babel/preset-env": "^7.9.6",
    "nodemon": "^2.0.3"
  }
}

3) Creación del servidor Express JS.

crearemos un servidor muy sencillo con el framework de Node JS

src/server.js

import express, {json, urlencoded} from "express"
import morgan from "morgan";
import passport from "passport";
import cors from "cors";
const app = express()

//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
import routes from "./routes/routes";
app.use('/', routes);

//Creando servidor
import  {conectarDatabase} from "./database"; //importando funcion para conectar ala base de datos
let puerto = process.env.PORT || 4000
app.listen(puerto, () => {
    console.log(`server on port ${puerto}`)
    conectarDatabase()
})

4) Conexión a la base de datos MongoDB

src/database.js

import { connect } from "mongoose";

export function conectarDatabase() {
  try {
    connect("mongodb://localhost:27017/googlepassport", {
      useNewUrlParser: true,
      useUnifiedTopology: true
    });
    console.log('conectado a MongoDB');
    
  } catch (error) {
    console.log(error);
  }
}


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

Este modelo esta basado en la informacion que nos comparte Google

src/models/user.js

import mongoose, { model, Schema } from 'mongoose';

const userSchema = new Schema({
    _id: String,
    nombre: String,
    avatar: String,
    fechadeRegistro: {type: String, default: new Date().toISOString()}
})
export default model('users', userSchema);

6) Configuracion de Passport JS con Google.

Creamos un estrategia, para usar la autenticación de Google, primero debe crear una aplicación en Google API Console en el cual obtendras:

  • clientID
  • clientSecret

Define 2 Callbacks URL

  1. Para Registrarse
  2. Para Iniciar Sesión

src/passport/google-auth.js

import passport from "passport";
var GoogleStrategy = require("passport-google-oauth").OAuth2Strategy;

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

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-google",new GoogleStrategy(
    {
      clientID:"ssjsjsjjsj111j1j1j1iiwjwnwwjww",
      clientSecret: "jsjsjsjjsjss",
      callbackURL: "http://localhost:4000/auth/google/callback",
    },
    async (accessToken, refreshToken, profile, done) => {
      const user = await User.findById(profile.id); // si el usuario no existe 
                                                    //lo creamos
      if (user) {
        done(null, false);
      } else {
        let newUser = new User();
            newUser._id = profile.id
            newUser.nombre = profile.displayName
            newUser.avatar = profile.photos[0].value
           await newUser.save() //guardamos en la base de datos
        done(null, profile); //guardamos en la base de datos
      }
    }
  )
);


// Estrategia para Iniciar Sesion


passport.use("sign-up-google",new GoogleStrategy(
  {
    clientID:"sjsjsjsjssswww",
    clientSecret: "sjsjsjsjsjsj",
    callbackURL: "http://localhost:4000/auth/google/signup",
  },
  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 {
      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

src/routes/routes.js

import { Router } from "express";
import passport from "passport";
import jwt from "jsonwebtoken";
const router = Router()

//rutas para Google

//ruta para Registrarse

router.get("/auth/google/callback",passport.authenticate("sign-in-google", {scope: ['https://www.googleapis.com/auth/plus.login'], session: false }),
  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:3000/jugadores')

    } else {
      res.redirect('http://localhost:3000/registrarse')
    }
  }
);


//rutas para Iniciar Sesion
router.get(
  "/auth/google/signup",
  passport.authenticate("sign-up-google", {scope: ['https://www.googleapis.com/auth/plus.login'], session: false }),
  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:3000/jugadores')
    } else {
      res.redirect('http://localhost:3000/iniciarsesion')
    } 
  }
);

export default router;

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

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

Registrarse.js

import React from 'react'

export default function Registrase() {
    return (
        <div>
            <a className="enlace" href="http://localhost:4000/auth/google/callback">Registrase con Google</a>
        </div>
    )
}

iniciarSesion.js

import React from 'react'

export default function IniciarSesion() {
    return (
        <div>
            <a className="enlace" href="http://localhost:4000/auth/google/signup">Iniciar con Google</a>
        </div>
    )
}

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

src/passport/verify-token.js

const JWTstrategy = require('passport-jwt').Strategy;
const passport = require('passport')

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 jugadores

ESta es una vista en la cual si tenemos el token valido veremos los jugadores pintados en pantalla. Obtenemos el token definimos en el backend con document.cookie

import React, { useEffect, useState } from "react";
import axios from "axios";
export default function Jugadores() {
  let valor = document.cookie.split("token=");
  const [cookie] = useState(valor[1]);
  const [jugadores, setjugadores] = useState([]);

  useEffect(() => {
    async function getJugadores() {
      const response = await axios.get(`http://localhost:4000/api/jugadores?secret_token=${cookie}`)
        setjugadores(response.data.jugadores)
    };
    getJugadores()
  },[cookie]);
  
  return (
    <div>
      <h1>Jugadores</h1>
      <div>{cookie}</div>
      <div>
        {jugadores.map(jugador =>(
           <h1 key={jugador.id}>{jugador.nombre}</h1>
        ))}
      </div>
    </div>
  );
}

10) Creacion de ruta protegida

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

todas las rutas que pongamos en este archivo seran protegidas

src/routes/rutasProtegidas.js

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

router.get("/jugadores", (req, res, next) => {
  let jugadores = [
      { id: 1, nombre: "Lebron James" },
      { id: 2, nombre: "Kristaps Porziņģis" },
      { id: 3, nombre: "Nelson Hernandez" }
    ];

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

export default router;

Finalmente nuestro src/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

import express, {json, urlencoded} from "express"
import morgan from "morgan";
import passport from "passport";
import cors from "cors";
const app = express()

import('./passport/google-auth')
import('./passport/verify-token')

//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
import routes from "./routes/routes";
app.use('/', routes);
import rutasProtegidas from "./routes/rutasProtegidas";
app.use('/api', passport.authenticate('jwt', { session : false }), rutasProtegidas );

//Creando servidor
import  {conectarDatabase} from "./database"; //importando funcion para conectar ala base de datos
let puerto = process.env.PORT || 4000
app.listen(puerto, () => {
    console.log(`server on port ${puerto}`)
    conectarDatabase()
})

enlace del repositorio de ejemplo