FCM Web Push Notifications - can't obtain token - firebase

This is really nocking me out after spending with such easy thing ver two days:
I'm trying to implement FCM Web browser Push Notifications and I went through the google docs several times as well as I watched all official videos on youtube. It is really easy to get the Token but for some reason it crashes in Firebase's JS code.
Here is my HTML/JS code here:
<html>
<head>
<title>Web Push Test 2</title>
<script src="/vendors/jquery/dist/jquery.min.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.13.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.13.0/firebase-messaging.js"></script>
</head>
<body>
<button type="button" id="subscribe">Subscribe</button><br />
<script src="https://www.gstatic.com/firebasejs/4.13.0/firebase.js"></script>
<script>
var config = {
apiKey: "AIzaSyBgYGotOm09UkhERqVPriV1XNhymxracno",
authDomain: "******-b6f9c.firebaseapp.com",
databaseURL: "https://******-b6f9c.firebaseio.com",
projectId: "*******-b6f9c",
storageBucket: "******-b6f9c.appspot.com",
messagingSenderId: "333638181210"
};
firebase.initializeApp(config);
if ('Notification' in window) {
console.log("Notification is in window.");
var messaging = firebase.messaging();
messaging.usePublicVapidKey("BE0MvVZ0zyTYGmeNIdj4A8XZZ50OKaZL90xmXbIVfufcMuPb0lAUC99426aBPrAEPHAWYeMbOTpHbcM3OiySEcs");
messaging.onMessage(function(payload) {
console.log("Message received. ", payload);
});
messaging.onTokenRefresh(function() {
messaging.getToken().then(function(refreshedToken) {
console.log('Token refreshed.');
setTokenSentToServer(false);
sendTokenToServer(refreshedToken);
}).catch(function(err) {
console.log('Unable to retrieve refreshed token ', err);
showToken('Unable to retrieve refreshed token ', err);
});
});
if (Notification.permission === 'granted') {
console.log("Permission is granded.");
subscribe();
}
$('#subscribe').on('click', function () {
console.log("Subscribe fired.");
subscribe();
});
}
function subscribe() {
messaging.requestPermission().then(function() {
console.log('Notification permission granted.');
messaging.getToken().then(function(currentToken) {
if (currentToken) {
sendTokenToServer(currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
setTokenSentToServer(false);
}
}).catch(function(err) {
console.log('An error occurred while retrieving token. ', err);
showToken('Error retrieving Instance ID token. ', err);
setTokenSentToServer(false);
});
}).catch(function(err) {
console.log('Unable to get permission to notify.', err);
});
}
window.is_sentToServer = false
function setTokenSentToServer(sent) {
window.is_sentToServer = sent
}
function showToken(currentToken) {
console.log('Token: '+currentToken);
}
function sendTokenToServer(currentToken) {
$.post('/?c=push&a=save_subscription', {token: currentToken}, function(data){
console.log('Token added...');
setTokenSentToServer(true);
});
}
</script>
</body>
When I run the page, I get the following error:
An error occurred while retrieving token. TypeError: Cannot read property 'buffer' of undefined
And the crash point of firebase JS is here:
https://www.gstatic.com/firebasejs/messaging/dist/index.esm.js
function isTokenStillValid(pushSubscription, publicVapidKey, tokenDetails) {
if (!isArrayBufferEqual(publicVapidKey.buffer, tokenDetails.vapidKey.buffer)) {
return false;
}
var isEndpointEqual = pushSubscription.endpoint === tokenDetails.endpoint;
var isAuthEqual = isArrayBufferEqual(pushSubscription.getKey('auth'), tokenDetails.auth);
var isP256dhEqual = isArrayBufferEqual(pushSubscription.getKey('p256dh'), tokenDetails.p256dh);
return isEndpointEqual && isAuthEqual && isP256dhEqual;
}
So as I can understand so far - the tokenDetails.vapidKey variable is undefined, that's is why it can't read the buffer size, but the question is - why?
I double checked that all my provided Keys are valid but can't figure out what could be wrong here...
Many thanks to anyone who could help me out with this
I'm running that test on localhost as it is shown in original YouTube tutorial and I didn't forget to create the firebase-messaging-sq.js which looks like this:
// firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/4.13.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.13.0/firebase-messaging.js');
var config = {
apiKey: "AIzaSyBgYGotOm09UkhERqVPriV1XNhymxracno",
authDomain: "*****-b6f9c.firebaseapp.com",
databaseURL: "https://******-b6f9c.firebaseio.com",
projectId: "*******-b6f9c",
storageBucket: "*****-b6f9c.appspot.com",
messagingSenderId: "333638181210"
};
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging.usePublicVapidKey("BE0MvVZ0zyTYGmeNIdj4A8XZZ50OKaZL90xmXbIVfufcMuPb0lAUC99426aBPrAEPHAWYeMbOTpHbcM3OiySEcs");
messaging.setBackgroundMessageHandler(function (payload) {
console.log('Handling background message ', payload);
return self.registration.showNotification(payload.notification.title, {
body: payload.notification.body
});
});

This was a bug in Firebase SDK. A thorough explanation of the issue is in the PR here.
The fix will be in version 5.0.2 of the SDK. It should be out later today or tomorrow.
If you don't want to update, then a workaround is to clear your application data. This will force the SDK to regenerate your token. You can do this in Chrome by opening the Developer Console, going to the Application tab, selecting "Clear storage" from the menu on the left, and clicking the "Clear site data" button at the bottom.

Related

Phoneauthprovider is not a function firebase react-native

Hi everybody im making a app using react-native and fire base im have this initial config at firebase config :
import firebase from 'firebase/app';
import 'firebase/auth';
import Constants from 'expo-constants';
// Firebase Config
// Initialize Firebase
export const firebaseConfig = {
apiKey: Constants?.manifest?.extra?.apiKey,
authDomain: Constants?.manifest?.extra?.authDomain,
projectId: Constants?.manifest?.extra?.projectId,
storageBucket: Constants?.manifest?.extra?.storageBucket,
messagingSenderId: Constants?.manifest?.extra?.messagingSenderId,
appId: Constants?.manifest?.extra?.appId
};
let Firebase
if (firebase.apps.length === 0) {
console.log('hello world')
Firebase = firebase.initializeApp(firebaseConfig);
}
export default Firebase;
And im triyng to call this method:
const loginUser = async() => {
switch(loginType){
case 0:
break;
case 1:
if (typeof(verificationId) == 'string') {
setLoading(true)
try {
const credential = new Firebase.auth.PhoneAuthProvider.credential(
verificationId,
verificationCode
);
await Firebase.auth.signInWithCredential(credential);
showMessage({ text: 'Phone authentication successful 👍' });
} catch (err) {
setLoading(false)
showMessage({ text: `Error: ${err.message}`, color: 'red' });
}
} else {
try {
const phoneProvider = Firebase.auth.PhoneAuthProvider();
const verificationId = await phoneProvider.verifyPhoneNumber(
phoneNumber,
recaptchaVerifier.current
);
setVerificationId(verificationId);
showMessage({
text: 'Verification code has been sent to your phone.',
});
} catch (err) {
showMessage({ text: `Error: ${err.message}`, color: 'red' });
}
}
break;
}
}
When im try to call my 'phone Login method' react-native show me this message:
im use this guide for how to configure the enviroment:
https://blog.jscrambler.com/how-to-integrate-firebase-authentication-with-an-expo-app
but using phone verification with recaptcha im not found the problem i believe the problem its in my implementation but in not found nothing
Thanks for the answers
I see you're trying to implement phone auth using firebase and I personally had success doing that using this:
async function signInWithPhoneNumber(phoneNumber) {
//1. Have the user input thei phone number into a TextInput and pass it to this function
//2. Have a confirm useState to make sure the verification code was sent successfully by firebase
//3. Check for the confirm state in the main component and show the user another TextInput to enter the verification code if confirm has a value
await firebase.auth()
.signInWithPhoneNumber(phoneNumber)
.then(confirmation => {
setConfirm(confirmation)
})
.catch(e => {
Alert.alert('Error sending verification code to this phone number')
})
}
async function confirmCode(code) {
//1. Have the code the user entered through the TextInput pass through here and call the below function
try {
let validation = await confirm?.confirm(code)
if (validation) console.log('correct code.')
} catch (error) {
Alert.alert('Invalid code.')
}
}
You're importing your own Firebase object, which is an instance of FirebaseApp. The PhoneAuthProvider class is not defined on FirebaseApp, but rather is in the (static) firebase.auth namespace.
So you either need to also import the regular Firebase Auth SDK into your code, instead of just your own Firebase object, or you can attach the firebase.authnamespace to yourFirebase` object and use it from there with:
...
if (firebase.apps.length === 0) {
console.log('hello world')
Firebase = firebase.initializeApp(firebaseConfig);
Firebase.auth = firebase.auth;
}
export default Firebase;

Did anything major change that my Cloud functions stopped working with flutter web?

I used to have Firebase Cloud Functions running. But after refactoring my whole codebase to sound null safety, cloud functions stopped working (Sadly, I cannot reproduce at which point in the timeline)..
pubspec.yaml
dependencies:
flutter:
sdk: flutter
firebase_core: ^1.0.2
firebase_auth: ^1.0.1
cloud_firestore: ^1.0.4
cloud_functions: ^1.1.0
...
web/index.html
...
<script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-functions.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-firestore.js"></script>
<script>
// Your web app's Firebase configuration
var firebaseConfig = {
apiKey: "<myApiKey>",
authDomain: "<my-project>.firebaseapp.com",
databaseURL: "https://<my-project>.firebaseio.com",
projectId: "<my-project>",
storageBucket: "<my-project>.appspot.com",
messagingSenderId: "<myMessageSenderId>",
appId: "<myAppId>"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.functions().useFunctionsEmulator("http://10.0.2.2:5001");
</script>
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
functions/index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const { UserPropertyValue } = require('firebase-functions/lib/providers/analytics');
admin.initializeApp();
exports.setRoles = functions.https.onCall((data, context) => {
let userId = null;
let userCustomClaimsAdmin = false;
let userCustomClaimsEditor = false;
// get user and update custom claim
return admin.auth().getUserByEmail(data.email).then(user => {
userId = user.uid;
const currentCustomClaims = (user.customClaims == undefined) ? {} : user.customClaims;
switch (data.role) {
case 'admin':
currentCustomClaims.admin = (data.permission == 'grant') ? true : false;
break;
case 'editor':
currentCustomClaims.editor = (data.permission == 'grant') ? true : false;
break;
default:
return;
}
userCustomClaimsAdmin = currentCustomClaims.admin;
userCustomClaimsEditor = currentCustomClaims.editor;
return admin.auth().setCustomUserClaims(userId,
currentCustomClaims
);
}).then(() => {
// Update User record in Firestore
return admin.firestore().collection("users").doc(userId).update({
isAdmin: userCustomClaimsAdmin,
isEditor: userCustomClaimsEditor,
});
}).then(() => {
return {
message: 'Success'
}
})
.catch(err => {
console.log(err.toString());
});
});
Finally I call the function with:
...
final HttpsCallable setRoleCallable = FirebaseFunctions.instance
.httpsCallable('setRoles',
options:
HttpsCallableOptions(timeout: const Duration(seconds: 10)));
...
try {
final HttpsCallableResult result = await setRoleCallable.call(
<String, dynamic>{
'email': "<emailOfUserToBeChanged>",
'role': "<selectedRole>",
'permission': "<givenAccess>"
},
);
print(result.data);
} on FirebaseFunctionsException catch (e) {
print('caught firebase functions exception');
print(e.code);
print(e.message);
print(e.details);
} catch (e) {
print('caught generic exception');
print(e);
}
That call (emulated functions at localhost resp. 10.0.2.2) ends in
caught firebase functions exception
internal
internal
null
Did anything change in the meantime that I have missed? I could not find anything regarding this topic within the Firebase documentation.
Perhaps it might be a little change at some point that I did not recognize yet..
Well, a major change with Cloud Functions is that you now have to have the paid Firebase plan to use cloud functions as they sadly removed Cloud Functions from the free tier.
In your Cloud Function you don't wait for the asynchronous operations to complete before sending back the response. See the doc for more details on this key aspect.
The tricky thing is that it generates some "erratic" behaviour (sometimes it works, sometimes not) that can be explained as follows:
In some cases, your Cloud Function is terminated before the asynchronous operations are completed, as explained in the doc referred to above.
But, in some other cases, it may be possible that the Cloud Functions platform does not immediately terminate your CF, giving enough time for the asynchronous operations to be completed.
So you have the impression that "Cloud functions stopped working with flutter web" while, actually, sometimes it works and some other times not...
In addition, note that the setCustomUserClaims() method returns a Promise<void> not a user, therefore you need to keep a set of global variables for the userId and the claims in order to pass it from one then() block to the other.
So the following should do the trick (untested):
exports.setRoles = functions.https.onCall((data, context) => {
console.log('user to change email: ' + data.email);
let userId = null;
let userCustomClaimsAdmin = false;
let userCustomClaimsEditor = false;
// get user and update custom claim
return admin.auth().getUserByEmail(data.email)
.then(user => {
userId = user.uid; // the setCustomUserClaims() method returns a Promise<void> not a user !!
const currentCustomClaims = (user.customClaims == undefined) ? {} : user.customClaims;
switch (data.role) {
case 'admin':
currentCustomClaims.admin = (data.permission == 'grant') ? true : false;
break;
case 'editor':
currentCustomClaims.editor = (data.permission == 'grant') ? true : false;
break;
default:
return;
break;
}
// Here you need to adapt the value of userCustomClaimsAdmin and userCustomClaimsEditor
userCustomClaimsAdmin = ...
userCustomClaimsEditor = ...
// See return below !!!!
return admin.auth().setCustomUserClaims(user.uid,
currentCustomClaims
);
})
.then(() => {
// See return below !!!!
return admin.firestore().collection("users").doc(userId).update({
isAdmin: (userCustomClaimsAdmin) ? user.customClaims.admin : false,
isEditor: (userCustomClaimsEditor) ? user.customClaims.editor : false,
});
})
.then(() => {
return {
message: 'Success'
}
})
.catch(err => {
console.log(err.toString());
// !!!! See the doc: https://firebase.google.com/docs/functions/callable#handle_errors
});
});
Well, I am developing a flutter-web project. I use cloud-functions, not cloud-functions-web.
In my main.dart the directive to use the functions emulator was missing:
...
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseFunctions.instance
.useFunctionsEmulator(origin: 'http://localhost:5001'); // this was missing
runApp(MyApp());
...
}
It used to work, as I already had that directive in my index.html
...
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.functions().useFunctionsEmulator("http://10.0.2.2:5001");
...
Nevertheless it works now.

Messaging: We are unable to register the default service worker. Failed to register a ServiceWorker

This is my code based on tutorial from firebase for web push notification:
<script type="text/javascript">
// Initialize Firebase
var config = {
apiKey: ".....................",
authDomain: "push-test-8e36f.firebaseapp.com",
databaseURL: "................",
projectId: "................",
storageBucket: "..................",
messagingSenderId: "SOME SENDER ID"
};
let a = firebase.initializeApp(config);
console.log(a);
</script>
<script type="text/javascript">
async function askForPermissioToReceiveNotifications(siteName) {
const messaging = firebase.messaging();
await messaging.requestPermission();
const token = await messaging.getToken();
console.log('user token: ' + token);
}
Notification.requestPermission().then(function (result) {
if (result === 'granted') {
askForPermissioToReceiveNotifications('somesite');
return;
} else {
console.log('The permission request was dismissed.');
return;
}
});
</script>
However, I always face with the problem involve in service worker, in every tutorial dont mention about that.
Any recommendations to fix this issue?

Error: No access, refresh token or API key is set

I'm trying to make an app in Node to access my google calendar, so I followed the steps at https://developers.google.com/calendar/quickstart/nodejs but I'm getting Error: Error: No access, refresh token or API key is set..
Yes I have created the credentials.
Yes I have downloaded the json, renamed to client_secret.json and added to the application folder.
Here is the code:
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
const OAuth2Client = google.auth.OAuth2;
const SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'];
const TOKEN_PATH = './client_secret.json';
try {
const content = fs.readFileSync('client_secret.json');
authorize(JSON.parse(content), listEvents);
} catch (err) {
return console.log('Error loading client secret file:', err);
}
function authorize(credentials, callback) {
const {client_secret, client_id, redirect_uris} = credentials.installed;
let token = {};
const oAuth2Client = new OAuth2Client(client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
try {
token = fs.readFileSync(TOKEN_PATH);
} catch (err) {
return getAccessToken(oAuth2Client, callback);
}
oAuth2Client.setCredentials(JSON.parse(token));
callback(oAuth2Client);
}
function getAccessToken(oAuth2Client, callback) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return callback(err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
try {
fs.writeFileSync(TOKEN_PATH, JSON.stringify(token));
console.log('Token stored to', TOKEN_PATH);
} catch (err) {
console.error(err);
}
callback(oAuth2Client);
});
});
}
function listEvents(auth) {
const calendar = google.calendar({version: 'v3', auth});
calendar.events.list({
calendarId: 'primary',
timeMin: (new Date()).toISOString(),
maxResults: 10,
singleEvents: true,
orderBy: 'startTime', }, (err, {data}) => {
if (err) return console.log('The API returned an error: ' + err);
const events = data.items;
if (events.length) {
console.log('Upcoming 10 events:');
events.map((event, i) => {
const start = event.start.dateTime || event.start.date;
console.log(`${start} - ${event.summary}`);
});
} else {
console.log('No upcoming events found.');
}
});
}
Any ideas?
Can you confirm as following points again?
The files of const TOKEN_PATH = './client_secret.json'; and const content = fs.readFileSync('client_secret.json'); are the same.
Please modify from const TOKEN_PATH = './client_secret.json'; to const TOKEN_PATH = './credentials.json';, and run again.
By this, client_secret.json that you downloaded has already might be overwritten. So please also confirm this.
When an error occurs even if above modification was done, please confirm the version of googleapis. Because it has been reported that googleapis with v25.0.0 - v30.0.0. has some bugs for some APIs.
If you think a bug for the error, please modify the version of googleapis to v24.0.0. The error may be removed.
References :
How do I update my google sheet in v4?
Create a gmail filter with Gmail API nodejs, Error: Filter doesn't have any criteria
Insufficient Permission when trying to create a folder on Google Drive via API(v3)
Youtube Data API V3 - Error fetching video with google.youtube.videos.list()
Google drive API - Cannot read property 'OAuth2' of undefined
How to run a Google App Script using Google API Service Library (Node.js)
If these points were not useful for your situation, I'm sorry.

Fire-base web notification not received while no errors

I am working with web app in which I want to integrate Firebase Notifications but after I setup all the requirements I tried to use Firebase notification composer to test it, I got no errors and the status of the message was completed but I received nothing neither on background nor in foreground.
here is my code
index.html
<script src="https://www.gstatic.com/firebasejs/4.10.1/firebase.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: "MY_API_KEY",
authDomain: "app.firebaseapp.com",
databaseURL: "https://app.firebaseio.com",
projectId: "app",
storageBucket: "app.appspot.com",
messagingSenderId: "MY_SENDER_ID"
};
firebase.initializeApp(config);
var messaging = firebase.messaging();
messaging.usePublicVapidKey("BLWwgk4yFuoNHdPDccuDnXYmhxZA8kwpWArWaE3t7njDT90-30dcWlJIhFbXxMpfXczcvtU8AvMf_F1EJg8Qy");
messaging.requestPermission().then(function(res) {
console.log('test')
messaging.getToken().then(function(res){
console.log(res)
})
})
messaging.onTokenRefresh(function() {
messaging.getToken()
.then(function(refreshedToken) {
console.log('Token refreshed.');
})
.catch(function(err) {
console.log('Unable to retrieve refreshed token ', err);
});
});
messaging.onMessage(function(payload) {
console.log("Message received. ", payload);
// ...
});
</script>
firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js');
firebase.initializeApp({
'messagingSenderId': 'MY_SENDER_ID'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
const notificationTitle = 'Background Message Title';
const notificationOptions = {
body: 'Background Message body.',
icon: '/firebase-logo.png'
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
status of Firebase notification composer
Notes:
no errors on browser console.
no errors on Firebase console.
i had the same problem then i figured out that the version of firebase im using in the foreground is different than the version in sw, so i changed to the same version i use in the foreground and the problem is solved. Hope this help
I had the exact same problem. The problem was not in my front-end code at all but in the requests sent from firebase console. I would suggest you use Postman or your own backend to send a request to see if it works.
Heres a quick demo of my postman request -
method: POST
url : https://fcm.googleapis.com/fcm/send
Headers :
"Content-Type": "application/json",
"Authorization": (Your server key which is found in Cloud messaging settings in firebase console) Edit: Make sure to add "key=" before your server key. Example - "Authorization" : "key=AAAA7_.......QRIM"
Body:
"to" : (Your front end app token),
"data" : {
"body" : "Test message",
"title" : "Test title"
}
Hope this helps

Resources