How to get FCM token? - javascript

I am trying to get FCM token in react js application.
First thing i tried is to use messaging.useServiceWorker(registration) then use messaging.getToken() and it's working fine on localhost for firefox and google chrome, but on an HTTPS live server it works fine on firefox but in chrome it throws an error: DOMException: Failed to execute 'subscribe' on 'PushManager': Subscription failed - no active Service Worker.
I saw firebase docs and found that messaging.useServiceWorker is deprecated now and I have to use messaging.getToken({ serviceWorkerRegistration }) instead but it throws an error: FirebaseError: Messaging: We are unable to register the default service worker. Failed to register a ServiceWorker for scope ('http://localhost:3000/firebase-cloud-messaging-push-scope') with script ('http://localhost:3000/firebase-messaging-sw.js'): The script has an unsupported MIME type ('text/html'). (messaging/failed-service-worker-registration).
Notes
firebase-messaging-sw.js File is under the public directory.
firebase-messaging-sw.js File is empty.
This how I register the service worker:
export const registerServiceWorker = () => {
if ("serviceWorker" in navigator) {
return new Promise((resolve, reject) => {
navigator.serviceWorker
.register(process.env.PUBLIC_URL + "/firebase-messaging-sw.js")
.then(function (registration) {
console.log("[registration]", registration)
// messaging.useServiceWorker(registration)
resolve(registration);
})
.catch(function (err) {
console.log("[ERROR registration]: ", err)
reject(null);
});
});
} else {
console.log("SERVICE WORKER NOT IN THE BROWSER")
}
};
What should I do to get FCM token in a write way?

I have found a solution for this issue here is my code:
class Firebase {
constructor() {
if (firebase.apps.length) return;
firebase.initializeApp(config);
this.auth = firebase.auth();
this.messaging = firebase.messaging();
navigator.serviceWorker.getRegistrations().then((registrations) => {
if (registrations.length) {
[this.registration] = registrations;
return;
}
navigator.serviceWorker
.register("/firebase-message-sw.js")
.then((registration) => {
this.registration = registration;
});
});
}
async askNotificationPermission() {
try {
const token = await this.messaging.getToken({
serviceWorkerRegistration: this.registration,
});
return token;
} catch (error) {
console.error("[FIREBASE ERROR]: ", error);
return null;
}
}
}
And I am firing askNotificationPermission function with a click action.

Related

after deploy, fcm alert function is not working... java.lang.IllegalArgumentException: Exactly one of token, topic or condition must be specified

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??

Twilio conversations push notifications not working - Empty Credentials error

I need to enable push notifications in my project. I have already integrated twilio conversations.
I followed this documentation and seems to be ok, but there is a problem with the setPushRegistrationId method.
I have this code:
import { getMessaging, getToken, onMessage } from "firebase/messaging";
import initFirebase from "../firebase/initFirebase";
export const handleNotifications = async (conversationClientInstance) => {
const app = initFirebase();
const messaging = getMessaging(app);
getToken(messaging, {
vapidKey:
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
})
.then((currentToken) => {
if (currentToken) {
console.log("currentToken", currentToken);
//Here is the problem <----------
conversationClientInstance
.setPushRegistrationId("fcm", currentToken)
.then((el) => console.log("el", el));
onMessage((payload) => {
conversationClientInstance.handlePushNotification(payload);
});
} 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);
// ...
});
};
I am getting this error on the twilio dashboard:
I also replicated this situation in this github repo. It is deployed, so you can try it live.
In console I can see the FCM token, but setPushRegistrationId returns undefined (the console.log('el', el))
I think I forgot something, but I can't figure out what. What do you thik?

Swallowed message : Error: Uncaught (in promise): [object Undefined]

My login component briefly displays before being removed by an error message about an undefined object in a promise.
Here is the promise definition:
static init(): Promise<any> {
KeycloakClientService.auth.loggedIn = false;
return new Promise((resolve, reject) => {
const keycloakConfig = {
url: environment.KEYCLOAK_URL,
realm: environment.KEYCLOAK_REALM,
clientId: environment.KEYCLOAK_CLIENTID,
'ssl-required': 'external',
'public-client': true
};
const keycloakAuth: any = new Keycloak(keycloakConfig);
keycloakAuth.init({onLoad: 'check-sso'})
.success(() => {
KeycloakClientService.auth.loggedIn = true;
KeycloakClientService.auth.authz = keycloakAuth;
KeycloakClientService.auth.logoutUrl = environment.KEYCLOAK_URL
+ '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
+ document.baseURI;
console.log('=======>> The keycloak client has been initiated successfully');
resolve('Succeeded in initiating the keycloak client');
})
.error(() => {
reject('Failed to initiate the keycloak client');
});
});
}
It is called by:
KeycloakClientService.init()
.then(
() => {
console.log('The keycloak client has been initialized');
}
)
.catch(
(error) => {
console.log(error);
window.location.reload();
}
);
The console shows both messages:
The keycloak client has been initiated successfully
The keycloak client has been initialized
I'm using Angular 6.0.4 and tried following this blog
Any way to work around this error so as to keep my login form displayed ?
UPDATE: I tried using an observable instead of a promise but the issue remained the same:
public init(): Observable<any> {
KeycloakClientService.auth.loggedIn = false;
return new Observable((observer) => {
const keycloakConfig = {
'url': environment.KEYCLOAK_URL,
'realm': environment.KEYCLOAK_REALM,
'clientId': environment.KEYCLOAK_CLIENTID,
'ssl-required': 'external',
'public-client': true
};
const keycloakAuth: any = new Keycloak(keycloakConfig);
keycloakAuth.init({ 'onLoad': 'check-sso' })
.success(() => {
KeycloakClientService.auth.loggedIn = true;
KeycloakClientService.auth.authz = keycloakAuth;
KeycloakClientService.auth.logoutUrl = environment.KEYCLOAK_URL
+ '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
+ document.baseURI;
console.log('The keycloak auth has been initialized');
observer.next('Succeeded in initiating the keycloak client');
observer.complete();
})
.error(() => {
console.log('The keycloak client could not be initiated');
observer.error('Failed to initiate the keycloak client');
});
});
}
The whole source code is available on GitHub
UPDATE: Following an answer below, I also tried to use a then() and a catch() keywords but the error remained the exact same:
keycloakAuth.init({ 'onLoad': 'check-sso' })
.then(() => {
KeycloakClientService.auth.loggedIn = true;
KeycloakClientService.auth.authz = keycloakAuth;
KeycloakClientService.auth.logoutUrl = environment.KEYCLOAK_URL
+ '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
+ document.baseURI;
console.log('The keycloak auth has been initialized');
observer.next('Succeeded in initiating the keycloak client');
observer.complete();
})
.catch(() => {
console.log('The keycloak client could not be initiated');
observer.error('Failed to initiate the keycloak client');
});
This is a wild guess, but maybe it's a conflict with Angular's zones. Since this is a security library it might not like that Angular has replaced core functions with proxies. For example, NgZone modifies window.setTimeout and the HTTP methods.
So you could try running this code outside of zones. The only problem here is that you're using a static function, and will have to make this an injectable service so that you can access NgZone
#Injectable()
export class KeycloakClientService {
public constructor(private zone: NgZone) {
}
public init(): Promise<any> {
KeycloakClientService.auth.loggedIn = false;
return new Promise((resolve, reject) => {
this.zone.runOutsideAngular(() => {
const keycloakConfig = {
url: environment.KEYCLOAK_URL,
realm: environment.KEYCLOAK_REALM,
clientId: environment.KEYCLOAK_CLIENTID,
'ssl-required': 'external',
'public-client': true
};
const keycloakAuth: any = new Keycloak(keycloakConfig);
keycloakAuth.init({onLoad: 'check-sso'})
.success(() => {
KeycloakClientService.auth.loggedIn = true;
KeycloakClientService.auth.authz = keycloakAuth;
KeycloakClientService.auth.logoutUrl = environment.KEYCLOAK_URL
+ '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
+ document.baseURI;
console.log('=======>> The keycloak client has been initiated successfully');
resolve('Succeeded in initiating the keycloak client');
})
.error(() => {
reject('Failed to initiate the keycloak client');
});
});
}
}
}
The change here is to use zone.runOutsideAngular
If you remove the success block, where do you run your logic within success?
I read some of their source code, I think this is why success causes the problem:
Within keycloak.js, there is a function createNativePromise():
function createNativePromise() {
var p = {
setSuccess: function(result) {
p.success = true;
p.resolve(result);
},
setError: function(result) {
p.success = false;
p.reject(result);
}
};
p.promise = new Promise(function(resolve, reject) {
p.resolve = resolve;
p.reject = reject;
});
p.promise.success = function(callback) {
p.promise.then(callback);
return p.promise;
}
p.promise.error = function(callback) {
p.promise.catch(callback);
return p.promise;
}
return p;
}
And it's used this way(simplified code):
function refreshToken() {
var promise = createNativePromise();
...
if (refreshTokenFailed) {
promise.setError(true);
}
...
return promise.promise;
}
The problem is, promise.setError() calls promise.reject(result), so the promise is rejected, it's expecting a catch.
But within promise.success, there is a promise.promise.then(callback);, and nobody is catching this promise.
This is why you get the Uncaught (in promise): [object Undefined], and in my case, i always get Uncaught (in promise): true.
Solution:
Notice that promise.promise is a real Promise, so we can use then and catch instead of success and error.
The drawback is, the typescript type will be wrong.
We have observed a similar error about the promise object undefined. The situation was our local application was working fine with the local keycloak standalone server but faced this error when the local application trying to connect with a keycloak server hosted on the ABC server (ABC is used as a reference here to give any arbitrary name).
This issue was resolved when we hosted the application and the keycloak server both on the ABC server.
It looks like there are some time sync issues in different machines due to which the promise object is not returned.

registration.showNotification is not a function

I'm using serviceworker-webpack-plugin to create a service worker in my reactjs apps.
I've followed the example to register the service worker in the main thread. I've learnt that Html5 Notification doesn't work on Android chrome, so I used registration.showNotification('Title', { body: 'Body.'}); instead of new Notification('...') to push notifications. But when I tested it on the desktop chrome, it throws this error
registration.showNotification is not a function
Is the registration.showNotification only available on Android chrome but not on the desktop?
public componentDidMount(){
if ('serviceWorker' in navigator &&
(window.location.protocol === 'https:' || window.location.hostname === 'localhost')
) {
const registration = runtime.register();
registerEvents(registration, {
onInstalled: () => {
registration.showNotification('Title', { body: 'Body.'});
}
})
} else {
console.log('serviceWorker not available')
}
}
runtime.register() returns a JavaScript Promise, which is why you are getting a not a function error because Promises don't have a showNotification() method.
Instead, you'd have to chain a .then() callback to it in order to get the actual registration object (or use async/await, which is also cool).
runtime.register().then(registration => {
registration.showNotification(...);
})
Below solution worked for me. Can try.
The main root cause of .showNotification() not firing is service worker. Service worker is not getting registered. So it wont call registration.showNotification() method.
Add service-worker.js file to your project root directory
You can download service-worker.js file from Link
Use below code to register service worker and fire registration.showNotification() method.
const messaging = firebase.messaging();
messaging.onMessage(function (payload) {
console.log("Message received. ", payload);
NotisElem.innerHTML = NotisElem.innerHTML + JSON.stringify(payload);
//foreground notifications
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('./service-worker.js', { scope: './' })
.then(function (registration) {
console.log("Service Worker Registered");
setTimeout(() => {
registration.showNotification(payload.data.title, {
body: payload.data.body,
data: payload.data.link
});
registration.update();
}, 100);
})
.catch(function (err) {
console.log("Service Worker Failed to Register", err);
})
}
});

authWithOAuthPopup Error on Web Platform

I am trying to use Firebase's Facebook authentication on a webpage, but I get this error:
Login failed Error: Invalid authentication credentials provided.
at Error (native)
at http://45.55.203.213/js/firebase.js:160:367
at e (http://45.55.203.213/js/firebase.js:140:400)
Here's my Javascript code:
var ref = new Firebase("https://fantasy-smash-bros.firebaseio.com/");
function FBLogin() {
ref.authWithOAuthPopup("facebook", function(error, authData) {
if (error) {
console.log("Login failed", error);
} else {
loginWithAuthData(authData);
}
});
}
function loginWithAuthData(authData) {
username = authData.facebook.displayName;
avatarURL = authData.facebook.cachedUserProfile.picture.data.url;
userID = authData.facebook.id;
}
function attemptLogin() {
var user = ref.getAuth();
if (user) {
loginWithAuthData(user);
} else {
$("#fb-login").click(function () {
FBLogin();
});
}
}
$(document).ready(function () {
attemptLogin();
});
Note: I have imported firebase.js. Here's a copy of the Javascript I used.
I believe I have setup everything correctly: adding Firebase's callback URL to my Facebook app's whitelist, enabling Facebook authentication on the Firebase app. But I am still stumped on what to do. Any help would be greatly appreciated.

Categories

Resources