Iscrizione di un utente

Matt Gaunt

Il primo passaggio consiste nel chiedere all'utente l'autorizzazione per inviargli messaggi push e, in seguito, possiamo mettere a disposizione un PushSubscription.

L'API JavaScript per farlo è ragionevolmente semplice, quindi analizziamo il flusso della logica.

Rilevamento delle funzionalità

Innanzitutto dobbiamo verificare se il browser corrente supporta effettivamente i messaggi push. Possiamo verificare se il push è supportato con due semplici controlli.

  1. Cerca serviceWorker su navigator.
  2. Cerca PushManager sulla finestra.
if (!('serviceWorker' in navigator)) {
  // Service Worker isn't supported on this browser, disable or hide UI.
  return;
}

if (!('PushManager' in window)) {
  // Push isn't supported on this browser, disable or hide UI.
  return;
}

Sebbene il supporto dei browser sia in rapida crescita sia per i service worker che per la messaggistica push, è sempre una buona idea attivare il rilevamento delle funzionalità per entrambe le funzionalità e migliorare progressivamente.

Registra un service worker

Grazie alla funzionalità di rilevamento, sappiamo che sia i service worker che la modalità Push sono supportati. Il passaggio successivo consiste nella "registrazione" del nostro service worker.

Quando registriamo un service worker, diciamo al browser dove si trova il nostro file dei service worker. Il file è solo JavaScript, ma il browser "gli darà accesso" alle API dei Service worker, incluso il push. Per essere più precisi, il browser esegue il file in un ambiente service worker.

Per registrare un service worker, chiama navigator.serviceWorker.register(), passando il percorso al nostro file. In questo modo:

function registerServiceWorker() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      console.log('Service worker successfully registered.');
      return registration;
    })
    .catch(function (err) {
      console.error('Unable to register service worker.', err);
    });
}

Questa funzione indica al browser che è disponibile un file del service worker e dove si trova. In questo caso, il file del service worker si trova all'indirizzo /service-worker.js. Dietro le quinte, il browser eseguirà i seguenti passaggi dopo la chiamata a register():

  1. Scarica il file del service worker.

  2. Esegui JavaScript.

  3. Se tutto funziona correttamente e non sono presenti errori, la promessa restituita da register() verrà risolta. In caso di errori di qualsiasi tipo, la promessa verrà rifiutata.

Se register() rifiuta, verifica che non ci siano errori di battitura / errori in Chrome DevTools.

Quando register() si risolve, restituisce ServiceWorkerRegistration. Utilizzeremo questa registrazione per accedere all'API PushManager.

Compatibilità del browser con l'API PushManager

Supporto dei browser

  • 42
  • 17
  • 44
  • 16

Fonte

Richiesta di autorizzazione in corso...

Abbiamo registrato il nostro service worker e possiamo iscrivere l'utente. Il passaggio successivo consiste nel ottenere l'autorizzazione dall'utente per l'invio di messaggi push.

L'API per ottenere l'autorizzazione è relativamente semplice, ma lo svantaggio è che l'API è recentemente cambiata da un callback alla restituzione di una Promise. Il problema è che non riusciamo a capire quale versione dell'API è implementata dal browser attuale, perciò devi implementarle entrambe.

function askPermission() {
  return new Promise(function (resolve, reject) {
    const permissionResult = Notification.requestPermission(function (result) {
      resolve(result);
    });

    if (permissionResult) {
      permissionResult.then(resolve, reject);
    }
  }).then(function (permissionResult) {
    if (permissionResult !== 'granted') {
      throw new Error("We weren't granted permission.");
    }
  });
}

Lo snippet di codice importante nel codice riportato sopra è la chiamata a Notification.requestPermission(). Questo metodo mostrerà una richiesta all'utente:

Richiesta di autorizzazione su Chrome per desktop e dispositivi mobili.

Dopo che l'utente ha interagito con la richiesta di autorizzazione premendo Consenti, Blocca o semplicemente chiudendola, verrà fornito il risultato sotto forma di stringa: 'granted', 'default' o 'denied'.

Nel codice di esempio riportato sopra, la promessa restituita da askPermission() si risolve se viene concessa l'autorizzazione, altrimenti viene visualizzato un errore che rifiuta la promessa.

Un caso limite che devi gestire è se l'utente fa clic sul pulsante "Blocca". Se ciò accade, la tua app web non potrà più chiedere l'autorizzazione all'utente. Dovrai "sbloccare " manualmente l'app modificando lo stato di autorizzazione, che è nascosto nel riquadro delle impostazioni. Rifletti attentamente su come e quando chiedi l'autorizzazione all'utente, poiché se fa clic su "Blocca", non è facile annullare la decisione.

La buona notizia è che la maggior parte degli utenti è lieta di fornire l'autorizzazione a condizione che sachi il motivo della richiesta.

Più avanti vedremo come alcuni siti popolari chiedono l'autorizzazione.

Iscrivere un utente con PushManager

Una volta registrato il service worker e ricevuto l'autorizzazione, possiamo iscrivere un utente chiamando registration.pushManager.subscribe().

function subscribeUserToPush() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      const subscribeOptions = {
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
          'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
        ),
      };

      return registration.pushManager.subscribe(subscribeOptions);
    })
    .then(function (pushSubscription) {
      console.log(
        'Received PushSubscription: ',
        JSON.stringify(pushSubscription),
      );
      return pushSubscription;
    });
}

Quando chiamiamo il metodo subscribe(), passiamo un oggetto options, che è composto da parametri obbligatori e facoltativi.

Vediamo tutte le opzioni che possiamo passare.

Opzioni userVisibileOnly

Quando il push è stato aggiunto per la prima volta ai browser, non era chiaro se gli sviluppatori fossero in grado di inviare un messaggio push e non mostrare una notifica. Viene comunemente chiamato push silenziosa, perché l'utente non sa che qualcosa è avvenuto in background.

Il problema era che gli sviluppatori potessero eseguire azioni dannose come monitorare la posizione di un utente su base continuativa senza che l'utente lo sapesse.

Per evitare questo scenario e per dare agli autori delle specifiche il tempo di valutare il modo migliore per supportare questa funzionalità, è stata aggiunta l'opzione userVisibleOnly, mentre il valore true è un accordo simbolico con il browser secondo cui l'app web mostrerà una notifica ogni volta che viene ricevuto un push (ovvero senza push silenzioso).

Al momento devi trasmettere un valore di true. Se non includi la chiave o la tessera userVisibleOnly in false, riceverai il seguente errore:

Al momento Chrome supporta l'API Push solo per gli abbonamenti che comportano messaggi visibili all'utente. Puoi indicarlo chiamando pushManager.subscribe({userVisibleOnly: true}). Per maggiori dettagli, visita la pagina https://goo.gl/yqv4Q4.

Al momento il push silenzioso non verrà mai implementato in Chrome. Gli autori delle specifiche stanno invece esplorando la nozione di API budget che consentirà alle app web di un certo numero di messaggi push silenziosi in base all'utilizzo di un'app web.

Opzione applicationServerKey

Nella sezione precedente abbiamo accennato brevemente alle "chiavi dei server delle applicazioni". Le "chiavi del server di applicazioni" sono utilizzate da un servizio push per identificare l'applicazione che sottoscrive un utente e garantire che la stessa applicazione invii messaggi a quell'utente.

Le chiavi server delle applicazioni sono coppie di chiavi pubbliche e private univoche per l'applicazione. La chiave privata deve essere mantenuta segreta per l'applicazione e la chiave pubblica può essere condivisa liberamente.

L'opzione applicationServerKey passata alla chiamata subscribe() è la chiave pubblica dell'applicazione. Il browser trasmette questo messaggio a un servizio push al momento dell'iscrizione dell'utente, il che significa che il servizio push può collegare la chiave pubblica dell'applicazione al PushSubscription dell'utente.

Il diagramma seguente illustra questi passaggi.

  1. La tua app web viene caricata in un browser e chiami subscribe(), passando la chiave pubblica del server dell'applicazione.
  2. Il browser invia quindi una richiesta di rete a un servizio push che genererà un endpoint, lo associa alla chiave pubblica dell'applicazione e lo restituirà al browser.
  3. Il browser aggiungerà questo endpoint a PushSubscription, che viene restituito tramite la promessa subscribe().

Illustrazione della chiave pubblica del server dell'applicazione utilizzata nel metodo
di sottoscrizione.

Se in un secondo momento vorrai inviare un messaggio push, dovrai creare un'intestazione Authorization contenente informazioni firmate con la chiave privata del server delle applicazioni. Quando il servizio push riceve una richiesta di invio di un messaggio push, può convalidare questa intestazione di autorizzazione firmata cercando la chiave pubblica collegata all'endpoint che riceve la richiesta. Se la firma è valida, il servizio push sa che deve provenire dal server delle applicazioni con la chiave privata corrispondente. È fondamentalmente una misura di sicurezza che impedisce a chiunque altro di inviare messaggi agli utenti di un'applicazione.

Come viene utilizzata la chiave privata del server
dell'applicazione quando si invia un messaggio

Tecnicamente, il campo applicationServerKey è facoltativo. Tuttavia, lo richiede per l'implementazione più semplice su Chrome, mentre altri browser potrebbero richiederlo in futuro. È facoltativo su Firefox.

La specifica che definisce la chiave del server delle applicazioni è la specifica VAPID. Ogni volta che leggi qualcosa che fa riferimento a "chiavi server applicazioni" o "chiavi VAPID", ricorda che si tratta della stessa cosa.

Creare chiavi server delle applicazioni

Puoi creare un set pubblico e privato di chiavi server delle applicazioni visitando la pagina web-push-codelab.glitch.me oppure utilizzando la riga di comando web-push per generare chiavi seguendo questi passaggi:

    $ npm install -g web-push
    $ web-push generate-vapid-keys

È necessario creare queste chiavi una sola volta per l'applicazione, ma assicurati di mantenere privata la chiave privata. (Sì, l'ho appena detto.)

Autorizzazioni esubscribe()

Esiste un effetto collaterale della chiamata a subscribe(). Se la tua app web non dispone delle autorizzazioni necessarie per mostrare le notifiche al momento della chiamata a subscribe(), il browser richiederà automaticamente le autorizzazioni. Questo è utile se la tua UI funziona con questo flusso, ma se vuoi avere un maggiore controllo (e penso che la maggior parte degli sviluppatori lo farà), attieniti all'API Notification.requestPermission() che abbiamo usato in precedenza.

Che cos'è PushSubscription?

Chiamiamo subscribe(), passiamo alcune opzioni e in cambio otteniamo una promessa che si risolve in un PushSubscription che genera codice come questo:

function subscribeUserToPush() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      const subscribeOptions = {
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
          'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
        ),
      };

      return registration.pushManager.subscribe(subscribeOptions);
    })
    .then(function (pushSubscription) {
      console.log(
        'Received PushSubscription: ',
        JSON.stringify(pushSubscription),
      );
      return pushSubscription;
    });
}

L'oggetto PushSubscription contiene tutte le informazioni necessarie per inviare un messaggio push a quell'utente. Se stampi i contenuti utilizzando JSON.stringify(), vedrai quanto segue:

    {
      "endpoint": "https://some.pushservice.com/something-unique",
      "keys": {
        "p256dh":
    "BIPUL12DLfytvTajnryr2PRdAgXS3HGKiLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WAkAPIxr4gK0_dQds4yiI=",
        "auth":"FPssNDTKnInHVndSTdbKFw=="
      }
    }

endpoint è l'URL dei servizi push. Per attivare un messaggio push, invia una richiesta POST a questo URL.

L'oggetto keys contiene i valori utilizzati per criptare i dati dei messaggi inviati con un messaggio push (di cui parleremo più avanti in questa sezione).

Invia un abbonamento al tuo server

Una volta ottenuta l'iscrizione push, dovrai inviarla al tuo server. Spetta a te farlo, ma un piccolo suggerimento è quello di utilizzare JSON.stringify() per ottenere tutti i dati necessari dall'oggetto abbonamento. In alternativa, puoi unire lo stesso risultato manualmente in questo modo:

const subscriptionObject = {
  endpoint: pushSubscription.endpoint,
  keys: {
    p256dh: pushSubscription.getKeys('p256dh'),
    auth: pushSubscription.getKeys('auth'),
  },
};

// The above is the same output as:

const subscriptionObjectToo = JSON.stringify(pushSubscription);

L'invio dell'iscrizione avviene nella pagina web in questo modo:

function sendSubscriptionToBackEnd(subscription) {
  return fetch('/api/save-subscription/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(subscription),
  })
    .then(function (response) {
      if (!response.ok) {
        throw new Error('Bad status code from server.');
      }

      return response.json();
    })
    .then(function (responseData) {
      if (!(responseData.data && responseData.data.success)) {
        throw new Error('Bad response from server.');
      }
    });
}

Il server nodo riceve questa richiesta e salva i dati in un database per utilizzarli in seguito.

app.post('/api/save-subscription/', function (req, res) {
  if (!isValidSaveRequest(req, res)) {
    return;
  }

  return saveSubscriptionToDatabase(req.body)
    .then(function (subscriptionId) {
      res.setHeader('Content-Type', 'application/json');
      res.send(JSON.stringify({data: {success: true}}));
    })
    .catch(function (err) {
      res.status(500);
      res.setHeader('Content-Type', 'application/json');
      res.send(
        JSON.stringify({
          error: {
            id: 'unable-to-save-subscription',
            message:
              'The subscription was received but we were unable to save it to our database.',
          },
        }),
      );
    });
});

Grazie ai dettagli PushSubscription sul nostro server, possiamo inviare un messaggio all'utente in qualsiasi momento.

Domande frequenti

Alcune domande comuni poste a questo punto:

Posso cambiare il servizio push utilizzato da un browser?

No. Il servizio push viene selezionato dal browser e, come abbiamo visto con la chiamata subscribe(), il browser eseguirà richieste di rete al servizio push per recuperare i dettagli che costituiscono PushSubscription.

Ogni browser utilizza un servizio push diverso. Non esistono API diverse?

Tutti i servizi push prevedono la stessa API.

Questa API comune è denominata Web Push Protocol e descrive la richiesta di rete che l'applicazione deve effettuare per attivare un messaggio push.

Se mi iscrivo a un utente da un computer, l'abbonamento viene effettuato anche sul telefono?

Purtroppo no. Un utente deve registrarsi per il push su ogni browser su cui vuole ricevere messaggi. Vale anche la pena notare che questa operazione richiede che l'utente conceda l'autorizzazione su ogni dispositivo.

Passaggi successivi

Codelab