Ionic e Node: Come Configurare la CORS Policy per Applicazioni Web
CORS policy con Ionic e Node: che incubo! Hai sviluppato un’app e dei servizi back-end e vuoi sapere come gestire la CORS policy?
Mettiti comodə e leggi questo articolo.
Repository
Repository App Ionic di esempio
Repository back-end Node.js di esempio
Breve intro
A chi non è capitato di avere a che fare con lo sviluppo di un’applicazione che deve comunicare con dei servizi esterni e di vedere che tutto funziona (più o meno) perfettamente, finché non installiamo l’app sul device e… Si rompe.
Vediamo come gestire questa comunicazione nel modo più semplice possibile (per sviluppo, e non solo), in modo da portare alla luce temi come la CORS policy e la sua gestione.
L’app usata per l’esempio è estremamente semplice: permette di inserire una serie di attività come in una TODO list, il tutto grazie ad un servizio sviluppato in Node.js che comunica con un database MongoDB (se hai dubbi su come questi componenti funzionano o come crearli, controlla le risorse utili in fondo!).
CORS policy: cosa sono
Si tratta di un meccanismo di sicurezza basato sull’utilizzo del protocollo HTTP che consente a un server di specificare una qualsiasi origine (dominio, schema o porta) oltre alla propria da cui un browser dovrebbe consentire il caricamento delle risorse; in altre parole, serve a specificare chi, in termini di dominio, ha accesso ad una certa risorsa, come ad esempio un servizio.
Casi d’uso
Il funzionamento di base è molto semplice: i browser effettuano una richiesta di “verifica preliminare” rispetto al contenuto che devono accedere (aka preflight) al server che ospita la risorsa, al fine di verificare che il server ne consenta la richiesta effettiva.
Per rendere ancora più immediato il concetto, vediamo un esempio: supponendo di avere un dispositivo (browser o mobile che sia) con un indirizzo http://localhost:8100 (che definiamo origine) e un’API Node.js che risponde all’indirizzo http://localhost:8081 (che definiamo destinazione), se i CORS accettano qualunque tipo di request, abbiamo quanto segue:
Caso 1: CORS in cui vengono accettate tutte le request
Allo stesso modo, se configurassimo i servizi Node.js in modo che accettino le request solo da quelle con origine http://localhost:8100, avremmo quest’altro caso d’uso, che è abbastanza comunque quando si testa un’applicazione Ionic in locale:
Caso 2a: CORS per cui solo http://localhost:8100 è accettata come origin, quindi il browser accede correttamente
Caso 2b: CORS per cui solo http://localhost:8100 è accettata come origin, quindi da mobile non si accede ai servizi
Questo è il caso più comune: come visibile nella parte superiore a sinistra dell’immagine, dove è riportato quello snippet di codice, vediamo che nella configurazione del back-end abbiamo configurato un’origine specifica per cui accettiamo le request in ingresso.
Chiaramente, essendo un array, è possibile specificarne più di una e non solo: per come è stata pensata questa policy, è possibile fare riferimento anche ad altri campi inerenti l’header delle request, come il campo Access-Control-Allow-Credentials.
Torniamo però a noi: nei casi che abbiamo appena illustrato, la soluzione sembra semplice. In effetti, lo è se abbiamo controllo sul back-end e quindi possiamo decidere in che modo modificare la gestione di questa policy.
La stessa documentazione di Ionic suggerisce che, usando Capacitor, è possibile specificare a seconda del sistema operativo, l’origine con la quale veicolare le request.
Per quanto sembri strano, localhost è il valore di default e funzionerà anche una volta che l’applicazione sarà disponibile negli store: questo vuol dire che se lasciassimo che il servizio Node.js accetti tutte le richieste da http://localhost, questo meccanismo funzionerebbe come un orologio svizzero anche una volta che il nostro utente avrà scaricato l’app dal suo store di riferimento.
Documentazione ufficiale di Ionic sull’uso di Capacitor: https://ionicframework.com/docs/troubleshooting/cors
Best practices
Tuttavia, è sicuro lasciare localhost come origine? Assolutamente no (anche se…)
Come scritto nelle ultime due righe riportate nella figura precedente, localhost dovrebbe essere sostituito da un valore più consono relativo all’hostname che diventerà la nostra origine.
Un suggerimento può essere quello di utilizzare il dominio che fa riferimento all’applicazione: se, per esempio, questa avesse un sito di riferimento come pippo.com, l’idea dovrebbe essere quella di utilizzare questa come origine.
Per farlo, sono necessarie due modifiche: in primis, è necessario specificarlo all’interno del file capacitor.config.json in questo modo:
{
"appId": "io.ionic.starter",
"appName": "todoslist",
"bundledWebRuntime": false,
"npmClient": "npm",
"webDir": "www",
"plugins": {
"SplashScreen": {
"launchShowDuration": 0
}
},
"cordova": {},
"server": {
"hostname": "http://pippo"
}
}
Non solo: come visto in precedenza, all’interno della dichiarazione della funzione cors() nel servizio Node.js, dobbiamo aggiungere il dominio scelto tra quelli ammessi:
app.use(cors({origin: ['http://pippo']}))
Caso 3: CORS per cui solo http://pippo è accettata come origin, quindi da mobile si accede correttamente
Let’s test!
Per poter verificare che tutto funzioni correttamente sfruttando la nostra rete di casa, abbiamo bisogno di 2 cose: un dispositivo mobile e una connessione WiFi.
Prima di tutto, colleghiamo il dispositivo mobile su cui installeremo l’app di sviluppo alla stessa rete dove è collegato il nostro portatile; fatto questo, verifichiamo qual è l’indirizzo del portatile nella rete locale tramite la riga di comando, con il comando ipconfig per Windows o ifconfig per sistemi GNU/Linux e macOS:
Esempio di indirizzo IP nella rete locale
Perfetto, l’indirizzo IPv4 è 192.168.1.64; ci aspettiamo che anche il telefono abbia un indirizzo di rete che appartiene alla stessa rete, e possiamo verificarlo tramite le impostazioni, a seconda del sistema operativo. Indicativamente, se inizia con 192.168.1.x, siamo a cavallo!
A questo punto, modifichiamo il file che riporta l’indirizzo di base dei servizi che utilizziamo nella nostra app: in questo caso, è sufficiente modificare il valore della proprietà endpointUrl all’interno del file environment.ts in questo modo:
export const environment = {
production: true,
baseURL: 'http://192.168.1.66:8081/api'
};
Fatto questo, siamo pronti: colleghiamo il telefono al portatile, installiamo l’app, e verifichiamo che tutto funzioni perfettamente!
Risorse utili
- Ionic 5: Guida completa per creare app per Android e iOS
- Ionic cheatsheet
- Documentazione ufficiale delle CORS policy di Ionic
- Post di dettaglio sul funzionamento della CORS policy nel blog Ionic
- Spiegazione sul funzionamento della CORS policy
- Articolo su come funziona uno stack MEAN
- Libro di Node.js