I'm trying a basic proof of concept with firebase cloud messaging and I'm having pain receiving messages, nothing happens when I send messages even if the app is in foreground, the onMessage event doesn't return anything despite I don't have any error on sending all seem to be ok from postman or fcm console.
I noticed also that chrome://gcm-internals/ displays state "CONNECTING"
here is my app.js but for me it seems more to be related to the gcm state who should display "CONNECTED"
var firebase = require("firebase/app");
require('firebase/messaging');
// Your web app's Firebase configuration
var firebaseConfig = {
apiKey: "AIzaSyCbrs6uvQQ5s6kAp6YfvDzsds_CfMt3-hA",
authDomain: "test-flarum-fcm.firebaseapp.com",
databaseURL: "https://test-flarum-fcm.firebaseio.com",
projectId: "test-flarum-fcm",
storageBucket: "",
messagingSenderId: "977636469573",
appId: "1:977636469573:web:76e2e191f02f8923df6c2c"
};
// Initialize Firebase
var app = firebase.initializeApp(firebaseConfig);
// Retrieve Firebase Messaging object.
var messaging = firebase.messaging();
//console.log(messaging);
console.log('myToken : '+getToken());
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
console.log('Notification permission granted.');
//getMyToken();
if(isTokenSentToServer()){
console.log("token already saved")
}else{
// TODO(developer): Retrieve an Instance ID token for use with FCM.
getMyToken();
}
} else {
console.log('Unable to get permission to notify.');
setTokenSentToServer(false);
}
});
// Get Instance ID token. Initially this makes a network call, once retrieved
// subsequent calls to getToken will return from cache.
function getMyToken() {
messaging.getToken().then((currentToken) => {
if (currentToken) {
console.log(currentToken);
//sendTokenToServer(currentToken);//#todo
//updateUIForPushEnabled(currentToken);
saveToken(currentToken);
setTokenSentToServer(true);
} else {
// Show permission request.
console.log('No Instance ID token available. Request permission to generate one.');
// Show permission UI.
//updateUIForPushPermissionRequired();//#todo
//setTokenSentToServer(false); //#todo
setTokenSentToServer(false);
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
//showToken('Error retrieving Instance ID token. ', err);
setTokenSentToServer(false);
});
}
// Callback fired if Instance ID token is updated.
messaging.onTokenRefresh(() => {
messaging.getToken().then((refreshedToken) => {
console.log('Token refreshed.');
// Indicate that the new Instance ID token has not yet been sent to the
// app server.
setTokenSentToServer(false);
// Send Instance ID token to app server.
//sendTokenToServer(refreshedToken);
// ...
}).catch((err) => {
console.log('Unable to retrieve refreshed token ', err);
//showToken('Unable to retrieve refreshed token ', err);
});
});
function setTokenSentToServer(sent) {
window.localStorage.setItem('sentTokenToServer', sent ? 1 : 0);
}
function isTokenSentToServer(){
return window.localStorage.getItem('sentTokenToServer') == 1;
}
function saveToken(token){
myToken = token;
window.localStorage.setItem('myToken', token);
}
function getToken(){
return window.localStorage.getItem('myToken');
}
messaging.onMessage((payload) => {
console.log('Message received. ', payload);
// ...
});
Thanks for your help
[update] I solved the problem of gcm state but that's not wworking better despite the send result seems to be ok in postman
{
"multicast_id": 7270949329343339591,
"success": 1,
"failure": 0,
"canonical_ids": 0,
"results": [
{
"message_id": "0:1569712574121124%e609af1cf9fd7ecd"
}
]
}
Related
I have a firebase messaging service worker setup to custom handle push notifications (data messaging) in my web app that uses messaging.setBackgroundMessageHandler to handle background messages. The app works perfectly in localhost, but only for a few hours or 1-2 days on my dev server, after which it stops receiving any notifications.
The problem is that when I load the page and I call firebase messaging.getToken(...) to register it to my server, when a notification is sent on the server (java firebase admin sdk) to that token I just received, there is this error:
com.google.firebase.messaging.FirebaseMessagingException
Requested entity was not found.
Also, if the user logs out of the app, I call messaging.deleteToken(currentToken) and unregister from server. But the javascript delete token method does not work and I get the error (dev tools browser console):
DELETE firebase-messaging.js:1 DELETE https://fcmregistrations.googleapis.com/v1/projects/myproject/registrations/[MY_TOKEN] 404
firebase-messaging.js:1 Uncaught (in promise) FirebaseError: Messaging: A problem occured while unsubscribing the user from FCM: FirebaseError: Messaging: A problem occured while unsubscribing the user from FCM: Requested entity was not found. (messaging/token-unsubscribe-failed). (messaging/token-unsubscribe-failed).
at https://www.gstatic.com/firebasejs/7.17.1/firebase-messaging.js:1:24434
So I cannot even delete that token that is actually given by the Firebase SDK.
Eventually, if I just unregister my service worker and refresh the page, I get another token and everything works again.
What am I missing? (I have the latest javascript firebase sdk 7.17.1; I found nothing else regarding this issue on google)
Portions of code are as follows (some are written in GWT, but only the wrapping part; the integration with firebase is in pure javascript):
Frontpage (window client):
//initialization in web client is done by calling this method
public final void initialize(String publicKey, FirebaseCallback callback) {
usePublicVapidKey(publicKey);
String context = GWT.getHostPageBaseURL(); //current web context (host + server web context)
init_native(context , "fcm-sw-import.js", callback); // my service worker
}
init_native function contains:
var messaging = this
//$wnd is actually "window"
$wnd.navigator.serviceWorker.getRegistrations().then(function(registrations) {
for (i = 0; i < registrations.length; i++) {
if (registrations[i].active && registrations[i].active.scriptURL.includes('fcm-sw-import.js') == false) {
registrations[i].unregister();
console.log ("FCM SW unregistered other service workers: " + registrations[i], registrations[i]);
}
}
});
$wnd.navigator.serviceWorker.register(fcmServiceWorkerJSfile, {scope: '.'})
.then(function(registration) {
registration.update();
messaging.useServiceWorker(registration);
$wnd.navigator.serviceWorker.ready.then(function (registration) {
console.log('FCM SW ready');
//request permissions, get token, register to server, register my callbacks etc.
messaging.#...fcm.FirebaseMessaging::requestPermissionAndRetrieveToken (L...fcm/FirebaseCallback;)(callback);
});
//console.log("SW - registered");
});
request permissions and get token:
var messaging = this;
messaging.requestPermission().then(function() {
messaging.getToken().then(function(currentToken) {
messaging.currentFcmToken = currentToken;
if (currentToken) {
$wnd.navigator.serviceWorker.addEventListener('message', function handler(event) {
....
});
...
}
else {
// Show permission request.
...
// Show permission UI.
...
}
})['catch'](function(err) { //use [catch] so gwt wont think is a keyword
console.log(err);
... on error callback
});
// Callback fired if Instance ID token is updated.
messaging.onTokenRefresh(function() {
messaging.getToken().then(function(refreshedToken) {
var oldToken = messaging.currentFcmToken;
messaging.currentFcmToken = refreshedToken;
...on token refresh callback
})['catch'](function(err) {
});
});
messaging.onMessage(function(payload) {
...
});
})['catch'](function(err) {
...
});
Service worker is fcm-sw-import.js:
importScripts('app/fcm-sw-reg.js?v26'); //Production
setupAndInit({
apiKey: ...,
authDomain: ...,
databaseURL: ...,
messagingSenderId: ...,
projectId: ...,
storageBucket: "",
messagingSenderId: ...,
appId: ...
}, '.', "e-cache-v1");
setupAndInit method is in "fcm-sw-reg.js":
importScripts('https://www.gstatic.com/firebasejs/7.17.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.17.1/firebase-messaging.js');
... other imports, vars
function setupAndInit(config, urlContext, cacheName) {
//initialize(config, urlContext);
SENDER_ID = config.messagingSenderId;
urlContextPath = urlContext;
if (!firebase.apps.length) {
firebase.initializeApp(config);
}
const messaging = firebase.messaging();
CACHE_NAME = cacheName;
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
logToConsole('SW - Opened cache');
return cache.addAll(urlsToCache);
})
);
self.skipWaiting(); // Activate worker immediately
});
self.addEventListener('activate', function(event) {
self.clients.claim();
});
self.addEventListener('fetch', function(e) {
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
});
messaging.setBackgroundMessageHandler(function(payload) {
var wnd = self;
logToConsole('SW - FCM Received fcm background message ', payload);
if (payload.from != SENDER_ID) { //cancel if the sender does not match
return;
}
var data = payload.data;
var notifExporter = new evc.NotificationDetailsExporter(data);
var notifDetails = notifExporter.getNotificationDetails();
var notificationTitle;
var notificationOptions;
if (notifDetails != null) {
var notificationTitle = notifDetails.title;
var iconPath = data.frImLink ? data.frImLink : '';
var text = data.string13 ? data.string13 : "";
var value = data.value ? " - " + data.value : "";
notificationOptions = {
body: notifDetails.message,
icon: iconPath,
data: data
};
}
logToConsole('SW - sending message to wclients...');
clients.matchAll().then(clients => {
clients.forEach(client => {
send_message_to_client(client, {ebasFCM:"true", data: payload.data});
})
})
if (notificationTitle)
return self.registration.showNotification(notificationTitle,
notificationOptions);
else
return null;
});
logToConsole("SW FCM - Initialized FCM worker: " + SENDER_ID + " at: " + urlContextPath);
self.addEventListener('notificationclick', function(event) {
event.waitUntil(clients.matchAll({
includeUncontrolled: true,
type: "window"
}).then(function(clientList) {
logToConsole("SW FCM - Searching client list: ", clientList);
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
logToConsole("SW FCM - Searching client urls: ", client.url);
}
logToConsole("SW FCM - url context path: ", urlContextPath);
logToConsole("SW FCM - notif click event: ", event);
var notifExporter = new evc.NotificationDetailsExporter(event.notification.data);
var toUrl = notifExporter.getNotificationContextUrl();
if (clientList.length) {
if (toUrl)
send_message_to_client(clientList[0], {type: "openUrl", url: toUrl});
return clientList[0].focus();
}
else if (clients.openWindow) {
if (toUrl == null)
toUrl = "";
return clients.openWindow( urlContextPath + toUrl);
}
}));
event.notification.close();
});
logToConsole('SW FCM - Setup fcm done');
}
Please advise. Thank you.
I'm trying to create a collection called "users" but there seems to be a problem with the firebase reference , I'm using an already existing repo where I added my addUserToDb() function, in the function above called saveMessagingDeviceToken() there seems to be no problem with the firebase.
<code>
// Saves the messaging device token to the datastore.
function saveMessagingDeviceToken() {
firebase.messaging().getToken().then(function(currentToken) {
if (currentToken) {
console.log('Got FCM device token:', currentToken);
// Saving the Device Token to the datastore.
firebase.firestore().collection('fcmTokens').doc(currentToken)
.set({uid: firebase.auth().currentUser.uid});
} else {
// Need to request permissions to show notifications.
requestNotificationsPermissions();
}
}).catch(function(error){
console.error('Unable to get messaging token.', error);
});
}
// Add the user to DB
function addUserToDb(profilePicUrl, userName, userID){
var db = this.firebase.firestore();
db.collection("users").doc(userID).set({
name: userName,
profilePic: profilePicUrl,
uid: userID
})
.then(function() {
console.log("Document successfully written!");
})
.catch(function(error) {
console.error("Error writing document: ", error);
});
}
</code>
The way to add is taken from https://firebase.google.com/docs/firestore/manage-data/add-data which is the documentation.
the full error :
TypeError: Cannot read property 'firebase' of undefined
at addUserToDb (main.js:115)
at Object.authStateObserver [as next] (main.js:199)
at subscribe.ts:104
at subscribe.ts:233
When i try to subscribe to a topic i get the following error:
.subscribeToTopic is not a function
const messaging = firebase.messaging();
messaging
.requestPermission()
.then(() => {
return messaging.getToken();
})
.then(token => {
messaging
.subscribeToTopic(token, 'allUsers')
.then(response=> {
console.log(JSON.stringify(response));
})
.catch(function(error) {
console.log('Error subscribing to topic:', error);
});
})
.catch(err => {
console.log('Unable to get permission to notify.', err);
});
If I remove that line of .subscribeToTopic and add a POST call via http it works using the following url:
https://iid.googleapis.com/iid/v1/TOKEN/rel/topics/TOPIC_NAME
I took a look to this question and the docs
Cloud Messaging in Cloud Functions: admin.messagin(...).send is not a function
https://firebase.google.com/docs/cloud-messaging/js/topic-messaging
ah i solved it by handling on backend side ( nodeJS ) where the documentation is easy to handle topic.
so in this case we have alr generate token on frontend side then in backend (nodeJS) we tried to subscribe to topic by the token.
so in frontend end when we stream or firebase.messaging().onMessage(payload => { would like to trigger and show the message by topic.
FYI : https://github.com/firebase/firebase-js-sdk/issues/5289#issuecomment-899542765
so from the link we know that
Notification.vue
// these from frontend side ( for example vueJS )
import firebase from 'firebase/app'
import 'firebase/messaging'
// firebase only for get token, onMessaging, request permission check, there is no function to subscribe topic by the token, so we handle on backend side my alternative
then in server.js
// these from backend side ( for examle nodeJS )
const { admin } = require('./firebase-config');
// admin.messaging().sendToTopic()
// admin.messaging().subscribeToTopic()
// admin.messaging().sendToDevice()
if you are looking for the firebase-config.js here is
/*
* Initialize firebase
*/
var admin = require("firebase-admin");
var serviceAccount = require("./firebase.json"); // you can get the .json file on firebase service account .
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://project-xxxxxxx.firebaseio.com"
});
module.exports.admin = admin
my implementation :
app.get('/firebase/notification', (req, res)=>{
const registrationToken = req.body.registrationToken;
admin.messaging().subscribeToTopic(registrationToken, 'myTopic')
.then(response => {
console.log('Successfully subscribed to topic:', response)
const options = notification_options;
const message_notification = {
notification: {
title: 'Yogi Arif Widodo',
body: '2 10 pm',
url: 'https://localhost:8080',
other: 'other data',
}
};
admin.messaging().sendToTopic('myTopic', message_notification, options).then( response => {
so when i tested on firebase console send by topic myTopic my Notification.vue trigger these code
firebase.messaging().onMessage(payload => {
.....console.log
}
You need to use the method send not sendToTopic:
// The topic name can be optionally prefixed with "/topics/".
var topic = 'highScores';
var message = {
data: {
score: '850',
time: '2:45'
},
topic: topic
};
// Send a message to devices subscribed to the provided topic.
admin.messaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
send() was released and replaced sendtotopic/sendtodevice in version FCM v1
https://firebase.googleblog.com/2018/02/firebase-cloud-messaging-v1-now.html
https://firebase.google.com/docs/cloud-messaging/js/topic-messaging
So I'm using VueJS with the PWA option enabled. When using Firebase Cloud Messaging (for web app), I'm getting a notification saying: This site has been updated in the background but Only when the tab is not focused or open. If the tab is active, I don't receive anything in the console.
I'm using Postman to send notifications for now, and it returns success
{
"multicast_id": 5364665067527599460,
"success": 1,
"failure": 0,
"canonical_ids": 0,
"results": [
{
"message_id": "0:1550148053476716%2fd9afcdf9fd7ecd"
}
]
}
Here is my main.js
const config = {
apiKey: "API_KEY",
authDomain: "AUTH_DOMAIN",
databaseURL: "DATABASE_URL",
projectId: "PROJECT_ID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "SENDER_ID"
};
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging.usePublicVapidKey("PUBLIC_VAPID_KEY");
navigator.serviceWorker.register('./firebase-messaging-sw.js')
.then((registration) => {
console.log("Now using registration: " + registration);
messaging.useServiceWorker(registration);
}).catch(err => {
console.log("Error in registration");
console.log(err)
});
My firebase-messaging-sw.js (I never saw the console log thats inside setBackgroundMessageHandler)
importScripts('https://www.gstatic.com/firebasejs/5.8.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/5.8.2/firebase-messaging.js');
var config = {
messagingSenderId: 'SENDER_ID'
};
firebase.initializeApp(config);
let messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
return self.registration.showNotification('title', {body: 'message'});
});
And in my App.vue (neither have I ever seen the console log in onMessage)
created() {
this.$messaging.onMessage(function(payload) {
console.log('Message received: ', payload);
// ...
});
},
methods: {
sendNotif() {
this.$messaging.requestPermission().then(() => this.$messaging.getToken())
.then((token) => {
console.log(token) // Receiver Token to use in the notification
})
.catch(function(err) {
console.log("Unable to get permission to notify.", err);
});
},
},
I can access the token, none of my configuration is wrong or else the postman request wouldn't give success. I've checked out several other questions related to this but no success so far...
I'm building a Chrome Extension that needs to be able to receive push notifications. An alternative would be polling the server every X seconds, but that doesn't seem like much of a good choice. For this reason I chose Firebase. The custom JWT token auth seems to be done and working. The next step is to fetch the registration token, and sending this to the server.
The problem:
The token seems to never be fetched:
var config = {
apiKey: "<KEY>",
authDomain: "<DOMAIN>",
messagingSenderId: "<SENDER_ID>"
};
var app = firebase.initializeApp(config);
var messaging = firebase.messaging(app);
function initApp() {
firebase.auth().onAuthStateChanged(function(user) {
if(user) {
// Update UI
fetchToken();
} else {
// no user is signed in
}
});
}
function fetchToken() {
messaging.requestPermission()
.then(function() {
messaging.getToken()
.then(function(currentToken) {
if (currentToken) {
sendRegistrationTokenToServer(currentToken);
} else {
console.log('No token available')
}
})
.catch(function(err) {
console.debug('An error occurred while retrieving token. ', err);
});
}).catch(function(err) {
console.log('Notification permissions denied.', err);
});
firebase.messaging().onTokenRefresh(function() {
messaging.getToken()
.then(function(refreshedToken) {
sendRegistrationTokenToServer(refreshedToken);
console.log('Token refreshed')
})
.catch(function(err) {
console.log('Unable to retrieve refreshed token', err);
});
});
}
$(document).ready(function() {
initApp();
// some other stuff
});
Does anyone know why the token isn't being fetched?
No network activity can be seen in the developer console (for fetching the token). Also no error is printed. I do, however, see network activity for getAccountInfo?key=<somekey> which carries an idToken with it, though I'm not certain this is a related request (response doesn't seem to return a token).
The problem is you cannot call messaging.requestPermission() from the background script. If the user has not allowed notifications, this call will attempt to prompt the user, which will fail silently. What you can do is request the notifications permission somewhere else in your extension that has a UI (e.g. your options page) with:
Notification.requestPermission().then((permission) => { ...
Once the user has allowed the permission you can use the Permissions API, to query if you have the permission, in place of the call to messaging.requestPermission(). This is a function I wrote to do this:
/**
* Determine if navigator Notifications permission has been granted
* Note: this will work from the background script of an extension,
* unlike the other solutions
* #see https://developers.google.com/web/updates/2015/04/permissions-api-for-the-web
* #see https://w3c.github.io/permissions/
* #see https://www.chromestatus.com/features/6443143280984064
* #returns {Promise<boolean>} true if granted
* #memberOf app.Notify
*/
hasNavigatorPermission: function() {
return navigator.permissions.query({
name: 'notifications',
}).then((status) => {
if (status.state === 'granted') {
return Promise.resolve(true);
}
return Promise.resolve(false);
});
},
And use it like this:
/**
* Get the registration token for fcm
* #returns {Promise<token>} A registration token for fcm
* #memberOf app.Fb
*/
getRegToken: function() {
return app.Notify.hasNavigatorPermission().then((granted) => {
if (!granted) {
return Promise.reject(new Error(ERROR_NOTIFICATIONS));
}
return _messaging.getToken();
}).then((token) => {
if (token) {
return Promise.resolve(token);
}
return Promise.reject(new Error(ERROR_TOKEN));
});
},
A couple other things to note:
Don't rely on Notification.permission in an extension. It does not have the correct value.
As of Chrome 59, if you dismiss the notifications dialog too many times while developing, it will stop displaying it for a period of time. You need to clear your browser data to get it to start displaying again.