Preguntas push messaging para pwa.

ricm

Se incorporó
28 Agosto 2005
Mensajes
7.590
Estaba probando una pwa y me intereso el tema del push messaging.
He seguido parte de la guia y llegue a un punto donde logre suscribir a un cliente, y me manda la siguiente informacion.

JSON:
{
    "subscription": {
        "endpoint": "https://fcm.googleapis.com/fcm/send/cAb65LUDH_A:APA91bGp_aaaaaaaaaaaULia8wHb_bsqiwTgtBPGpT4Pv-WrxEexa_zQ584fXceO-nkAJHQAOqBDvkwkA0ZFWMrHBYQYk5aE5GKfrTJCd0oERs6VG7QVnhzhqScILrdsT3Eyh1",
        "expirationTime": null,
        "keys": {
            "p256dh": "BIw4AvjdePBKRVJlmbbL8pKrP9c6RrUMRL2JqdrqddQtluZHK7_MmaaaaaaaaaaahGAkuVRuatwq3EnY3InE",
            "auth": "X7Vo3_AdaaaaaaBcag"
        }
    }
}

En teoria, con esto ya podria enviarle una notificacion usando postman por ejemplo.

Las preguntas que tengo:
Necesito guardar todos esos datos?
necesito usar firebase obligado?
como envio una notificacion usando postman? todo lo que veo en internet son tutoriales que usan firebase.
 

biomorgoth

Miembro Regular
Se incorporó
28 Febrero 2021
Mensajes
58
Si, debes guardar toda esa información del navegador para enviar una notificación, y preferiblemente asociarla a un usuario u otra entidad más adecuada en una base de datos.

Sobre Firebase, no necesariamente debes usarlo, de hecho hasta donde recuerdo de las veces que he implementado Web Push Notifications, cada navegador podría usar una plataforma distinta pero compatible con los estandares Web Push y VAPID. Desconozco el caso de las aplicaciones de escritorio que se apoyen en PWA.

Acerca de enviar una notificación usando Postman, sospecho que va a ser bastante complicado debido a que el contenido a enviar debe ser encriptado en base a la especificación de Web Push. Es mucho más sencillo (y sabio) reusar una librería de Web Push para armar y enviar la notificación. Podrías armar una pequeña app en el lenguaje de tu preferencia que reciba las llaves VAPID que previamente creaste y la data de la suscripción del navegador (la que citaste en el mensaje anterior), además del mensaje a enviar.

De todas formas acá te dejo un enlace a la librería web-push para Node.js. La menciono porque a pesar de que podrías no tener experiencia en Node.js, igual tiene un modo de uso mediante CLI.

https://www.npmjs.com/package/web-push
 
Upvote 0

ricm

Se incorporó
28 Agosto 2005
Mensajes
7.590
Si, debes guardar toda esa información del navegador para enviar una notificación, y preferiblemente asociarla a un usuario u otra entidad más adecuada en una base de datos.

Sobre Firebase, no necesariamente debes usarlo, de hecho hasta donde recuerdo de las veces que he implementado Web Push Notifications, cada navegador podría usar una plataforma distinta pero compatible con los estandares Web Push y VAPID. Desconozco el caso de las aplicaciones de escritorio que se apoyen en PWA.

Acerca de enviar una notificación usando Postman, sospecho que va a ser bastante complicado debido a que el contenido a enviar debe ser encriptado en base a la especificación de Web Push. Es mucho más sencillo (y sabio) reusar una librería de Web Push para armar y enviar la notificación. Podrías armar una pequeña app en el lenguaje de tu preferencia que reciba las llaves VAPID que previamente creaste y la data de la suscripción del navegador (la que citaste en el mensaje anterior), además del mensaje a enviar.

De todas formas acá te dejo un enlace a la librería web-push para Node.js. La menciono porque a pesar de que podrías no tener experiencia en Node.js, igual tiene un modo de uso mediante CLI.

https://www.npmjs.com/package/web-push
Muchas gracias por la info. Buen dato de la libreria!
 
Upvote 0

ricm

Se incorporó
28 Agosto 2005
Mensajes
7.590
@biomorgoth
Consulta corta, las llaves puedo usar las mismas que genere en firebase o tengo que usar nuevas con un proyecto propio?
 
Upvote 0

biomorgoth

Miembro Regular
Se incorporó
28 Febrero 2021
Mensajes
58
@biomorgoth
Consulta corta, las llaves puedo usar las mismas que genere en firebase o tengo que usar nuevas con un proyecto propio?
Si son llaves VAPID, entonces si deberías poder usarlas fuera de Firebase. Generalmente son una llave privada y otra pública. Podrías usar un par de llaves nuevas generadas por una librería, pero eso implica que cualquier suscripción que tengas con la llave anterior será inaccesible con la llave nueva.
 
Upvote 0

ricm

Se incorporó
28 Agosto 2005
Mensajes
7.590
Si son llaves VAPID, entonces si deberías poder usarlas fuera de Firebase. Generalmente son una llave privada y otra pública. Podrías usar un par de llaves nuevas generadas por una librería, pero eso implica que cualquier suscripción que tengas con la llave anterior será inaccesible con la llave nueva.
Genere llaves vapid usando https://vapidkeys.com/ y las copie tal cual como texto.
suscribi un nuevo usuario, y capture sus datos.

luego use la version python de la libreria que me recomendaste, con el siguiente codigo de ejemplo.
Python:
from pywebpush import webpush, WebPushException

try:
    webpush(
        subscription_info={
            "endpoint": "https://updates.push.services.mozilla.com/wpush/v2/gAAAAABiundvgG07iLusK76B2jK56Do3tTenmfgf_tRQ2PaxiVwY4D0rKSKohZg1_RylYtyzdqX2d5GcEMNyUez0ktOKnM0yIouJwd-Aqnc7SRzu73EjAZwMx438ZVHgCcfDHXaP7cxIV-15SvuNJMf4Z8VSKqK1PZjzFO9zTsrhMu6Q-PljxOM",
            "keys": {
                "p256dh": "BArn6iktod6sQa-KDMEkEuNYK0WTh1m6jj4oIH47L_cZ8zJIx0A9XA2ttjWK9PuBELDRgNJSMXhCh0SQqHoBdfw",
                "auth": "jr4CkAxls5eOYpeSDdsN5w"
            }},
        data="Mary had a little lamb, with a nice mint jelly",
        vapid_private_key="tn5ErpxnEAot-2ZM57Z3xmhEG92jxSk5Bxxxxxxxxxx",
        vapid_claims={
                "sub": "mailto:[email protected]",
            }
    )
except WebPushException as ex:
    print("I'm sorry, Dave, but I can't do that: {}", repr(ex))
    # Mozilla returns additional information in the body of the response.
    if ex.response and ex.response.json():
        extra = ex.response.json()
        print("Remote service replied with a {}:{}, {}",
              extra.code,
              extra.errno,
              extra.message
              )
y recibo el siguiente error
WebPushException('Push failed: 401 Unauthorized\nResponse body:{"code": 401, "errno": 109, "error": "Unauthorized", "more_info": "http://autopush.readthedocs.io/en/latest/http.html#error-codes", "message": "Request did not validate missing authorization header: Key mismatch"}')

La verdad no se me ocurre que puede pasar para que falle la autorizacion, segun mi las llaves deberian estar bien, aunque la documentacion dice:
vapid_private_key - Either a path to a VAPID EC2 private key PEM file, or a string containing the DER representation.
No se a que se refiere con DER representation, y si mi llave esta en ese formato o no
 
Última modificación:
Upvote 0

biomorgoth

Miembro Regular
Se incorporó
28 Febrero 2021
Mensajes
58
Genere llaves vapid usando https://vapidkeys.com/ y las copie tal cual como texto.
suscribi un nuevo usuario, y capture sus datos.

luego use la version python de la libreria que me recomendaste, con el siguiente codigo de ejemplo.
[...]
y recibo el siguiente error


La verdad no se me ocurre que puede pasar para que falle la autorizacion, segun mi las llaves deberian estar bien, aunque la documentacion dice:

No se a que se refiere con DER representation, y si mi llave esta en ese formato o no
Te recomiendo que no uses sitios de terceros para generar llaves. No he implementado WebPush en Python, pero veo que igual hay librerías que facilitan el desarrollo. Para generar las llaves VAPID tienes a py-vapid, el cual también cuenta con un CLI para generar las llaves en formato PEM.
bin/vapid --gen can be used to generate a new set of public and private key PEM files. These will overwrite the contents of private_key.pem and public_key.pem.

Después puedes indicar la ruta al archivo con la llave privada como el argumento vapid_private_key del método webpush, ya que según la documentación:
vapid_private_key - Either a path to a VAPID EC2 private key PEM file, or a string containing the DER representation. (See py_vapid for more details.) The private_key may be a base64 encoded DER formatted private key, or the path to an OpenSSL exported private key file.

Finalmente, debes generar la applicationServerKey que vas a embeber en el código de tu sitio web donde implementarás WebPush (específicamente donde llamarás a PushManager.subscribe(). Para esto el mismo py-vapid te permite generar este valor a partir de las mismas llaves PEM que generaste al principio (asumiendo que no las moviste de lugar ni cambiaste de directorio en el terminal:
bin/vapid --applicationServerKey will return the applicationServerKey value you can use to make a restricted endpoint. See https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe for more details. Be aware that this value is tied to the generated public/private key. If you remove or generate a new key, any restricted URL you’ve previously generated will need to be reallocated. Please note that some User Agents may require you to decode this string into a Uint8Array.

Eso debería poder ayudar al respecto.
 
Upvote 0

biomorgoth

Miembro Regular
Se incorporó
28 Febrero 2021
Mensajes
58
Ha pasado un tiempo desde el último mensaje acá, pero finalmente tuve el tiempo para terminar esta implementación de Web Push + Notifications API apoyado por Python en el backend. Podría ser útil para futuras referencias. Probado en Windows 10 + Chrome y todo en orden.

 
Upvote 0

Amenadiel

Ille qui nos omnes servabit
Fundador
OVERLORD
REPORTERO
Se incorporó
15 Enero 2004
Mensajes
18.398
necesito usar firebase obligado?
No. Hay muchos proveedores, con distintos precios y features. Me ha tocado usar solamente Firebase y OneSignal, y diría que para elegir proveedor los factores relevantes incluyen la calidad de la documentación, la cantidad de preguntas en Stack Overflow, la existencia de librerías para el lenguaje de programación de tu app, las integraciones con otros servicios, o los servicios complementarios.

Me da la impresión que OneSignal es más fácil de implementar y su servicio es mayormente enfocado en las notificaciones. Por el contrario, Firebase es más complejo de configurar y la documentación no es muy didáctica. Sin embargo vale la pena tener en cuenta que Firebase le lleva muchos otros servicios (A/B testing, remote config, contextual links, analytics, hosting, realtime database, single sign on, APM, publicidad... ) así que dependiendo de cuánto de eso quieras usar, la dificultad inicial puede valer la pena con creces.

Voy a usar firebase para responder lo que sigue.

Necesito guardar todos esos datos?
Cuando un usuario acepta recibir ~spam~ er digo, notificaciones, se genera un notification_token, también llamado device_id y registracion_id dependiendo del proveedor, pero es lo mismo. (en tu endpoint, lo que empieza con cAb65LUDH_A:APA91bGp_aaaaaaaaaaaULia8wHb_bsqiwTgtBPGpT4Pv...) Eso lo debes enviar al backend para persistirlo en la BBDD. Mira el comentario en el ejemplo de firebase: Send the token to your server and update the UI if necessary

Código:
const messaging = getMessaging();
getToken(messaging, { vapidKey: '<YOUR_PUBLIC_VAPID_KEY_HERE>' }).then((currentToken) => {
  if (currentToken) {
    // Send the token to your server and update the UI if necessary
    // ...
  } else {
    // Show permission request UI
    console.log('No registration token available. Request permission to generate one.');
    // ...
  }
}).catch((err) => {
  console.log('An error occurred while retrieving token. ', err);
  // ...
});

En adelante, cuando quieras enviarle una notificación usarás ese token como destinatario.

Como el mismo usuario se puede meter a tu app con varios browsers, con el teléfono, el ipad y la pantalla mágica, puede tener muchos tokens. Así que se guardan en un modelo independiente, sobre el cual el usuario tiene relación 1:N. El SDK permite enviar notificaciones poniendo un array de tokens como destinatario, pero también permite crear grupos de tokens y mandar el mensaje al grupo, para ahorrar dolores de cabeza. Y de paso también puedes suscribir tokens a un tópico y mensajear a todos los suscriptores de éste. Como uno elige el slug del tópico o del grupo, es fácil usar algo que sea único por usuario (el slug de su mail por ejemplo).


como envio una notificacion usando postman? todo lo que veo en internet son tutoriales que usan firebase.
No tiene mucha ciencia hacerlo con Postman o con curl o desde cualquier app server-side que pueda hacer requests. El formato legacy es

Código:
POST https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA

{
"notification":{
    "title":"MyDot vuelve recargado",
    "body":"Hola! Haz click ahora! ¡Estamos entregando tus productos en menos de 60 minutos!"
},
  "to" : "cAb65LUDH_A:APA91bGp_aaaaaaaaaaaULia8wHb_bsqiwTgtBPGpT4Pv..."
}

el atributo to es el registration_id del destinatario, mientras que el header authorization es key=<FCM_SERVER_KEY>, llave complementaria a la que usaste para pedir autorización al inicio. No hay nada encriptado ahí.

Por otro lado: el formato legacy se llama así porque hay otro formato moderno que es el recomendado en la actualidad. Por ejemplo

Código:
POST https://fcm.googleapis.com/v1/projects/<NOMBRE_DE_PROYECTO>/messages:send HTTP/1.1

Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...PbJ_uNasm  // tu

{
  "message": {
    "token" : "cAb65LUDH_A:APA91bGp_aaaaaaaaaaaULia8wHb_bsqiwTgtBPGpT4Pv..."
    "notification": {
      "title": "FCM Message",
      "body": "This is a message from FCM"
    }
  }
}

Este payload (el json) tiene una propiedad message, y dentro de ésta va un objeto parecido al legacy, salvo porque en vez de una propiedad to usas una propiedad token. Esto tiene una buena razón de ser, que explicaré más abajo.

También tenemos la posibilidad de usar el SDK. Firebase tiene SDK para cualquier lenguaje, y está pensado para correr en el servidor. Cuando instancias el cliente de Firebase con tus credenciales él se preocupa de autorizar tus envíos, así que sólo le pasas el payload a un método. No me acuerdo si era igual que el del formato moderno o le pasas sólo el contenido de message.

Finalmente, el dashboard de firebase console tiene una interfaz web para enviar notificaciones de prueba.

Sobre el uso de token en reemplazo de to, se debe a la unificación de lo que antes eran distintos endpoints para enviar notificaciones a un token, un grupo o un topic. Ahora eso se distingue según le hayas puesto una propiedad topic, token o to. Esta última se usa sólo para grupos. Debieran haberle puesto group, digo yo.



El payload de la notificación tiene muchas otras propiedades que se usan para customizar su comportamiento. Según se reciba en un browser, android o iphone:

Código:
{
    "to": "<group_unique_id>",
    "data": {
        "notification_type": "aviso"
    },
    "notification": {
        "title": "Capa9 vuelve recargado",
        "body": "Métete al foro pos, tetón"
    },
    "android": {
        "collapse_key": "Nuevos reviews en Capa9",
        "ttl": 7600000,
        "priority": "HIGH",
        "notification": {
            "event_time": "2019-10-25T16:19:21.791Z",
            "visibility": "PUBLIC",
            "notification_priority": "PRIORITY_HIGH",
            "icon": "fcm_push_icon",
            "tag": "Capa9",
            "color": "#55A07A",
            "sound": "default"
        },
        "fcm_options": {
            "analytics_label": "usuarios_que_han_posteado"
        }
    },
    "webpush": {
        "headers": {
            "TTL": "120",
            "Urgency": "high",
            "Topic": "Capa9"
        },
        "notification": {
            "badge": "fcm_push_icon.png",
            "icon": "img/icons/android-icon-72x72.png",
            "tag": "Capa9",
            "timestamp": 1572020361791,
            "vibrate": [
                200,
                100,
                200
            ]
        },
        "fcm_options": {
            "analytics_label": "usuarios_que_han_posteado"
        }
    },
    "apns": {
        "headers": {
            "apns-priority": "10",
            "apns-collapse-id": "Nuevos reviews en Capa9"
        },
        "fcm_options": {
            "analytics_label": "usuarios_que_han_posteado"
        }
    },
    "fcm_options": {
        "analytics_label": "usuarios_que_han_posteado"
    }
}

A diferencia del caso webpush, para obtener autorización en apps nativas las llaves son un poco más seguras que un simple string, y se suben como certificado a la consola de firebase.


Acerca de enviar una notificación usando Postman, sospecho que va a ser bastante complicado debido a que el contenido a enviar debe ser encriptado en base a la especificación de Web Push.

Cuando usas un proveedor, éste se ocupará de hacer la encriptación a partir del payload que le envíes y las llaves que tengas cargadas en el proyecto. El rol del proveedor como broker de las notificaciones facilita harto la implementación y todos tienen features adicionales para que valga la pena el servicio. Onda segmentar destinatarios, o llevar analíticas, invitar colaboradores al proyecto, plugins para frameworks populares.

De todas maneras es loable que hayas aprendido a implementar el flujo por tu cuenta. El día de mañana cuando salgan APIs emparentadas a Web Push vas a cachar a la primera, mientras que yo seguro estaré puteando a Firebase por sus métodos inconsistentes entre cada servicio.
 
Última modificación:
Upvote 0
Subir