In questo articolo vedremo come implementare un sistema di login con Google in ExpressJS.

Useremo Passport come middleware di autenticazione tramite OAuth con una sua strategy specifica dedicata a Google.

Passport inserisce nell'oggetto request metodi e proprietà. Una di queste proprietà è l'oggetto user che contiene i dati dell'utente restituiti dalla piattaforma su cui si effettua l'autenticazione. Questo oggetto viene salvato nella sessione corrente ed è disponibile in tutte le route.

Occorre fare una precisazione sugli URL di callback di un'applicazione Google. Google distingue due tipi di ambienti: sviluppo e produzione. In sviluppo un URL come http://localhost:3000/return può esssere gestito da Passport come relativo, ossia semplicemente /return. Al contrario in produzione l'URL di callback inserito in Passport nella fase di inizializzazione deve essere assoluto.

Nella Google Developers Console vanno specificati sia il dominio base dell'applicazione sia gli URL assoluti per il callback OAuth e quello dell'origine consentita per l'accesso. Lo scope OAuth sarà profile.

Creiamo un file .env con le nostre credenziali di accesso.

GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
SESSION_SECRET=choose-a-random-string

Carichiamo la configurazione all'inizio del file principale.

'use strict';

require('dotenv').config();

Ora definiamo le risorse principali della nostra applicazione.

const path = require('path');
const express = require('express');
const passport = require('passport');
const { Strategy } = require('passport-google-oauth20');
const { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, SESSION_SECRET } =  process.env;
const port = process.env.PORT || 3000;
const app = express();
const routes = require('./routes');

A questo punto configuriamo Passport con le sue impostazioni di base.

passport.use(new Strategy({
    clientID: GOOGLE_CLIENT_ID,
    clientSecret: GOOGLE_CLIENT_SECRET,
    callbackURL: '/return'
  },
  (accessToken, refreshToken, profile, cb) => {
    return cb(null, profile);
}));

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

passport.deserializeUser((obj, cb) => {
  cb(null, obj);
});

callbackURL definisce l'URL a cui l'utente verrà reindirizzato dopo l'autenticazione OAuth. In produzione dovete impostare l'URL completo nelle impostazioni dell'app Google.

Ora possiamo inizializzare Passport e collegarlo alla sessione.

app.use(require('express-session')({ secret: SESSION_SECRET, resave: true, saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());

app.use('/', routes);

app.listen(port);

Dobbiamo quindi definire le route della nostra applicazione tenendo a mente che ci dovrà essere una route per il login, una per il logout ed una per l'URL di callback OAuth. In tutti i casi effettueremo un reindirizzamento HTTP sulla home page.

'use strict';

const express = require('express');
const passport = require('passport');
const router = express.Router();

router.get('/', (req, res, next) => {
    const { user } = req;
    res.render('home', { user });
});

router.get('/login/google', passport.authenticate('google', { scope: ['profile'] }));

router.get('/logout', (req, res, next) => {
  req.logout();
  res.redirect('/');
});

router.get('/return', 
  passport.authenticate('google', { failureRedirect: '/' }),
  (req, res, next) => {
    res.redirect('/');
});

module.exports = router;

La view della home page verrà modificata dinamicamente in base al fatto che un utente abbia effettuato il login o meno.

<% if (!user) { %>
    <h1>Welcome!</h1> 
    <p class="mt-5"><a href="/login/google" class="loginBtn loginBtn--google">Login with Google</a></p>
<% } else { %>
    <h1>Hello, <%= user.displayName %>.</h1>
    <p class="mt-5"><a href="/logout" class="btn btn-primary">Logout</a></p>
<% } %>

Demo

Heroku

Codice sorgente

GitHub