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

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

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 Linkedin. Linkedin 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 sezione sviluppatori di Linkedin vanno specificati gli URL assoluti per il callback OAuth. Gli scope OAuth vanno definiti in Passport nella fase di inizializzazione.

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

LINKEDIN_API_KEY=your-client-id
LINKEDIN_SECRET_KEY=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-linkedin-oauth2');
const { LINKEDIN_API_KEY, LINKEDIN_SECRET_KEY, 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: LINKEDIN_API_KEY,
    clientSecret: LINKEDIN_SECRET_KEY,
    callbackURL: '/return',
    scope: ['r_emailaddress', 'r_liteprofile'],
    state: true
  },
  (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 Linkedin.

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/linkedin', passport.authenticate('linkedin'));

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

router.get('/return', 
  passport.authenticate('linkedin', { failureRedirect: '/', successRedirect: '/' }),
  (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/linkedin" class="loginBtn loginBtn--linkedin">Login with Linkedin</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