Can I import OneSignal tokens to FCM? - firebase

I have several thousand OneSignal web push notification tokens I want to import to FCM. Is there a way to do this?
I see this endpoint which requires the https://fcm.googleapis.com/fcm/send/...key... endpoint that OneSignal gives me, but I don't know what to put in for auth and p256dh.
https://developers.google.com/instance-id/reference/server#create_registration_tokens_for_apns_tokens

So yes this can be done. First you will need to contact OneSignal support and get the public and private VAPID keys for your app. Each app in your dashboard will have a different set.
Next you will need to make an API call to OneSignal in order to export the users in a CSV file.
You can find the API url in the docs and use curl or use your favorite language. I used Node + Axios to make my calls. The API call will supply you with a link to download the CSV.
Here is the documentation https://documentation.onesignal.com/reference#csv-export
You want to make sure you add the "extra_fields" parameter to your request with the "web_auth" and "web_p256" fields added. The CSV will provide you with the other piece of the puzzle which is the endpoint url in their identifier column.
Once you have all this information you can now send pushes using a library such as web-push for Node
https://github.com/web-push-libs/web-push
Hope that helps!
EDIT
As Cedric stated the actual push payload is a little bit more complicated because you need to comply with the OneSignal Service worker data handling.
You can see the formatting starting at line 313 here
If you are using a library like web-push for Node to send your push payloads your payload would be formatted something like this for a standard push to a OneSignal service worker.
const uuidv1 = require('uuid/v1')
const webpush = require('web-push')
let subscription = {
endpoint: 'USER ENDPOINT URL',
keys: {
auth: 'USER AUTH KEY',
p256dh: 'USER P256 KEY'
}
}
let vapid = { private: 'VAPID PRIVATE KEY', public: 'VAPID PUBLIC KEY' }
// Format Message for OneSignal Service Worker
let notification = JSON.stringify({
custom: {
i: uuidv1(), //Generate UUID for the OneSignal Service worker to consume
u: 'CLICK URL'
},
title: 'TOP TITLE',
alert: 'MESSAGE BODY',
icon: 'ICON IMAGE URL'
})
webpush.setVapidDetails('mailto: sendError#YourEmail.com', vapid.public, vapid.private)
webpush.sendNotification(subscription, notification)

It's much more complex than Dan's answer. If your users don't subscribe to your own service worker, it won't work. OS will send its default notification when an 'unknown' error occurs, which it will send "You have new updates" as a notification to the user even though you passed different payload. You also need to pass: "custom": { "i": uuidv1() } to your payload for it to work. (don't forget to install uuid first through npm and call it). Check out this link and you'll figure out what other payload props you need to pass.

Related

How to test otp login in cypress?

How does one E2E test OTP login?
I have set up an OTP login, I want to write a Cypress test for it where the user enters the OTP and gets it in email. How do I write a test for this, given that the OTP changes every time I send an email?
The current solutions I have are:
To create a test account and hardcode a static OTP for it on the server.
To create a mock API with static responses and use that for testing (currently I'm using the actual deployed API for testing)
If I'm understanding your requirement, you can use otplib to bypass the email reading/parsing stage and directly generate the token that would otherwise be sent to the user in an email.
The package cypress-otp is a thin wrapper around otplib, but unfortunately it's not up-to-date and is awfully noisy and hard to follow for such a simple task.
This is how I unraveled the code and updated for Cypress ver 10.10.0:
Install otplib
yarn add -D otplib or npm install otplib --save-dev
Add a task to call otplib from you test
This takes the place of reading a mail and parsing out the token, which you don't need to test because normally a user does that and enters the token into your app under test.
In cypress.config.js
const { defineConfig } = require("cypress");
const otplib = require("otplib");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on('task', {
generateToken(secret) {
return otplib.authenticator.generate(secret);
}
})
},
},
});
Example test taken from cypress-otp (simplified)
describe('Example of generating a token for use in OTP tests', () => {
let secret; // in this example, secret is taken from the app page,
// but normally you will pass it in from a fixture
// or an environment variable
beforeEach(() => {
cy.visit('https://otplib.yeojz.dev'); // we use this page as a sample app
// Get the secret
cy.contains("Demo Secret")
.parent().parent()
.find('input').invoke('val')
.should('not.eq', 'Loading...') // simpler than waitUntil()
.then(value => secret = value)
})
it('tests the token entry', () => {
cy.task('generateToken', secret)
.then(token => {
cy.contains('Verify Token').click();
cy.contains('Please input a token')
.parent()
.find('input')
.type(token);
cy.contains('The token is valid in this current window')
.should('be.visible')
})
})
})
This test is the one given in cypress-otp, a simplified version of it which is more easily understood with moderate programming skills.
It's a bit contrived, because the app provides both the secret and the token, and then also verifies it.
The essential part is the cy.task('generateToken', secret) which makes receiving the token very easy.
If your application is sending OTP via emails then I have a solution for you.
Mailhog.
You can point the SMTP to mailhog and all the outbound emails will appear in Mailhog's management console. Something like this.
After that it's a piece of cake. You can access those email inside cypress tests using this plugin.
https://www.npmjs.com/package/cypress-mailhog
It's been two months but I hope this helps.

Push Notifications in Perl

I am implementing push notifications for a site that has a Perl back end. Firebase is the push notification service i am using. I have spent a fair bit of time with this and looked at a number of guides and some useful resources on SO. I have come up with a working implementation with just one issue. The problem is when send out a push notification it seems to arrive on the client/browser as an empty message. That is no data containing the 'title' and 'body' is retrievable on the client/browser side when the push notification arrives.
I have tried using both firebases older and newer api and either way it ends up with the same outcome of empty push notifications arriving on the client/browser. I have tested this on chrome,firefox and android and the same thing happens.
Here is the perl code that sends the push notification. I have excluded generating the bearer token to limit how much code there is to read.
#SEND PUSH NOTIFICATION
my $push_subscriber = <get subscriber details from db>
my $end_point_host = $push_subscriber->{endpoint};
my $end_point = "https://$end_point_host/v1/projects/<my project
id>/messages:send";
my $request = HTTP::Request->new('POST',$end_point);
$request->header('Authorization'=>"Bearer $bearer_token");
$request->header('Content-Type' => 'application/json');
$request->content(JSON::encode_json ({
message => {
token => $push_subscriber->{subscription_id},
notification => {
title => 'test',
body => 'test content'
},
webpush => {
headers => {
Urgency => 'high'
},
notification => {
body => 'test content',
requireInteraction => 'true'
}
}
}}));
#send the request
$ua->request($request));
Here is the client/browser side javascript that is called when a push notification arrives. This is inside service-worker.js
self.addEventListener('push', function(e) {
var body;
if (e.data) {//THE PROBLEM IS HERE. No 'data' object exists
body = e.data.text();
} else {
body = "Empty Message";
}
var options = {
body: body
};
e.waitUntil(
self.registration.showNotification('My Notification', options)
);
});
The point where the problem presents itself is pointed out in the above javascript. Any help/feedback would be much appreciated. Thanks.
I ended up getting this working by just re-writing my client side subscription code. In my case the bell icon subscription on/off button along with all the js code to make it work.
Basically i went from using googles solution to a firebase specific solution with this guide.
https://firebase.google.com/docs/cloud-messaging/js/receive
You only need to store the 'token' on your server and the endpoint is always - https://fcm.googleapis.com/v1/projects/YOUR PROJECT ID/messages:send
The firebase guide contains a sample file where you can subscribe/unsubscribe for push notifications.
https://github.com/firebase/quickstart-js/blob/4be200b1c55616535159365b74bfd1fc128c1ebf/messaging/index.html
Once i had this working i could then cut it down and re-write it into just a simple notification button.
For some reason the provided firebase-messaging-sw.js from the guide didn't work for me but using service-worker.js shown in my OP did and so i can now receive push notifications along with their title, body and other data.
This here is how i generate the bearer token used in my OP sample perl code to send out a push notification.
Google API OAuth2 : “invalid_grant” error when requesting token for Service Account with Perl
That should hopefully cover everything you need to know if you are wanting to do push notifications on a site with a Perl back end. Hopefully this helps someone else wanting to do the same thing.

How can I use notification actions with Firebase Messaging Web SDK

How do I use notification actions with the Firebase Messaging SDK on the web?
There are a few common pitfalls people hit when attempting this.
Firebase Notifications - There is a feature of the Firebase Messaging SD
K's none as "Firebase Notifications". When you send a push message to a Firebase Instance-ID (IID) token, you can use a "notification" key which the SDK's will look for and if found, construct a notification for you. The benefit of this is that you have to write no code to show a notification. The downside is that it can be restrictive if you want to do anything complex or perform work on the device once the notification is received. So to use actions, you MUST NOT USE THIS. Instead call the FCM API with the IID token and a "data" payload.
Data Payload - The data payload has a restriction where it can only be key value pairs, where the value must be a string, i.e. no arrays. What this means is that you can't just send an array of actions and construct a notification with that. The way around this is to create a JSON string, send that to the FCM API and then parse and use the JSON on the device.
Time for an example.
Calling the FCM API
The format of your payload should be something like this:
{
"data": {
"some-data": "Im a string",
"some-other-data": "Im also a string",
"json-data": "{\"actions\": [{\"action\":\"yes\", \"title\":\"Yes\"},{\"action\":\"no\",\"title\":\"No\"}]}"
},
"to": "YOUR-IID-TOKEN"
}
You can send this with curl like so:
curl -X POST -H "Authorization: key=YOUR-SERVER-KEY" -H "Content-Type: application/json" -d '{
"data": {
"some-data": "Im a string",
"some-other-data": "Im also a string",
"json-data": "{\"actions\": [{\"action\":\"yes\", \"title\":\"Yes\"},{\"action\":\"no\",\"title\":\"No\"}]}"
},
"to": "YOUR-IID-TOKEN"
}' "https://fcm.googleapis.com/fcm/send"
With that you'll be able to get the data in the onBackgroundMessage callback in your service worker.
Receiving the Payload on the Device
In a service worker we could have the following code:
messaging.setBackgroundMessageHandler(function(payload) {
console.log('Message received: ', payload);
});
Which would print out the following in the console:
Notice the JSON data is still just a string, not an object.
Next up we can parse the JSON data and check its the right format to use as our notification actions.
We can change our code to the following:
messaging.setBackgroundMessageHandler(function(payload) {
console.log('Message received: ', payload);
const parsedJSON = JSON.parse(payload.data['json-data']);
console.log('Actions:', parsedJSON);
});
This will give the following log:
With this, we can finally create our notification with the following code:
messaging.setBackgroundMessageHandler(function(payload) {
console.log('Message received: ', payload);
const parsedJSON = JSON.parse(payload.data['json-data']);
console.log('Actions:', parsedJSON);
// Customize notification here
const notificationTitle = 'Actions Title';
const notificationOptions = {
body: 'Actions body.',
actions: parsedJSON.actions,
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
Now you should have a notification with actions:
Testing
As Meggin as pointed out in the comments, it's not obvious how to test it, so a few guiding principles.
The biggest pain point is that if your web server sets a cache header for you service worker file, it won't update between refreshes, one way to fix this it to open your service worker file in a new tab and refresh that page until your service worker is up to date (This is viewing the actual source code of your service worker). Then when you refresh your web page your service worker will be the latest one and you can tell it's updated by the number next to the service worker incrementing.
Alternatively, just unregister the service worker the service worker and refresh the page - this should give you the latest service worker.
To test your notification, you'll need to click a tab that is for a different web page before sending a push message.
The reason for this is that if the user is currently on one of your pages, the push message is sent to the pages onMessage() callback instead of the onBackgroundMessage() callback.
Following Matt's advice, I was able to get a proper notification with content from my firebase function passed into my service worker (including actions), but I had to pass all of my data through the one json object, otherwise it wouldn't work for me.
Here's what my firebase functions code looks like:
function sendPayload(tokenArray) {
const payload = {
"data": {
"jsondata": "{\"body\":\"Meggin needs help\", \"title\":\"Can you help her make the code work?\",\"actions\": [{\"action\":\"yes\", \"title\":\"Yes\"},{\"action\":\"no\",\"title\":\"No\"}]}"
}
};
admin.messaging().sendToDevice(tokenArray, payload)
.then(function(response) {
// See the MessagingDevicesResponse reference documentation for
// the contents of response.
console.log("Successfully sent message:", response);
})
.catch(function(error) {
console.log("Error sending message:", error);
});
}
And here's what my code looks like in my service worker:
messaging.setBackgroundMessageHandler(function(payload) {
console.log('Payload received: ', payload);
const parsedJSON = JSON.parse(payload.data.jsondata);
console.log("What does actions look like? " + parsedJSON.actions);
console.log("What does title look like? " + parsedJSON.title);
const notificationTitle = parsedJSON.title;
const parsedBody = parsedJSON.body;
const parsedActions = parsedJSON.actions;
// Customize notification here
const notificationOptions = {
body: parsedBody,
actions: parsedActions,
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
It's worth noting that one major hurdle that helped me get passed this is understanding how to test push notifications and service workers!
You actually can't see my notification unless the browser is closed, so obviously, you can't watch the console.
But then once you've pushed the notification, you go into the console, and change the file at the top of console to be the service worker file specifically.
And then you can see the console logs!
I realize this might seem obvious to many people, but it wasn't to me, and it's crucial to understanding how to parse the payload and get it to do what you want!

User based notifications with the Bluemix Push Notification service

Currently developing a Cordova app and wanted to use the IBM Bluemix Push Notification service to send user based push notifications.
According to the documentation here, seems like the first step is to call MFPPush.initialize(appGuid, clientSecret), which I tried to do. But this function is not present in the plugin interface and therefore I get an 'undefined' error when running the app.
Moreover, the doc also talks about calling MFPPush.registerDevice({},success,failure,userId). However, when I look at the plugin javascript interface, it only takes 3 parameters.
Could someone please give some advice to help me sort this out?
Thanks.
I just ran the Bluemix Cordova hellopush sample which should help you out. Make sure you follow the directions in the README, and make sure to change the route and guid in your index.js (it should look something like this):
route: "http://imfpush.ng.bluemix.net",
guid: "djkslk3j2-4974-4324-8e82-421c02ce847c",
You will be able to find the route and guid in your Push Notifications service credentials.
After running it by following the directions (and ensuring that you have GCM / APNS set up correctly for whatever platform you are using), you should be greeted with this screen after clicking register:
#johan #joe Cordova app can use the IBM Bluemix Push Notification service to send user based push notifications. Please follow the below example using BMSPush to register for Push Notifications.
// initialize BMSCore SDK
BMSClient.initialize("Your Push service region");
// initialize BMSPush SDK
var appGUID = "Your Push service appGUID";
var clientSecret = "Your Push service clientSecret";
// Initialize for normal push notifications
var options = {}
BMSPush.initialize(appGUID,clientSecret,options);
// Initialize for iOS actionable push notifications and custom deviceId
var options ={"categories":{
"Category_Name1":[
{
"IdentifierName":"IdentifierName_1",
"actionName":"actionName_1",
"IconName":"IconName_1"
},
{
"IdentifierName":"IdentifierName_2",
"actionName":"actionName_2",
"IconName":"IconName_2"
}
]},
"deviceId":"mydeviceId"
};
BMSPush.initialize(appGUID, clientSecret, options);
var success = function(response) { console.log("Success: " + response); };
var failure = function(response) { console.log("Error: " + response); };
// Register device for push notification without UserId
BMSPush.registerDevice(options, success, failure);
// Register device for push notification with UserId
var options = {"userId": "Your User Id value"};
BMSPush.registerDevice(options, success, failure);
Please go through the Bluemix Cordova Plugin Push SDK doc link.

Firebase 2.0 Send Push Notification From Device to Device - iOS [duplicate]

I am thinking about keeping all registration ids(push token) in DB and sending notifications to user from iPhone. I tried something like this but did not get any notification.
func sendPNMessage() {
FIRMessaging.messaging().sendMessage(
["body": "hey"],
to: TOKEN_ID,
withMessageID: "1",
timeToLive: 108)
}
What I am doing wrong or maybe it is impossible at all?
Currently it's not possible to send messages from the application itself.
You can send messages from the Firebase Web Console, or from a custom server using the server-side APIs.
What you might want to do is to contact a server (like via http call) and that server will send the message to the user.
This way ensure that the API-KEY of the server is protected.
PS: the sendMessage(..) api is called upstream feature, and can be used to send messages from your app to your server, if you server has an XMPP connection with the FCM server.
Yes you can send push notification through Firebase.Please make sure do NOT include the server-key into your client. There are ways "for not so great people" to find it and do stuff... The Proper way to achieve that is for your client to instruct your app-server to send the notification.
You have to send a HTTP-Post to the Google-API-Endpoint.
You need the following headers:
Content-Type: application/json
Authorization: key={your_server_key}
You can obtain your server key within in the Firebase-Project.
HTTP-Post-Content: Sample
{
"notification": {
"title": "Notification Title",
"text": "The Text of the notification."
},
"project_id": "<your firebase-project-id",
"to":"the specific client-device-id"
}
Google Cloud Functions make it now possible send push notifications from device-to-device without an app server.
From the Google Cloud Functions documentation:
Developers can use Cloud Functions to keep users engaged and up to
date with relevant information about an app. Consider, for example, an
app that allows users to follow one another's activities in the app.
In such an app, a function triggered by Realtime Database writes to
store new followers could create Firebase Cloud Messaging (FCM)
notifications to let the appropriate users know that they have gained
new followers.
Example:
The function triggers on writes to the Realtime Database path where followers are stored.
The function composes a message to send via FCM.
FCM sends the notification message to the user's device.
Here is a demo project for sending device-to-device push notifications with Firebase and Google Cloud Functions.
Diego's answer is very accurate but there's also cloud functions from firebase it's very convenient to send notifications in every change in the db. For example let's say you're building chat application and sending notification in every new follower change.
This function sample is very good example.
For more information about cloud functions you can check official docs.
I have an app that has a "send feedback to developer" section. I also have a User collection in my firestore database. When a user logs into the app, I have that Users data update their FCM token with the following code in my SceneDelegate.swift:
import Firebase
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
authListener = Auth.auth().addStateDidChangeListener({ (auth, user) in
Auth.auth().removeStateDidChangeListener(self.authListener!)
if user != nil {
DispatchQueue.main.async {
let docRef = Firestore.firestore().collection("User").document((user?.email)!)
docRef.getDocument { (snapshot, error) in
guard let snapshot = snapshot else {return}
Messaging.messaging().token { token, error in
if let error = error {
print("Error fetching FCM registration token: \(error)")
} else if let token = token {
docRef.updateData(["FCMtoken":token])
print("FCM registration token: \(token)")
}
}
}
}
}
})
guard let _ = (scene as? UIWindowScene) else { return }
}
then in my feedback view controller i have this code to send my specific device (but you can look up/fetch which specific device you want in your database where the FCMtoken is stored where i have INSERT-DEVICE-TOKEN-HERE). The url to send to is "https://fcm.googleapis.com/fcm/send" and you can find YOUR-APP-FCM-KEY by going to your project settings in firebase, going to cloud messaging tab and its the server key.
func sendMePushNotification() {
let token = "INSERT-DEVICE-TOKEN-HERE"
if let url = URL(string: "https://fcm.googleapis.com/fcm/send") {
var request = URLRequest(url: url)
request.allHTTPHeaderFields = ["Content-Type":"application/json", "Authorization":"key=YOUR-APP-FCM-KEY"]
request.httpMethod = "POST"
request.httpBody = "{\"to\":\"\(token)\",\"notification\":{\"title\":\"Feedback Sent!\",\"body\":\"\(self.feedbackBox.text!)\",\"sound\":\"default\",\"badge\":\"1\"},\"data\": {\"customDataKey\": \"customDataValue\"}}".data(using: .utf8)
URLSession.shared.dataTask(with: request) { (data, urlresponse, error) in
if error != nil {
print("error")
} else {
print("Successfully sent!.....")
}
}.resume()
}
}
Use onesignal,you can send device to notifications or device to segments ,it can work with firebase in this way
Use onesignal functions to create a specific id,save it in a firebase database ,then when the id can be put in another function that is used to send a notification
Notes: 1-i am using it in my apps with firebase works perfectly
2-i can submit that code,just someone comments so i can find this answer

Resources