FirebaseMessagingError: Invalid registration token provided - firebase

I am trying to send push notifications to another iOS device using firebase cloud functions but I receive the following error below when attempting to do so:
'FirebaseMessagingError: Invalid registration token provided. Make sure it matches the registration token the client app receives from registering with FCM.'
This is the registration token I am trying to send to 6e04bb35f06e2d981d5603bbd229eeab5ee5649f6af7b4ecc3894be6ad1574d7 which is the same token I have saved in my realtime database:
Below is my onCreate function:
exports.onMessageCreate = functions.database.ref('/messages/{chatId}/{messageId}').onCreate((snapshot, context) => {
const chatId = context.params.chatId;
const receiverId = chatId.replace(context.auth.uid, '');
const root = snapshot.ref.root;
return admin.database().ref(`/users/${receiverId}/regToken`).once('value').then(tokenSnap => {
var regTokenRef = tokenSnap.val();
const payload = {
notification: {
title: 'title',
body: snapshot.val().text,
}
};
const options = { priority: "high" };
return admin.messaging().sendToDevice(regTokenRef, payload, options).then(function(response){
console.log("Successfully sent message: ", response);
console.log(response.results[0].error);
}).catch(function(error) {
console.log("Error sending message: ", error);
});
});
});
Do you know what may be causing this error? I am not too sure what I need to check here. Is the format wrong here? Does it need to be in quotes? I have looked at other links but they haven't really helped.
I am importing '#react-native-community/push-notification-ios' to generate the token. I think this may be the problem. I was having issues to use the messaging from firebase. Is this the issue?
Please see below the didFinishLaunchingWithOptions method updated in my AppDelegate.m file. Have I placed the below code correctly? When this is added it is causing my app to appear blank when launching the simulator with the FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp() (app/no-app).? However, when removed my app launches but with still has the FirebaseError.
if ([FIRApp defaultApp] == nil) {
[FIRApp configure];
}

#react-native-community/push-notification-ios is not able to generate an FCM token, only APN token: https://github.com/react-native-push-notification-ios/push-notification-ios/issues/117
If you want to use firebase, you need to generate the token from the messaging package of firebase to have a FCM one.

Related

How to solve problem Firebase Cloud Messaging error in Firebase Cloud function?

I have some problem about using Firebase Cloud Messaging from Firebase Cloud Functions.
The error message is below. It is from my Firebase Cloud Functions Log console.
Error: An error occurred when trying to authenticate to the FCM servers. Make sure the credential used to authenticate this SDK has the proper permissions.
At first, I follow Firebase Cloud Functions CodeLabs.
https://firebase.google.com/codelabs/firebase-cloud-functions
And at last lab "New Message Notifications", when I insert new message at Web "FriendlyChat" app, there is not display notification message. Then I checked log in Firebase Cloud Functions Log console, there was an error message which I had told.
How to solve problem Firebase Cloud Messaging error in Firebase Cloud function?
Or ... How can I check about cloud functions credential before call FCM?
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
// Sends a notifications to all users when a new message is posted.
exports.sendNotifications = functions.firestore.document('messages/{messageId}').onCreate(
async (snapshot) => {
// Notification details.
const text = snapshot.data().text;
const payload = {
notification: {
title: `${snapshot.data().name} posted ${text ? 'a message' : 'an image'}`,
body: text ? (text.length <= 100 ? text : text.substring(0, 97) + '...') : '',
icon: snapshot.data().profilePicUrl || '/images/profile_placeholder.png',
click_action: `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com`,
}
};
// Get the list of device tokens.
const allTokens = await admin.firestore().collection('fcmTokens').get();
const tokens = [];
allTokens.forEach((tokenDoc) => {
tokens.push(tokenDoc.id);
});
if (tokens.length > 0) {
// Send notifications to all tokens.
const response = await admin.messaging().sendToDevice(tokens, payload);
await cleanupTokens(response, tokens);
functions.logger.log('Notifications have been sent and tokens cleaned up.');
}
});
Thank you in advance.
I solve this problem by set "Enabled" at "Cloud Messaging API (Legacy)" at Project Settings.

Firebase Auth in a Flutter app with WebView

I'm developing a Flutter app that uses Firebase Auth to handle authentication. However, some sections of the app use a WebView that shows content from the web version (which also uses Firebase Auth). My question is to how ensure that users that have signed in to the app are also signed in within the WebView.
There's nothing built into Firebase to automatically synchronize the authentication state from native code into a web view that is opened from this native code.
It should be possible to pass the ID token from the native code to the web view and use it there, but I've never tried that myself.
Some relevant links that I found:
How to pass Firebase Auth token to webView and register for notifications on Android (describes the same problem, but then with Android - and unfortunately without an answer)
Is there a way to keep the user signed in between native code and a WebView using Firebase Auth on Android? (unfortunately also without an answer)
Webviews and social authentication with React Native (blog post describing a workaround for this type of problem with Facebook login and react native)
How to do Authentication on native and pass to webView? (also with React Native, but this answer looks promising)
capacitor-firebase-auth npm module (plugin for Capacitor framework that propagates the token from native code to web view)
None of these are pre-built solutions for Flutter + WebView, but I hope that combined they allow you to build something yourself. If you do: please share it! :)
Here is solution for Firebase Auth with WebView in React Native:
import React from 'react'
import WebView from 'react-native-webview'
export default function HomeScreen(props) {
// props.user represents firebase user
const apiKey = props.user.toJSON().apiKey
const authJS = `
if (!("indexedDB" in window)) {
alert("This browser doesn't support IndexedDB")
} else {
let indexdb = window.indexedDB.open('firebaseLocalStorageDb', 1)
indexdb.onsuccess = function() {
let db = indexdb.result
let transaction = db.transaction('firebaseLocalStorage', 'readwrite')
let storage = transaction.objectStore('firebaseLocalStorage')
const request = storage.put({
fbase_key: "firebase:authUser:${apiKey}:[DEFAULT]",
value: ${JSON.stringify(props.user.toJSON())}
});
}
}
`
return <WebView
injectedJavaScriptBeforeContentLoaded={authJS}
source={{
uri: 'http://192.168.1.102:3000',
baseUrl: 'http://192.168.1.102:3000',
}}
/>
}
Similar logic might be required in Flutter (JS injection).
High Level
From Flutter mobile client, sign in to Firebase
Generate a unique Firestore document for the logged in user, setting whatever auth data you need to lookup via calls from the webView - eg, uid, email, etc
Pass that doc.id to the webView, and use that token value as a parameter for cloud functions being called from the webView, that require the logged-in user data
Code
Implementation requires 5 small JS blocks between Firebase cloud and the browser:
From Flutter mobile client, call cloud function to give you a unique token, where token will be a doc ID and data will have Auth User uid:
exports.getWebAppUserToken = functions.https.onCall(async (data, context) => {
let docRef = await firestore.collection('webTokens')
.add({uid : context.auth['uid']});
return {'webToken' : docRef.id};
});
Pass the token into the url called to open the webview, eg: http://app.com/appPage/<token>, and then extract token in browser:
getValidationToken() {
let href = window.location.href;
let lastIdx = href.lastIndexOf('/');
return href.substr(lastIdx + 1).trim();
}
Now from the browser you can call a cloud function using the token:
const authFuncCalledFromWeb =
firebase.functions().httpsCallable('authFuncCalledFromWeb');
const result = await authFuncCalledFromWeb(uiValidationToken);
Cloud function that uses the webToken to get uid for the request:
exports.authFuncCalledFromWeb = functions.https.onCall(async (data, context) => {
let webToken = data;
let uid = await getWebTokenUid(webToken);
// >>> do stuff that requires uid
});
Helper to lookup webToken:
getWebTokenUid = async function (webToken) {
let webTokenDoc = await firestore.collection(appData.Collctn.webTokens)
.doc(webToken).get();
let webTokenDocData = webTokenDoc.data();
return webTokenDocData['uid'];
}
=================
Here's a variation if you want to consider expiring the token:
<!-- begin snippet: js hide: true -->
let EXPIRES_INTERVAL = 1000 * 60 * 20;
exports.getWebAppUserToken = functions.https.onCall(async (data, context) => {
logr.enter(`getWebAppUserToken`);
const uid = appConfig.getLoggedInUid(context);
logr.i(`uid: ${uid}`);
// For Field.expires, consider that webToken will not be
// looked up until user clicks HTML submit action.
// So whatever interval we give, we should check in client
// On the other hand, user can only get this token through
// the app in a cloud func, so expires may not be nec.
let expiresTimestamp = dateUtil.getNowNumericTimestamp() + EXPIRES_INTERVAL;
let webTokenProfile = {
[Field._created] : dateUtil.getNowReadableTimestampPST(),
[Field.expires] : expiresTimestamp,
[Field.uid] : uid,
}
let docRef = await firestore.collection('webTokens')
.add(webTokenProfile);
let webToken = docRef.id;
return {'data' : webToken};
});

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.

Flutter: Firebase Pushnotification On Data Change

After getting the comment, i have deployed this folowing code to my firebase project and it was successfully deploed!.But there is no notifications been send to me.
Please check my Firebase Realtime database Screenshot here for better understanding.
[ITS SOLVED NOW:IT WILL SEND NOTIFICATIONS TO ONLY ONE ID ie My Admin Device]
WORKING CODE:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firbase);
exports.codeformypeople = functions.database.ref('items/{id}').onWrite(evt => {
const payload = {
notification: { title: 'New Customer Requested', body: 'Touch to Open The App', badge: '1', sound: 'default', }
};
const token ="Lsn-bHfBWC6igTfWQ1-h7GoFMxaDWayKIpWCrzC";//replace with ur token
if (token) {
console.log('Toke is availabel .');
return admin.messaging().sendToDevice(token, payload);
} else {
console.log('token error');
}
});
[
SEE THIS VIDEO LINK FOR MORE DETAILS
note:If your app is opened and minmized then it will show notification,but if the app is opened and you are using,or if the app is terminated force close then it will not work!!
You can use firebase cloud function to trigger notification. Here is snippet of cloud functions which i am using to trigger notification:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.pushNotification = functions.database.ref('/Notifications/{pushId}')
.onWrite(( change,context) => {
console.log("Push Notification event triggered");
var request = change.after.val();
var payload = {
data:{
username: request.userName,
}
};
admin.messaging().sendToDevice(request.userTokenId, payload)
.then(function(response){
console.log("Successfully sent message: ",response);
console.log(response.results[0].error);
})
.catch(function(error){
console.log("Error sending message: ", error)
})
})
Below i have provided my notification structure, you can see below.This function will trigger if any new data is being pushed in database notification node. To see what is output/error you are getting when this function is trigger go to firebase admin panel > Functions > Logs.
You have deployed function code perfectly, but you forgot to add refresh tokenId in your database as you can see in above picture i am saving userTokenId in my database and in function admin.messaging().sendToDevice(request.userTokenId, payload) i am using that tokenId, this tokenId is used to send notification to particular device, you can get this tokenId using FirebaseInstanceId.getInstance().getToken() and save this in your fbproject1 > items database sturcture. please refer this & this

How to send email verification after user creation with Firebase Cloud functions?

I'm trying to send the verification email after the user is created. Since there's no way on Firebase itself, I'm trying it with cloud functions.
I cannot really find a lot of documentation about it. What I tried to do so far is:
exports.sendEmailVerification = functions.auth.user().onCreate(event => {
return user.sendEmailVerification()
});
But I get the error that user is not defined.
How can I create this function?
Thanks!
There are two possibilities to send an "email verification" email to a user:
The signed-in user requests that a verification email be sent. For that, you call, from the front-end, the sendEmailVerification() method from the appropriate Client SDK.
Through one of the Admin SDKs, you generate a link for email verification via the corresponding method (e.g. auth.generateEmailVerificationLink() for the Node.js Admin SDK) and you send this link via an email sent through your own mechanism. All of that is done in the back-end, and can be done in a Cloud Function.
Note that the second option with the Admin SDKs is not exactly similar to the first option with the Client SDKs: in the second option you need to send the email through your own mechanism, while in the first case, the email is automatically sent by the Firebase platform
If you'd like that ability to be added to the Admin SDK, I'd recommend you file a feature request.
This is how I implemented it successfully using Firebase cloud functions along with a small express backend server
Firebase Cloud function (background) triggered with every new user created
This function sends a "user" object to your api endpoint
const functions = require('firebase-functions');
const fetch = require('node-fetch');
// Send email verification through express server
exports.sendVerificationEmail = functions.auth.user().onCreate((user) => {
// Example of API ENPOINT URL 'https://mybackendapi.com/api/verifyemail/'
return fetch( < API ENDPOINT URL > , {
method: 'POST',
body: JSON.stringify({
user: user
}),
headers: {
"Content-Type": "application/json"
}
}).then(res => console.log(res))
.catch(err => console.log(err));
});
Server Middleware code
verifyEmail here is used as middleware
// File name 'middleware.js'
import firebase from 'firebase';
import admin from 'firebase-admin';
// Get Service account file from firebase console
// Store it locally - make sure not to commit it to GIT
const serviceAccount = require('<PATH TO serviceAccount.json FILE>');
// Get if from Firebase console and either use environment variables or copy and paste them directly
// review security issues for the second approach
const config = {
apiKey: process.env.APIKEY,
authDomain: process.env.AUTHDOMAIN,
projectId: process.env.PROJECT_ID,
};
// Initialize Firebase Admin
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
// Initialize firebase Client
firebase.initializeApp(config);
export const verifyEmail = async(req, res, next) => {
const sentUser = req.body.user;
try {
const customToken = await admin.auth().createCustomToken(sentUser.uid);
await firebase.auth().signInWithCustomToken(customToken);
const mycurrentUser = firebase.auth().currentUser;
await mycurrentUser.sendEmailVerification();
res.locals.data = mycurrentUser;
next();
} catch (err) {
next(err);
}
};
Server code
// Filename 'app.js'
import express from 'express';
import bodyParser from 'body-parser';
// If you don't use cors, the api will reject request if u call it from Cloud functions
import cors from 'cors';
import {
verifyEmail
} from './middleware'
app.use(cors());
app.use(bodyParser.urlencoded({
extended: true,
}));
app.use(bodyParser.json());
const app = express();
// If you use the above example for endpoint then here will be
// '/api/verifyemail/'
app.post('<PATH TO ENDPOINT>', verifyEmail, (req, res, next) => {
res.json({
status: 'success',
data: res.locals.data
});
next()
})
This endpoint will return back the full user object and will send the verification email to user.
I hope this helps.
First view the documentation by Firebase here.
As the registration phase completes and result in success, trigger the following function asynchronously :
private void sendVerification() {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
user.sendEmailVerification().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
system.print.out("Verification Email sent Champion")
}
}
});
}
The user will now be provided with a verification Email. Upon clicking the hyper linked the user will be verified by your project server with Firebase.
How do you determine whether or not a user did verify their Email?
private void checkEmail() {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user.isEmailVerified()) {
// email verified ...
} else {
// error : email not verified ...
}
}
Sadly, you may not customize the content/body of your verification Email ( I have been heavily corresponding with Firebase to provide alternative less hideous looking templates ). You may change the title or the message sender ID, but that's all there is to it.
Not unless you relink your application with your own supported Web. Here.
Since the release of the Version 6.2.0 of the Node.js Admin SDK on November 19, 2018 it is possible to generate, in a Cloud Function, a link for email verification via the auth.generateEmailVerificationLink() method.
You will find more details and some code samples in the documentation.
You can then send an email containing this link via Mailgun, Sendgrid or any other email microservice. You'll find here a Cloud Function sample that shows how to send an email from a Cloud Function.
If you want to let Admin SDK do it, as of now there is no option other than generating the email verification link and sending with your own email delivery system.
However
You can write a REST request on cloud functions and initiate the email verification mail this way.
export async function verifyEmail(apiKey : string, accessToken : string) {
// Create date for POST request
const options = {
method: 'POST',
url: 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/getOobConfirmationCode',
params: {
key: apiKey
},
data: {
requestType : "VERIFY_EMAIL",
idToken : accessToken
}
};
return await processRequest(options); //This is just to fire the request
}
As soon as you signup, pass the access token to this method and it should send a mail to the signup user.
apiKey : Is the "Web API key" listed in General tab of your project settings in firebase console
access token : Access token of the current user (I use signup rest api internally so there is an option to request token in response)

Resources