Send push notifications using Firebase and OneSignal - firebase

Not sure if this is possible, but I have an existing Ionic 3 app which uses Firebase as a backend. Maybe it's just me, I'm not able to integrate Google login, Facebook login and push notifications in the same app. Been trying for a few days now.
I was able to install OneSignal and send push notifications to an Android device, but I want to send them programatically using tokens which are saved for each device, not from the OneSignal dashboard.
This is what I use in Firebase Cloud Functions to send notifications. Can it be modified to send the notification to OneSignal and then to each device?
`function sendFcm(userID, eventSnapshot, eventID) {
const getDeviceTokensPromise = admin.database().ref(`/fcmTokens/${userID}/`).once('value');
return Promise.all([getDeviceTokensPromise]).then(result => {
const tokensSnapshot = result[0];
const payload = {
"notification": {
"title": "Your invitation has arrived",
"body": eventSnapshot.name,
"sound": "default",
// "click_action": "FCM_PLUGIN_ACTIVITY",
"icon": "fcm_push_icon"
},
"data": {
"eventId": eventID,
"uid": userID,
"eventObj": JSON.stringify(eventSnapshot),
"notificationType": "newEventNotification"
}
};
const tokens = Object.keys(tokensSnapshot.val());
console.log(tokens);
// Send notifications to all tokens.
return admin.messaging().sendToDevice(tokens, payload).then(response => {
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
console.log(tokens[index]);
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens which are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
})
}`

After searching a bit, I found the OneSignal API. Seems that I just need to save the player id and send it or mutiple in an array to onesignal.com/api/v1/notifications. More details here: https://documentation.onesignal.com/reference#section-send-based-on-onesignal-playerids-create-notification

Related

Firebase Cloud Messaging click_action not working

I am sending notification from Firebase console to web application (Firebase). When I am sending a POST request from POSTMAN, I am able to navigate to the URL when I click on the notification. But when I am sending it from Firebase console I am not able to open the URL. Also, I need to add my logo as my Icon to the notification.
POSTMAN
{
"notification": {
"title": "Push notification test",
"body": "It works! 🎉",
"icon": "https://soft-ing.info/img/firebase.png",
"click_action": "https://google.com"
},
"to": "dLXCbmVCh5Y:APA91bGmFN7BUsKqwWFokyoBsoph6k4EhBQEflwJLFrPaUzTceYhAPYfFf8LqTRBVJGCA0gWS_0k0DUCeJBa7jdopIyjFQNprtp3lkQgLmUNRUibLIIMxAuBZeXuHTqaU-BA4QwbekN6"
}
Service Worker File Code
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
const notificationTitle = payload.data.title;//'Background Message Title';
const notificationOptions = {
body: payload.data.body,//'Background Message body.',
icon: payload.data.icon,
image : payload.data.image,
data:{
time: new Date(Date.now()).toString(),
click_action : payload.data.click_action
}
};
return self.registration.showNotification(notificationTitle,notificationOptions);
});
self.addEventListener("notificationclick", (event) => {
event.waitUntil(async function () {
const allClients = await clients.matchAll({
includeUncontrolled: true
});
let chatClient;
let appUrl = 'https://www.google.com';
for (const client of allClients) {
//here appUrl is the application url, we are checking it application tab is open
if(client['url'].indexOf(appUrl) >= 0)
{
client.focus();
chatClient = client;
break;
}
}
if (!chatClient) {
chatClient = await clients.openWindow(appUrl);
}
}());
});
There's some discrepancy in the above two snippets you shared.
In your case body: payload.data.body should be body: payload.notification.body, you need to do similarly for other places in service worker since that's how you are sending the request.

Push notifications with firebase

I'm getting this error when i test with token which I got from firebase
Error -The request's Authentication (Server-) Key contained an invalid or malformed FCM-Token (a.k.a. IID-Token)
these are the codes that i used to get token from firebase.
async getToken() {
let fcmToken = await AsyncStorage.getItem('fcmToken');
console.log("before fcmToken: ", fcmToken);
if (!fcmToken) {
fcmToken = await firebase.messaging().getToken();
if (fcmToken) {
console.log("after fcmToken: ", fcmToken);
await AsyncStorage.setItem('fcmToken', fcmToken);
}
}
}
async requestPermission() {
firebase.messaging().requestPermission()
.then(() => {
this.getToken();
})
.catch(error => {
console.log('permission rejected');
});
}
async checkPermission() {
firebase.messaging().hasPermission()
.then(enabled => {
if (enabled) {
console.log("Permission granted");
this.getToken();
} else {
console.log("Request Permission");
this.requestPermission();
}
});
}`
But I got this Error 401 when I test with APNS & GCM Tester Online
https://pushtry.com/
Please may I have any methods to get push notification for react native android application?
You can use postman. Make a post request to this url "https://fcm.googleapis.com/fcm/send".
you need to send an object like this:
{
"data":
{
"title": "your title",
"body": "your body"
},
"notification": {
"title": "your title",
"body": "your body"
},
"registration_ids" : ["czf_nv_t1JA:APA91bGOyY3lTSG9b7Nr71xVo_Xn5RrxOIOwVfnKDv2OBanQjx1eQQyqdM3PFOd1Pjapm_DWn1R327iDyEwEeXjflJ3DyaGFF4iXmqot-OsyDt-Iz99Lu3MZZTvOSFIPiYgiaGHP5ByO"]
}
and registration_ids is your token.Also you need to set a header for your request with key Authorization and it's value comes from firebase console.for finding that you can check this https://developer.clevertap.com/docs/find-your-fcm-sender-id-fcm-server-api-key
Make sure in Authorization value is as per your firebase cloud messaging server key.
Reference link:- https://www.djamware.com/post/5c6ccd1f80aca754f7a9d1ec/push-notification-using-ionic-4-and-firebase-cloud-messaging

Remove users who uninstalled app from database [duplicate]

We have developed an app in iOS and Android which stores the FCM tokens in a database in order to send PUSH notifications depending on the user configuration.
Users install the app and the token of every device is stored in the database, so we would like to know what of those tokens are invalid because the app has been uninstalled.
On the other hand, we send the notifications through the website using JSON. Is there any limitation (I mean, is there any limit of elements in a JSON request)?
Thank you very much!
I recently noticed that step 9 in the Cloud Functions codelab uses the response it gets from the FCM API to remove invalid registration tokens from its database.
The relevant code from there:
// Get the list of device tokens.
return admin.database().ref('fcmTokens').once('value').then(allTokens => {
if (allTokens.val()) {
// Listing all tokens.
const tokens = Object.keys(allTokens.val());
// Send notifications to all tokens.
return admin.messaging().sendToDevice(tokens, payload).then(response => {
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(allTokens.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
}
});
I quickly checked and this same approach is also used in the Cloud Functions sample for sending notifications.

How to implement Firebase phone authentication with Ionic 4?

Is it possible to use phone authentication with Firebase and Ionic 4 in mobile apps?
I have seen some old tutorials implementing phone authorization with Ionic 3, but these seem to be outdated.
The firebaseui-web project does not support phone authentication for cordova apps, but I am unsure if that implies that Firebase phone authentication is impossible with ionic apps.
If you cannot use Firebase's phone authentication with Ionic 4, is there an alternative phone authentication service that does work with Ionic 4?
Yes. You can do it with Firebase's Javascript SDK, it will need the user to pass a CAPTCHA and then send the phone number a verification code which you can login and auth with, the process is explained here:
https://firebase.google.com/docs/auth/web/phone-auth#send-a-verification-code-to-the-users-phone
The problem is that the firebase auth sms service will only send messages when the app is in production mode (uploaded to the store). But to be able to test the methods from test mode, it is adding a test number in the white list of firebase.
In my case, I try these:
sms-verification.page.ts
sendSmsVerification(phoneNumber): Promise <firebase.auth.UserCredential> {
return new Promise((resolve, reject) => {
firebase.auth().useDeviceLanguage();
var verificationId;
var code;
const timeOutDuration = 60;
const tell = '+54' + phoneNumber;
this.FireBase.verifyPhoneNumber(tell, timeOutDuration).then(async (credential) => {
// alert(credential.instantVerification);
if (credential.verificationId) {
console.log("Android credential: ", credential);
verificationId = credential.verificationId;
} else {
console.log("iOS credential: ", credential);
verificationId = credential;
}
if (credential.instantVerification) {
code = credential.code;
this.verifySms(verificationId, code)
.then( resp => {
resolve(resp);
})
.catch( err => {
reject(err);
});
} else {
let prompt = await this.alertCtrl.create({
backdropDismiss: false,
header: 'Ingrese el codigo de confirmación del SMS.',
inputs: [{ name: 'confirmationCode', placeholder: 'Código de confirmación' }],
buttons: [
{ text: 'Cancelar',
handler: data => {
console.log('Cancel clicked');
resolve(data);
}
},
{ text: 'Verificar',
handler: data => {
code = data.confirmationCode;
this.verifySms(verificationId,code)
.then( resp => {
resolve(resp);
})
.catch( err => {
reject(err);
}); }
}
]
});
prompt.present();
}
}).catch(error => {
console.log('Error! Catch SMSVerificacion', error);
reject(error);
});
})
}
verifySms(verificationId, code): Promise <any> {
console.log('parametros de verifySms ', verificationId +' ', code);
const signInCredential = firebase.auth.PhoneAuthProvider.credential(verificationId,code);
return firebase.auth().signInAndRetrieveDataWithCredential(signInCredential);
}
Yes, it's possible to use firebase phone authentication using Cordova plugin,
cordova-plugin-firebase-authentication
Add this plugin to your ionic 4 project
cordova plugin add cordova-plugin-firebase-authentication --save
With this we can verify phone without using reCaptcha.
Note that this only work on real android device, not emulator or browser.
Function implementation
verifyPhoneNumber(phoneNumber, timeout)
cordova.plugins.firebase.auth.verifyPhoneNumber("+123456789", 30000)
.then(function(verificationId) {
// pass verificationId to signInWithVerificationId
});
or
AngularFire (With reCaptcha)
https://github.com/angular/angularfire
First, install angularfire lib into your project
npm install firebase #angular/fire --save
then import this lib into your class
import * as firebase from 'firebase/app';
code example:
firebase.auth().signInWithPhoneNumber(phoneNumber,recaptchaVerifier)
.then(confirmationResult => {
this.windowRef.confirmationResult = confirmationResult;
})

How do I properly grab the users profile from Realtime Database to get their username before the cloud function returns?

I am implementing Cloud Functions to send my users notifications for when interesting things happen like following, liking, commenting. I have copied & adapted the Firebase tutorial for sending a notification when a change at the followers node is detected, but I need to also query the database to get the follower's account data including their username. I think I am close, but the function doesn't finish in time and I'm having trouble understanding promises. Here is the function:
exports.sendFollowerNotification = functions.database.ref(`/userFollowers/{followedUid}/{followerUid}`)
.onWrite((change, context) => {
const followerUid = context.params.followerUid;
const followedUid = context.params.followedUid;
// If un-follow we exit the function
if (!change.after.val()) {
return console.log('User ', followerUid, 'un-followed user', followedUid);
}
console.log('We have a new follower UID:', followerUid, 'for user:', followedUid);
// Get the list of device notification tokens.
const getDeviceTokensPromise = admin.database()
.ref(`/users/${followedUid}/notificationTokens`).once('value');
console.log('Found the followed user\'s token')
const userInfo = admin.database().ref(`/users/${followedUid}`).once('value');
console.log(userInfo)
const username = userInfo['username'];
console.log(username);
////////////////// ABOVE is where I'm trying to get the username by reading their account data ///////////////////
// Get the follower profile.
const getFollowerProfilePromise = admin.auth().getUser(followerUid);
// The snapshot to the user's tokens.
let tokensSnapshot;
// The array containing all the user's tokens.
let tokens;
return Promise.all([getDeviceTokensPromise, getFollowerProfilePromise]).then(results => {
tokensSnapshot = results[0];
const follower = results[1];
// Check if there are any device tokens.
if (!tokensSnapshot.hasChildren()) {
return console.log('There are no notification tokens to send to.');
}
console.log('There are', tokensSnapshot.numChildren(), 'tokens to send notifications to.');
console.log('Fetched follower profile', follower);
// Notification details.
const payload = {
notification: {
title: 'You have a new follower!',
body: `{username} is now following you.`,
}
};
// Listing all tokens as an array.
tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
return admin.messaging().sendToDevice(tokens, payload);
}).then((response) => {
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
});
How can I ensure that username will have been made available before it returns? Thanks.
Ok, I think I get what you are saying...
These lines of code don't do what you think. All DB reads are done asynchronous, so...
const userInfo = admin.database().ref(`/users/${followedUid}`).once('value');
console.log(userInfo)
const username = userInfo['username'];
console.log(username);
once returns a promise, so userInfo is actually a promise to return the data. You won't get the data until you do a then.
More chaining promises I'm afraid... just rename userInfo to userInfoPromise and add it to your Promise.All array.

Resources