Firebase Messaging Error in Functions - firebase

in my project I use Firebase Functions to send message via FCM.
I use this API:
admin.messaging().send()
Recently and not for all token that I use to invoke it, I got this error:
Error: Requested entity was not found.
at FirebaseMessagingError.Error (native)
at FirebaseMessagingError.FirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:39:28)
at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:85:28)
at new FirebaseMessagingError (/user_code/node_modules/firebase-admin/lib/utils/error.js:241:16)
at Function.FirebaseMessagingError.fromServerError (/user_code/node_modules/firebase-admin/lib/utils/error.js:271:16)
at /user_code/node_modules/firebase-admin/lib/messaging/messaging-api-request.js:140:50
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
How can I solve it ?
I'm sure some time ago it works and these errors are not.
Any changes in Firebase?

This error means the token you are trying to send the notification, doesn't exists.
You must add your send code on a try catch block, and, depending on the error, erase (or invalidate) the token on your base.
try {
...
sendYourNotification(token);
...
} catch (error) {
if (
[
'The registration token is not a valid FCM registration token',
'Requested entity was not found.',
'NotRegistered.'
].includes(error.message)
) {
// invalidate current token
// find the user and remove this token
} else {
// Log it, because is some really unexpected
}
}

Related

Do Callable Cloud Functions Ensure a Valid Token when Called

I am calling a callable cloud function from a Javascript frontend and when calling Firebase in the past I would chain firebase.auth().currentUser.getIdToken(... before my call to the backend to ensure I had a valid token. Now that I am switching to callable cloud functions, I am wondering if this token refresh check is embedded in the callable itself or if I have to still check that my token is valid.
When calling a method returned by the callable builder, like const myFunc = httpsCallable(funcName); myFunc(/* data */);, only the current ID token is attached. If the token has not yet expired, it is not forcibly refreshed.
At least for the JavaScript SDK, this is seen in the source code of packages/functions/src/service.ts and packages/functions/src/context.ts:
// in packages/functions/src/service.ts
const context = await functionsInstance.contextProvider.getContext();
if (context.authToken) {
headers['Authorization'] = 'Bearer ' + context.authToken;
}
// in packages/functions/src/context.ts
async getAuthToken(): Promise<string | undefined> {
if (!this.auth) {
return undefined;
}
try {
const token = await this.auth.getToken();
return token?.accessToken;
} catch (e) {
// If there's any error when trying to get the auth token, leave it off.
return undefined;
}
}
This essentially leads to the following decisions:
If Firebase Authentication isn't loaded, return undefined (don't attach any tokens).
If the no one is logged in, return null (don't attach any tokens).
If the token has expired, attempt to get a fresh one (and then attach it to the request).
If the token can't be obtained, return undefined (don't attach any tokens).
If the token has not expired, return the access token (and then attach it to the request).
Even though token expiry is handled by the SDK, you can still forcibly freshen up the token by using getIdToken(/* forciblyRefresh: */ true) before calling the function. The Cloud Functions SDK will call the Admin SDK to verify whatever token is sent as soon as the request is received regardless.
Aside from that, you can further enhance the security of your Cloud Function by enforcing a cutoff on how long ago the user signed into their account for privileged actions like account deletion, changing service account details and so on. This is done using the auth_time claim inside the access token's data or the authTime property on the id token object.

MissingPluginException(No implementation found for method send on channel flutter_email_sender)

Sending mail from Flutter Web. Trying to send email with security code to recipient email input in the TextFormField.
import 'package:flutter_email_sender/flutter_email_sender.dart';
final Email email = Email(
body: _bodyController.text,
subject: _subjectController.text,
recipients: [_recipientController.text],
attachmentPaths: attachments,
isHTML: isHTML,
);
String platformResponse;
try {
await FlutterEmailSender.send(email);
platformResponse = 'success';
} catch (error) {
platformResponse = error.toString();
}
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(platformResponse),
),
);
pubspec.yaml
flutter_email_sender: ^5.0.2
The error got:
MissingPluginException(No implementation found for method send on channel flutter_email_sender)
The problem remains the same after Invalidate Caches/Restart.
Does anyone know how to solve to this problem? Thanks!
Currently the flutter_email_sender plugin is not supported for the web platform.
The displayed error message MissingPluginException(No implementation found for method send on channel flutter_email_sender) indicates that.
All packages available in pub.dev have some tags that indicates the supported platforms.
Check out the following
Execute flutter run since you changed the pubspec.yml
Rebuild it instead of hot restart/hot reload
If that doesn't work, try flutter clean
Rebuild it instead of hot restart/hot reload

Push notification on Firebase Functions - Iterate onWrite returns

So I am trying to send push notifications to user with using Firebase Functions & FCM. I referred to the sample code to populate my pushes and when I use the hardcoded token, it works and the push gets sent correctly. Below is my working cloud function code.
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendChatNotification = functions.database.ref('/user_pushes/{uid}').onWrite((change, context) => {
const afterData = change.after.val();
//let tokens = "My_hardcoded_device_token"
console.log('tokens',afterData.fcm_tokens)
let tokens = afterData.fcm_tokens
const payload = {
notification: {
title: afterData.sender_name,
body: afterData.message,
sound: "default"
}
};
return admin.messaging().sendToDevice(tokens, payload);
});
And below is my json of RTDB of '/user_pushes/{uid}'.
{ fcm_tokens:
{ 'token1': 0,
'token2': 0,
'token3': 0, },
message: '👍🏼',
sender_name: 'sender name'
}
Of course I want to get tokens from my database. I expected to use like for in loop to get my tokens from the node named 'fcm_tokens' but no luck yet to find proper snippet. I want my tokens to be in a kind of an array form so that the pushes can be sent to multiple receivers. I am an inexperienced self taught iOS developer and not yet learnt server side programming so if my approach to get this is not appropriate please do advise! Many thanks in advance!
###EDIT###
I tried to retrieve the tokens with below a line of code and it throws an another error saying that the token should not be empty string or an array although it is likely not empty. Maybe my token(s) are keys not the values and it is the reason why? If it is the case can anybody advise how to get the keys into an array?
let tokens = afterData.fcm_tokens
console.log('tokens',afterData.fcm_tokens)
tokens { 'fWgm79ifOUVSofW2XXhoKt:APA91bHovn04k5lwnkzfQD74VmekV8FoTSUh8pyr9d_I0EMDPuzFdKpD7Y4OU_AKHVjalGQaGE_I6A5m6livf8QrxHTSTmn9h6EB9qyBde_reQRcjU6cZLHLLXx2cO0w6f3MOZOFnYDv': 0 }
Error: Registration token(s) provided to sendToDevice() must be a non-empty string or a non-empty array.
at FirebaseMessagingError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:88:28)
at new FirebaseMessagingError (/srv/node_modules/firebase-admin/lib/utils/error.js:254:16)
at Messaging.validateRegistrationTokensType (/srv/node_modules/firebase-admin/lib/messaging/messaging.js:729:19)
at Messaging.sendToDevice (/srv/node_modules/firebase-admin/lib/messaging/messaging.js:328:14)
at exports.sendChatNotification.functions.database.ref.onWrite (/srv/index.js:27:26)
at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:134:23)
at /worker/worker.js:825:24
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
Finally I got it work after a few of workarounds. It turns out that it is quite simple. Just catch keys with forEach syntax to iterate through the json.
const tokens = Object.keys(afterData.fcm_tokens)
For more detail, please see below.
Error with registration token in FCM with Cloud Functions

TypeError: Redirected path should match configured path, but got: /callback

I have deployed the ExpressJs app on firebase function and trying to trigger the express app with an HTTP trigger.
I am trying to authorize my express app with OAUTH2.0 to use QuickBooks API. However, I am getting the following error:
TypeError: Redirected path should match configured path, but got: /callback
at CodeFlow.getToken (/srv/node_modules/client-oauth2/src/client-oauth2.js:597:7)
at /srv/routes/callback.js:12:25
at Layer.handle [as handle_request] (/srv/node_modules/express/lib/router/layer.js:95:5)
at next (/srv/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/srv/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/srv/node_modules/express/lib/router/layer.js:95:5)
at /srv/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/srv/node_modules/express/lib/router/index.js:335:12)
at next (/srv/node_modules/express/lib/router/index.js:275:10)
at Function.handle (/srv/node_modules/express/lib/router/index.js:174:3)
The error is in following file:
callback.js
router.get('/', function (req, res) {
// Verify anti-forgery
console.log("OriginalURL "+req.originalUrl)
console.log("base URL "+req.baseUrl)
// Exchange auth code for access token
tools.intuitAuth.code.getToken(req.originalUrl).then(function (token) { //This is where promise is failing
I am using npm package called "client-oauth2".
client-oauth2.js
if (
typeof options.redirectUri === 'string' &&
typeof url.pathname === 'string' &&
url.pathname !== Url.parse(options.redirectUri).pathname
) {
return Promise.reject(
new TypeError('Redirected path should match configured path, but got: ' + url.pathname) //The source of Error. I am not able to find out the value of options.redirectUri
)
}
Following are my configuration details:
"redirectUri": "us-central1-thematic-runner-245505.cloudfunctions.net/createEstimate/callback"
I have made sure that this URL matches with Quickbooks side redirect URL. In the case of localhost it works just fine.
Localhost URL: http://localhost:5001/thematic-runner-245505/us-central1/createEstimate/
The value of req.originalUrl from callback.js is
OriginalURL /thematic-runner-245505/us-central1/createEstimate/callback?code=AB11585447829JwgpXMEpWOR6irwprMe9Z8aqRoSK4npFDKmte&state=Z0t9yRkl-dWaO2J5sJRDaTB9eZKvyyyVcHYQ&realmId=4620816365002391850
Could somebody help me with this error? I don't know what I am doing wrong in the case of production. The callback URL seems to be fine. Any help would be appreciated.
This isn't a valid URL:
"redirectUri": "us-central1-thematic-runner-245505.cloudfunctions.net/createEstimate/callback"
There's no scheme/protocol portion to that URL (e.g. https://). It should start with https:// but instead you're jumping right into the domain.
It needs to be a valid URL, including the protocol/scheme.

Save device token with FCM in ionic 2

I am using FCM plugin to do push notification for ionic2.
reference : https://www.npmjs.com/package/cordova-plugin-fcm
I followed https://github.com/edismooth/ionic2-firebase/tree/master
It works fine and I can received the push from firebase console. Now I want to build my own server to let admin to send push notification with own backend.
I faced one problem is : I can get the device token, however, I have no idea how to save it to local storage. The code is as below. May I know which part I am wrong? Is that “this.local.set” can’t be used within the function of “FCMPlugin.getToken”. If yes, how can I save the token and use out of this function? Many thanks.
initializeApp() {
this.platform.ready().then(() => {
StatusBar.styleDefault();
FCMPlugin.getToken(
function (token) {
console.log(token); //I can get the token data
this.local.set('tokenvalue', token); // the token value can't be saved to local storage like this
},
function (err) {
console.log('error retrieving token: ' + err);
}
);
}
First, try this this.localStorage.set(...), if that doesn't work and you have access to the window object. Use window.localStorage.setItem(...)
You can use ionic Storage.
I describe it in this answer.
https://stackoverflow.com/a/41105221/902630

Resources