I'm trying to ensure that I'll be able to get an fcm device token from firebase whenever I request one via messaging.getToken(). Though I'm having an issue retrieving the token constantly on Google Chrome, it works intermittently.
When I test getting an FCM device token on page refresh on Firefox, it works every single time without fail and I receive a token in my callback function.
On the other hand with Google Chrome, its a completely different story, I only manage to receive a token intermittently. My code stops running at the point where I print in the console "Notification permission granted". No error messages from the catch block.
Upon further investigation, I found that the function messaging.getToken() does not return a token i.e. it was undefined, again, this only happens when I use Google Chrome.
I also tried doing this in a Brave browser, the behavior is similar to that of Google Chrome, except with Google Chrome, when I paste the following code into the console:
if(token){
console.log("value of token is:", token)
}
else
{
console.log("value of token is:", token);
}
});
it actually prints the token, whereas Brave doesn't.
Then of course IE and Safari don't support Firebase messaging
Code
firebase-init.js:
var firebaseConfig = {
apiKey: "api-key-here",
authDomain: "domain-here",
databaseURL: "data-base-url-here",
projectId: "project-id",
storageBucket: "storage-bucket",
messagingSenderId: "sender-id-here",
appId: "app-id-here"
};
console.log(firebase);
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();
// Request for permission
Notification.requestPermission()
.then((permission) => {
console.log('Notification permission granted.');
console.log(permission);
//code stops running here on google chrome
messaging.getToken()
.then((currentToken) => {
if (currentToken) {
console.log('Token: ' + currentToken);
sendTokenToServer(currentToken);
var data = { newToken: currentToken };
var url = "/Account/UpdateFirebaseToken";
$.ajax({
url: url,
type: "POST",
data: JSON.stringify(data),
dataType: "text",
processData: false,
contentType: "application/json; charset=utf-8",
success: function (data, status, jqXHR) {
console.log("successfully retrieved token:", data, status, jqXHR);
},
error: function (jqXHR, status, err) {
console.log(err);
},
complete: function (jqXHR, status) {
console.log("request complete");
}
});
} else {
//doesn't reach here
console.log('No Instance ID token available. Request permission to generate one.');
setTokenSentToServer(false);
}
})
.catch(function (err) {
//doesn't reach here either
console.log('An error occurred while retrieving token. ', err);
setTokenSentToServer(false);
});
})
.catch(function (err) {
console.log('Unable to get permission to notify.', err);
});
//});
firebase-messaging-sw.js:
importScripts('https://www.gstatic.com/firebasejs/6.2.3/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/6.2.3/firebase-messaging.js');
var config = {
apiKey: "api-key-here",
authDomain: "auth-domain-here",
messagingSenderId: "sender-id",
};
firebase.initializeApp(config);
var messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function (payload) {
var dataFromServer = JSON.parse(payload.data.notification);
var notificationTitle = dataFromServer.title;
var notificationOptions = {
body: dataFromServer.body,
icon: dataFromServer.icon,
data: {
url: dataFromServer.url
}
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
self.addEventListener("notificationclick", function (event) {
var urlToRedirect = event.notification.data.url;
event.notification.close();
event.waitUntil(self.clients.openWindow(urlToRedirect));
});
Removing the following script tag:
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill"></script>
from my _Layout.cshtml resolved the issue.
I'm not sure how that script is interfering with the promises, but I'd really appreciate it if someone could explain it to me.
It seems to be related with IndexedDB (that's where it hangs when debugging
the firebase code).
I had the exact same issue you did although I was injecting promise-polyfill through a webpack build. Your post allowed me to fix something that was making me mad for several weeks.
I resolved my particular issue by only applying the polyfill if window.Promise was not present so that my code (and the firebase SDK) would use the native Promise.
Related
I made alert service using FCM, it works fine in my local server. but after I deployed my server in ec2, trying alert function on ec2 app gives me this error :
[ient-SecureIO-1] o.a.t.websocket.pojo.PojoEndpointBase : No error handling configured for [springboot.utils.WebsocketClientEndpoint] and the following error occurred
java.lang.IllegalArgumentException: Exactly one of token, topic or condition must be specified
Reading the error message, i guess that there's no token in my server.
In notification.js, I'm trying to get token and request POST to '/register'
const firebaseModule = (function () {
async function init() {
// Your web app's Firebase configuration
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/firebase-messaging-sw.js')
.then(registration => {
var firebaseConfig = {
configuration Information
};
// Initialize Firebase
console.log("firebase Initialization");
firebase.initializeApp(firebaseConfig);
// Show Notification Dialog
const messaging = firebase.messaging();
messaging.requestPermission()
.then(function() {
console.log("Permission granted to get token");
return messaging.getToken();
})
.then(async function(token) {
console.log("Token: ", token);
await fetch('/register', { method: 'post', body: token })
messaging.onMessage(payload => {
const title = payload.notification.title
const options = {
body : payload.notification.body
}
navigator.serviceWorker.ready.then(registration => {
registration.showNotification(title, options);
})
})
})
.catch(function(err) {
console.log("Error Occured : " +err );
})
})
})
}
}
And by debugging it by console.log(), I found out that code is stopped before "if ('serviceWorker' in navigator) {"
So I need to make it proceed. But to be honest, it's been a while since i made this function, and I don't know almost anything about Javascript. I don't even know what navigator means(I googled it, but couldn't find clear answer) I'm having trouble figuring out what is wrong and how can I fix it. Can someone help me??
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 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"
}
]
}
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 getting a 404 file not found error when using the google drive v3 API. The file-Id I'm getting from google picker so I know the id is correct. The offending code is as following (javascript):
downloadFile: function () {
var _this = this;
gapi.client.init({
apiKey: this.developerKey,
clientId: this.clientId,
discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'],
scope: 'https://www.googleapis.com/auth/drive'
}).then(function () {
// not working with private files
gapi.client.setToken(_this.oauthToken);
gapi.client.drive.files.export({
'fileId': _this.selectedFileId,
'mimeType': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
}).then(function (response) {
console.log('success!');
});
}, function (error) {
console.log(error);
});
}, function (error) {
console.log(error);
});
}
Funnily enough it doesn't happen with all files only private files. So I assume the file not found error is just a generic response back from google indicating I wasn't allowed to access the file.
Oddly enough doing a files.get works fine:
gapi.client.drive.files.get({
fileId: _this.selectedFileId,
supportsTeamDrives: true
}).then(function (response) {
console.log('worked');
}, function (error) {
console.log('failed');
});
I saw this same error when I used the https://www.googleapis.com/auth/drive.file permission: to get export to work you need to have at least the https://www.googleapis.com/auth/drive.readonly permission granted to the OAuth token you are using.