101 – CORS policy con Ionic e Node

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.

 

Esempio di errore di CORS policy con Ionic e Node
Esempio di errore di CORS policy con Ionic e Node

 

In questo caso, l’app creata è estremamente semplice: permette di inserire una serie di attività da compiere o di cancellarle, 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

Cos’è

Si tratta di un meccanismo 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

 

Si basa su un altro meccanismo anche più vecchio: 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
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 2: CORS per cui solo http://localhost:8100 è accettata come origin
Caso 2a: CORS per cui solo http://localhost:8100 è accettata come origin, quindi il browser accede correttamente

 

Caso 2: CORS per cui solo http://localhost:8100 è accettata come origin, quindi da mobile non si accede ai servizi
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
Documentazione ufficiale di Ionic sull’uso di Capacitor: https://ionicframework.com/docs/troubleshooting/cors

 

Ma siamo sicuri…?

 

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

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
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

Condividi la tua opinione