Sending Push Notifications using expo + firebase - firebase

I am trying to send push notifications using expo tokens and firebase.
When I am trying to test the expo tokens by sending notification by expo notification tool it works fine.
But when I use firebase to check the push notification it does not works.
Here is my firebase function index.js file.
const functions = require('firebase-functions');
var fetch = require('node-fetch')
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendPushNotification = functions.database.ref('/UserOrders/').onCreate((snap, context) => {
const root = snap.data.ref.root
var messages = []
return root.child('/PushNotification').once('value').then(function (snapshot) {
snapshot.forEach(function (childSnapshot) {
var expoToken = childSnapshot.val().expoToken;
messages.push({
"to": expoToken,
"sound": "default",
"body": "New enquiry. Please see!!"
});
});
return Promise.all(messages)
}).then(messages => {
console.log('messages', messages)
fetch('https://exp.host/--/api/v2/push/send', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(messages)
});
}).catch(reason => {
console.log('reason failure', reason)
})
});

Related

Sending notifications to specific user with Expo - React Native

Platform: React Native (IOS target)
Hi,
I am trying to build a way to send push notifications ( it's basically an app simillar to any datting app where you click a user and it sends them a notification to let them know I want to connect).
The notifications are being received fine on Expo Go but I don't know how to target a specific individual? Do I need backend for this?
This is my code:
const ProfilesScreen: React.FC<Props> = ({ navigation, route }) => {
const [expoPushToken, setExpoPushToken] = useState("");
const [notification, setNotification] = useState(false);
const [notificationRes, setNotifcationRes] = useState([]);
const notificationListener = useRef();
const responseListener = useRef();
const [data, setData] = useState<any>([]);
const [notificationsSentUids, setNotificationSentUids] = useState<any>([]);
const [notificationsCount, setNotificationsCount] = useState<number>(0);
const [activeIndex, setActiveIndex] = useState<number>(0);
const [storageData, setStorageData] = useState({});
async function sendPushNotification(expoPushToken) {
const message = {
to: expoPushToken,
sound: "default",
title: "My message",
body: storageData
? `${storageData?.name} wants to connect with you`
: null,
data: { someData: "goes here" },
};
await fetch("https://exp.host/--/api/v2/push/send", {
method: "POST",
headers: {
Accept: "application/json",
"Accept-encoding": "gzip, deflate",
"Content-Type": "application/json",
},
body: JSON.stringify(message),
});
}
async function registerForPushNotificationsAsync() {
let token;
if (Device.isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
setExpoPushToken(token);
} else {
// alert("Must use physical device for Push Notifications");
}
return token;
}
useEffect(() => {
registerForPushNotificationsAsync().then(token => setExpoPushToken(token));
// This listener is fired whenever a notification is received while the app is foregrounded
notificationListener.current =
Notifications.addNotificationReceivedListener(notification => {
setNotification(notification);
});
// This listener is fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed)
responseListener.current =
Notifications.addNotificationResponseReceivedListener(response => {
setNotifcationRes(response);
return () => {
Notifications.removeNotificationSubscription(
notificationListener.current
);
Notifications.removeNotificationSubscription(responseListener.current);
};
}, []);
return ....
};

easiest way to send firebase push notification from client site in react-native

I am new to react native and I am stuck to send push notification from client side. i have FCM token of every user but need to send a notification to specific user. if you want more clarity please comment and let me know the solution of this
I saw your problem and I have a solution for you. I am already doing it and here's my code.
export const sendPushNotification = async (token, title, body,) => {
//console.log("token==>", token);
const FIREBASE_API_KEY ="<your firebase cloud messaging server key>"
const message = {
registration_ids: [token],
notification: {
title: title,
body: body,
vibrate: 1,
sound: 1,
show_in_foreground: true,
priority: "high",
content_available: true
},
};
let headers = new Headers({
"Content-Type": "application/json",
Authorization: "key=" + FIREBASE_API_KEY
});
let response = await fetch("https://fcm.googleapis.com/fcm/send", {
method: "POST",
headers,
body: JSON.stringify(message)
});
// console.log("=><*", response);
response = await response.json();
// console.log("=><*", response);
};
you can use this function like this
sendPushNotification(
FCMToken,
'title of message',
`this is body of message`,
);
hope it will worked on your side

how to avoid duplicate Push Notification

importScripts('https://www.gstatic.com/firebasejs/8.4.3/firebase-app.js')
importScripts('https://www.gstatic.com/firebasejs/8.4.3/firebase-messaging.js')
firebase.initializeApp({...config});
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) {
console.log('sw-fb', payload);
const notificationTitle = payload.notification.title;
const notificationOptions = {
body:"something Body",
data:{...}
};
return self.registration.showNotification(notificationTitle, notificationOptions);
},
Here i am trying to show push notification from firebase, but for every single notification getting two notifications. 1st one is the default one and 2nd one is from my service worker.
can anyone help me to fix the duplicate default notification.
to avoid default notification ,when sending through fcm need to send notification data as below
var payload = {
notification:{},
data: {
title:'title',
body:'something body',
image:path,
icon...},
token: registrationToken
};
messaging.send(payload).then((result) => {console.log(result)})
and update srviceworker as
messaging.onBackgroundMessage((payload) => {
const { data }= payload;
const notificationTitle = data?.title;
const notificationOptions =
{ image:data.image, title:data.title, body: data.body, icon:data.icon }
self.registration.showNotification(notificationTitle, notificationOptions);
});

Authenticate Firebase with Auth0 using Netlify Lambda Functions

I have a web app built with Gatsby that has client-side authentication through Auth0. I want to use Firebase as a database for my project, but I need to authenticate users first before they can read/write to Firebase.
The Firebase SDK (firebase-admin) has a function called signInWithCustomToken(token) that I thought I could pass the token from Auth0 into, but this doesn't work (see: https://community.auth0.com/t/react-auth0-firebase/11392).
Instead, I need to proxy Auth0's token through an API which will use firebase-admin to issue a token. Because my Gatsby site is hosted on Netlify, I'm planning to use Netlify Lambda Functions to get proxy Auth0's token. This is where I'm getting stuck.
I've followed this tutorial on how to use Netlify Lambda Functions with Gastsby: https://www.gatsbyjs.org/blog/2018-12-17-turning-the-static-dynamic/
I then went into my Auth.js file where my Auth0 code is and dropped a fetch call in the setSession. I passed the idToken from Auth0 into the url in the fetch function. I'm not sure if this is the right thing to do. I've read in the tutorial that it would be passed in an authorization header, but I'm unclear what that means. Anyways, here's the complete auth.js file:
import auth0 from 'auth0-js';
const windowGlobal = typeof window !== 'undefined' && window;
class Auth {
auth0 = new auth0.WebAuth({
domain: process.env.Auth_Domain,
clientID: process.env.Auth_ClientId,
redirectUri: process.env.Auth_Callback,
responseType: 'token id_token',
scope: 'openid profile email',
});
constructor() {
this.login = this.login.bind(this);
this.logout = this.logout.bind(this);
this.handleAuthentication = this.handleAuthentication.bind(this);
this.isAuthenticated = this.isAuthenticated.bind(this);
}
login() {
this.auth0.authorize();
}
logout() {
// Remove the locally cached profile to avoid confusing errors.
localStorage.removeItem('access_token');
localStorage.removeItem('id_token');
localStorage.removeItem('expires_at');
localStorage.removeItem('user');
windowGlobal.window.location.replace(`https://login.skillthrive.com/v2/logout/?returnTo=http%3A%2F%2Flocalhost:8000`)
}
handleAuthentication() {
if (typeof window !== 'undefined') {
this.auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult)
} else if (err) {
console.log(err);
}
});
}
}
isAuthenticated() {
const expiresAt = JSON.parse(localStorage.getItem('expires_at'));
return new Date().getTime() < expiresAt;
}
setSession(authResult) {
const expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());
localStorage.setItem('access_token', authResult.accessToken);
localStorage.setItem('id_token', authResult.idToken);
localStorage.setItem('expires_at', expiresAt);
fetch(`/.netlify/functions/firebase?id=${authResult.idToken}`)
.then(response => console.log(response))
this.auth0.client.userInfo(authResult.accessToken, (err, user) => {
localStorage.setItem('user', JSON.stringify(user));
})
}
getUser() {
if (localStorage.getItem('user')) {
return JSON.parse(localStorage.getItem('user'));
}
}
getUserName() {
if (this.getUser()) {
return this.getUser().name;
}
}
}
export default Auth;
I found a tutorial called How to Authenticate Firebase and Angular with Auth0 that has a function that mints a token for Firebase:
const jwt = require('express-jwt');
const jwks = require('jwks-rsa');
const firebaseAdmin = require('firebase-admin');
// Config
const config = require('./config');
module.exports = function(app) {
// Auth0 athentication middleware
const jwtCheck = jwt({
secret: jwks.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${config.AUTH0_DOMAIN}/.well-known/jwks.json`
}),
audience: config.AUTH0_API_AUDIENCE,
issuer: `https://${config.AUTH0_DOMAIN}/`,
algorithm: 'RS256'
});
// Initialize Firebase Admin with service account
const serviceAccount = require(config.FIREBASE_KEY);
firebaseAdmin.initializeApp({
credential: firebaseAdmin.credential.cert(serviceAccount),
databaseURL: config.FIREBASE_DB
});
app.get('/auth/firebase', jwtCheck, (req, res) => {
// Create UID from authenticated Auth0 user
const uid = req.user.sub;
// Mint token using Firebase Admin SDK
firebaseAdmin.auth().createCustomToken(uid)
.then(customToken =>
// Response must be an object or Firebase errors
res.json({firebaseToken: customToken})
)
.catch(err =>
res.status(500).send({
message: 'Something went wrong acquiring a Firebase token.',
error: err
})
);
});
I tried to incorporate small parts at a time into my Lambda function:
var admin = require("firebase-admin");
const jwt = require('express-jwt');
const jwks = require('jwks-rsa');
// For more info, check https://www.netlify.com/docs/functions/#javascript-lambda-functions
export function handler(event, context, callback) {
console.log("queryStringParameters", event.queryStringParameters);
const jwtCheck = jwt({
secret: jwks.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${process.env.Auth_Domain}/.well-known/jwks.json`
}),
audience: process.env.Auth_Audience,
issuer: `https://${process.env.Auth_Domain}/`,
algorithm: 'RS256'
});
callback(null, {
// return null to show no errors
statusCode: 200, // http status code
body: JSON.stringify({
msg: "Hello, World! " + Math.round(Math.random() * 10),
}),
})
}
I tried checking to see what came back for jwtCheck by console logging it, but all I got was something weird { [Function: d] unless: [Function], UnauthorizedError: [Function: r] }
How should I go about incorporating this into my Lambda function?
I found a module called serverless-http that allows me to write Lambda Function as if it were written in Express. This made it easy for me to wrap my head around what was happening, so I finally got this code to return the new minted token from Firebase:
const express = require('express');
const serverless = require('serverless-http');
const cors = require('cors');
const jwt = require('express-jwt');
const jwks = require('jwks-rsa');
const firebaseAdmin = require('firebase-admin');
const app = express();
app.use(cors());
const jwtCheck = jwt({
secret: jwks.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `${process.env.Auth_Domain}/.well-known/jwks.json`
}),
audience: `${process.env.Auth_ClientId}`,
issuer: `${process.env.Auth_Domain}`,
algorithm: 'RS256'
});
const serviceAccount = require('../firebase/firebase-keys.json');
firebaseAdmin.initializeApp({
credential: firebaseAdmin.credential.cert(serviceAccount),
databaseURL: `https://${serviceAccount.project_id}.firebaseio.com`
});
// GET object containing Firebase custom token
app.get('/firebase', jwtCheck, async (req, res) => {
const {sub: uid} = req.user;
try {
const firebaseToken = await firebaseAdmin.auth().createCustomToken(uid);
res.json({firebaseToken});
} catch (err) {
res.status(500).send({
message: 'Something went wrong acquiring a Firebase token.',
error: err
});
}
});
module.exports.handler = serverless(app);
Then on the client side I wrapped the fetch call into a function like this and used it when needed:
async setFirebaseCustomToken() {
const response = await fetch('/.netlify/functions/firebase', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('id_token')}`,
},
});
const data = await response.json();
console.log(data.firebaseToken);
}
This code is just going to console.log the new token, but now you'll have the response to do what you want with in Firebase client-side. Hope this helps!

Cloud function return RequestError: Error: socket hang up

I have use Cloud function connect with onesignal service which will send notification to user. After test function locally, it's work perfectly but after deploying to the cloud function it return me an error "RequestError: Error: read ECONNRESET" which I thick the cloud function reset the connection
Here is what my code look like
exports.sendNotification = functions
.pubsub
.topic('cron-notification')
.onPublish(async (message) => {
const databaseRef = admin.database();
// Query all user from realtime db
const snapshotsUser = await databaseRef
.ref(`user`)
.orderByKey()
.once("value");
// Check if user exist
if (snapshotsUser) {
//Get the user key
const user_object_key = Object.keys(snapshotsUser.val());
// send notification for each user
user_object_key.map(async (user_id) => {
// query something
const snapshotsUser = await databaseRef
.ref(`record/user_id`)
.orderByKey()
.once("value");
const message = {
"app_id": "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
"filters": [
{"field": "tag", "key": "user_id", "value": user_id}
],
"headings": {"en": `Hello World`},
"contents": {"en": `Hello`}
}
sendNotification(message);
})
}
});
function sendNotification(message) {
const headers = {
"Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic XXXXXXXXXXXXXXXXXXXXXXXXXXX"
};
// Use onesignal for send notification
const options = {
uri: "https://onesignal.com/api/v1/notifications",
headers: headers,
method: 'POST',
json: true,
body: message,
resolveWithFullResponse: true,
}
return request(options).then(response => {
if (response.statusCode >= 400) {
throw new Error(`HTTP Error: ${response.statusCode}`);
} else {
console.log(response.body)
}
}).catch(error => {
console.log(error);
})
}
Can anyone give suggestion for me?
According to the Node.js documentation ‘ECONNRESET’ error occurs when a connection is forcibly closed by peer. This results in loss of connection on the remote socket due to timeout or reboot. Since you mentioned the code is working locally and the error occurs after it is deployed, here is an answer that says the possible solution is to increase the number of cores so the requests can be serviced faster. Also, it might be useful to read the GitHub discussion on socket hang up error.
Just for people who face the same problem with me, I just notice that I did not return anything in the function. The cloud function will reset the function if it did not return any thing back. So from the code above should be like this and everything work fine.
exports.sendNotification = functions.pubsub.topic('cron-notification').onPublish(async (message) => {
const databaseRef = admin.database();
// Query all user from realtime db
const snapshotsUser = await databaseRef
.ref(`user`)
.orderByKey()
.once("value");
// Check if user exist
if (snapshotsUser) {
//Get the user key
const user_object_key = Object.keys(snapshotsUser.val());
// send notification for each user
return Promise.all([ user_object_key.map(async (user_id) => {
// query something
const snapshotsUser = await databaseRef
.ref(`record/user_id`)
.orderByKey()
.once("value");
const message = {
"app_id": "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
"filters": [
{"field": "tag", "key": "user_id", "value": user_id}
],
"headings": {"en": `Hello World`},
"contents": {"en": `Hello`}
}
sendNotification(message);
})])}});
function sendNotification(message) {
const headers = {
"Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic XXXXXXXXXXXXXXXXXXXXXXXXXXX"
};
// Use onesignal for send notification
const options = {
uri: "https://onesignal.com/api/v1/notifications",
headers: headers,
method: 'POST',
json: true,
body: message,
resolveWithFullResponse: true,
}
return request(options).then(response => {
if (response.statusCode >= 400) {
throw new Error(`HTTP Error: ${response.statusCode}`);
} else {
console.log(response.body)
}
}).catch(error => {
console.log(error);
})}

Resources