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
- Instalación de dependencias en el backend.
- Estructura del backend.
- Creación del servidor Express JS.
- Conexión a la base de datos MongoDB
- Creación de Schema de base de datos MongoDB.
- Configuracion de Passport JS con Google.
- Creación de rutas de Registro e Inicio de Sesión.
- Creación de boton de de Registrarse e Inicio de Sesion con React JS
- Configuracion de Passport-JWT
- 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
- Para Registrarse
- 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()
})


