Cosa fa ogni cosa (glossario semplice)
Ogni termine che incontri ha un ruolo preciso. Ecco cosa significa, in parole povere.
Dominio (domain)È l’indirizzo del sito (es. willypush.it). Il dominio è gestito interamente lato server: viene ricavato automaticamente dall’appId nel database. Il client non invia mai un parametro domain. Ogni appId corrisponde a un dominio specifico; il server lo risolve in autonomia.
Token FCM (push_token)È un “codice di recapito” che Google assegna al tuo browser quando attivi le notifiche. Senza questo codice il server non saprebbe dove inviare la push. È come un indirizzo di casa per le notifiche.
Chiave pubblica e chiave privataSono una coppia: la pubblica la invii al server (serve per cifrare i messaggi che ti manderanno). La privata resta solo sul tuo dispositivo e serve per decifrare. Chi ha solo la chiave pubblica non può leggere i messaggi; solo chi ha la chiave privata può.
Registrazione (register)È il momento in cui il tuo browser dice al server: “Sono questo dispositivo, questo è il mio token FCM e questa è la mia chiave pubblica; da ora puoi inviarmi notifiche cifrate”. Il server salva solo ciò che serve per inviare (token + chiave pubblica), mai la chiave privata.
Invio cifrato (send_encrypted)È l’operazione con cui qualcuno (tu dal pannello, o un’applicazione tramite API) manda una notifica. Il messaggio viene cifrato con la chiave pubblica di ogni destinatario; il server e Google vedono solo dati illeggibili. Solo il dispositivo con la chiave privata può decifrare e mostrare titolo e testo.
ServerÈ il computer che ospita le API (register, send_encrypted, ecc.). Non decifra mai i messaggi: riceve dati già cifrati o titolo/corpo solo per cifrarli subito e inoltrarli. In nessun punto il server “legge” il contenuto della notifica.
Google (FCM)È il servizio che consegna materialmente la notifica al dispositivo. Riceve dal server solo dati cifrati (enc, ephem) e li recapita. Anche Google non può leggere il contenuto.
Service Worker (sw-cifrato.php)È un piccolo programma che gira in background nel browser. Quando arriva una push cifrata, la riceve, la decifra con la chiave privata (che è salvata in modo sicuro nel browser) e mostra la notifica all’utente. Senza di lui il browser non saprebbe come decifrare.
crypto-client.jsÈ lo script che genera la coppia di chiavi, salva la chiave privata in modo sicuro e fornisce le funzioni per cifrare/decifrare. Lo carichi nella pagina dove l’utente clicca “Attiva notifiche cifrate”.
X-Willy-App-Id + X-Willy-App-KeySono i due header di autenticazione. X-Willy-App-Id identifica la tua applicazione (account/tenant); X-Willy-App-Key è la chiave segreta associata. Il server verifica la coppia appId + appKey e ricava automaticamente il dominio dal database. Le operazioni sensibili (invio, lista dispositivi cifrati, rimozione dispositivo) richiedono entrambi gli header; se la coppia non è valida la richiesta viene rifiutata (403).
Per le richieste web, il server valida anche l'header Origin inviato automaticamente dal browser — questo impedisce l'uso delle credenziali da un sito diverso da quello registrato.
A cosa serve quello che scriviamo
Quando integri le push cifrate, inserisci script e chiamate. Ecco a cosa serve ogni pezzo.
- Caricare Firebase (firebase-app, firebase-messaging) – Serve per parlare con Google: ottenere il token FCM e ricevere le push. Senza Firebase il browser non può ricevere notifiche push.
- Caricare crypto-client.js – Serve per generare le chiavi (pubblica e privata) e per decifrare. Senza questo script non puoi fare “notifiche cifrate”, solo notifiche normali.
- Chiamare get-firebase-config.php – Il server ti restituisce i parametri (apiKey, projectId, vapidKey, …) necessari per inizializzare Firebase nel browser. Ogni installazione ha la sua config.
- Registrare il Service Worker (sw-cifrato.php) – Così il browser sa che c’è uno script in background che gestisce le push cifrate: quando arriva un messaggio, lo decifra e mostra la notifica.
- Ottenere il token FCM – Senza token il server non sa dove inviare. Una volta ottenuto, lo invii insieme alla chiave pubblica nella registrazione.
- Generare la coppia di chiavi (WillyPushCrypto.generateKeyPair) – Crea la chiave pubblica (da inviare al server) e la chiave privata (che resta nel browser). La privata non viene mai inviata.
- Chiamare register.php con push_token, public_key, firebase_project_id (autenticato via header
X-Willy-App-Id + X-Willy-App-Key) – Dice al server: “Questo dispositivo (token) vuole ricevere push cifrate; usa questa chiave pubblica per cifrare i messaggi”. Il server ricava il dominio dall’appId e salva questi dati.
- Inviare con send_encrypted.php (titolo e corpo, oppure encrypted_messages) – Da pannello puoi cifrare nel browser e inviare solo dati cifrati; oppure tramite API invii titolo e corpo e il server cifra per ogni dispositivo. In entrambi i casi il server non “vede” il messaggio in chiaro quando è in modalità E2EE pura (encrypted_messages).
- Rimuovere un dispositivo (delete_encrypted_device.php con token_hash) – Serve per togliere un dispositivo dalla lista: da quel momento non riceverà più push cifrate fino a una nuova registrazione.
Esempi di integrazione (tutti i linguaggi)
Sostituisci BASE con l’URL della tua installazione (es. https://TUO_SITO.it/api-push), IL_TUO_APP_ID e LA_TUA_APP_KEY con le credenziali della tua app. Per le push cifrate, la registrazione deve includere public_key e firebase_project_id.
Dominio gestito lato server: il client non invia mai un parametro domain. Il server ricava il dominio dall’appId nel proprio database. L’autenticazione avviene tramite gli header X-Willy-App-Id e X-Willy-App-Key.
1. Registrazione device con chiave pubblica (E2EE)
A cosa serve: dire al server che questo dispositivo vuole ricevere notifiche cifrate e fornire la chiave pubblica con cui cifrare i messaggi.
cURL
curl -X POST "BASE/api/register.php" \
-H "Content-Type: application/json" \
-H "X-Willy-App-Id: IL_TUO_APP_ID" \
-H "X-Willy-App-Key: LA_TUA_APP_KEY" \
-d '{"push_token":"FCM_TOKEN","platform":"web","device_type":"web_browser","public_key":"BASE64URL_CHIAVE_PUBBLICA","firebase_project_id":"tuo-project-id"}'
JavaScript (fetch)
await fetch(BASE + '/api/register.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Willy-App-Id': appId,
'X-Willy-App-Key': appKey
},
body: JSON.stringify({
push_token: fcmToken,
platform: 'web',
device_type: 'web_browser',
public_key: publicKeyBase64,
firebase_project_id: window.firebase?.app()?.options?.projectId || ''
})
});
PHP
$payload = [
'push_token' => $fcmToken,
'platform' => 'web',
'device_type' => 'web_browser',
'public_key' => $publicKeyBase64,
'firebase_project_id' => $projectId
];
$ch = curl_init($base . '/api/register.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Willy-App-Id: ' . $appId,
'X-Willy-App-Key: ' . $appKey
],
CURLOPT_RETURNTRANSFER => true
]);
$res = curl_exec($ch);
Python
import requests
r = requests.post(BASE + '/api/register.php',
json={
'push_token': fcm_token,
'platform': 'web',
'device_type': 'web_browser',
'public_key': public_key_base64,
'firebase_project_id': project_id
},
headers={'X-Willy-App-Id': app_id, 'X-Willy-App-Key': app_key})
data = r.json()
C# (HttpClient)
var payload = new {
push_token = fcmToken,
platform = "web",
device_type = "web_browser",
public_key = publicKeyBase64,
firebase_project_id = projectId
};
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Willy-App-Id", appId);
client.DefaultRequestHeaders.Add("X-Willy-App-Key", appKey);
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(baseUrl + "/api/register.php", content);
2. Invio notifica cifrata (send_encrypted)
A cosa serve: inviare una notifica il cui titolo e corpo sono cifrati; solo i dispositivi con la chiave privata possono leggerla.
cURL
curl -X POST "BASE/api/send_encrypted.php" \
-H "Content-Type: application/json" \
-H "X-Willy-App-Id: IL_TUO_APP_ID" \
-H "X-Willy-App-Key: LA_TUA_APP_KEY" \
-d '{"title":"Titolo","body":"Solo il destinatario può leggere.","url":"/","target":{}}'
JavaScript
const res = await fetch(BASE + '/api/send_encrypted.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Willy-App-Id': appId,
'X-Willy-App-Key': appKey
},
body: JSON.stringify({
title: 'Titolo',
body: 'Solo il destinatario può leggere.',
url: '/',
target: {}
})
});
const data = await res.json();
// data.data.sent, data.data.failed, data.data.total
PHP
$body = [
'title' => 'Titolo',
'body' => 'Solo il destinatario può leggere.',
'url' => '/',
'target' => []
];
$ch = curl_init($base . '/api/send_encrypted.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($body),
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-Willy-App-Id: ' . $appId, 'X-Willy-App-Key: ' . $appKey],
CURLOPT_RETURNTRANSFER => true
]);
$res = curl_exec($ch);
Python
r = requests.post(BASE + '/api/send_encrypted.php',
json={
'title': 'Titolo',
'body': 'Solo il destinatario può leggere.',
'url': '/',
'target': {}
},
headers={'X-Willy-App-Id': app_id, 'X-Willy-App-Key': app_key})
print(r.json()['data']['sent'], r.json()['data']['failed'])
Go
payload := map[string]interface{}{
"title": "Titolo",
"body": "Solo il destinatario può leggere.",
"url": "/",
"target": map[string]interface{}{},
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", base+"/api/send_encrypted.php", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Willy-App-Id", appId)
req.Header.Set("X-Willy-App-Key", appKey)
resp, _ := http.DefaultClient.Do(req)
3. Lista dispositivi con chiave (list_encrypted)
A cosa serve: sapere quali dispositivi sono iscritti alle push cifrate (per inviare loro messaggi o mostrarli in un pannello).
cURL
curl -s -H "X-Willy-App-Id: IL_TUO_APP_ID" -H "X-Willy-App-Key: LA_TUA_APP_KEY" "BASE/api/list_encrypted.php"
JavaScript
const res = await fetch(BASE + '/api/list_encrypted.php', {
headers: { 'X-Willy-App-Id': appId, 'X-Willy-App-Key': appKey }
});
const data = await res.json();
// data.data.devices → array con push_token, public_key, token_hash
PHP
$ch = curl_init($base . '/api/list_encrypted.php');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-Willy-App-Id: ' . $appId, 'X-Willy-App-Key: ' . $appKey],
CURLOPT_RETURNTRANSFER => true
]);
$data = json_decode(curl_exec($ch), true);
Python
r = requests.get(BASE + '/api/list_encrypted.php',
headers={'X-Willy-App-Id': app_id, 'X-Willy-App-Key': app_key})
devices = r.json()['data']['devices']
4. Rimuovi dispositivo (delete_encrypted_device)
A cosa serve: togliere un dispositivo dalla lista delle push cifrate (es. se l’utente si disiscrive). Serve il token_hash (lo trovi in list_encrypted).
cURL
curl -X POST "BASE/api/delete_encrypted_device.php" \
-H "Content-Type: application/json" \
-H "X-Willy-App-Id: IL_TUO_APP_ID" \
-H "X-Willy-App-Key: LA_TUA_APP_KEY" \
-d '{"token_hash":"abc123def456"}'
JavaScript
await fetch(BASE + '/api/delete_encrypted_device.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Willy-App-Id': appId,
'X-Willy-App-Key': appKey
},
body: JSON.stringify({ token_hash: tokenHash })
});
PHP
$ch = curl_init($base . '/api/delete_encrypted_device.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['token_hash' => $tokenHash]),
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-Willy-App-Id: ' . $appId, 'X-Willy-App-Key: ' . $appKey],
CURLOPT_RETURNTRANSFER => true
]);
curl_exec($ch);
Python
requests.post(BASE + '/api/delete_encrypted_device.php',
json={'token_hash': token_hash},
headers={'X-Willy-App-Id': app_id, 'X-Willy-App-Key': app_key})