Firebase Cloud messaging NodeJS PROJECT_NOT_PERMITTED - node.js

I am trying to send a push notification from my nodejs app to my android app using firebase-admin
I followed the instructions as follows
import firebaseAdmin from 'firebase-admin';
import serviceAccount from '../../common/constants/pushKey.json';
firebaseAdmin.initializeApp({
credential: firebaseAdmin.credential.cert(serviceAccount),
});
and the pushKey is the json generated from
project settings -> service accounts -> firebase admin sdk -> generate new private key
However when calling
firebaseAdmin.messaging().sendToDevice(token, payload)
I get the following error
<TITLE>PROJECT_NOT_PERMITTED</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>PROJECT_NOT_PERMITTED</H1>
<H2>Error 401</H2>
</BODY>
</HTML>
". Status code: 401.
at FirebaseMessagingError.<anonymous> (/Users/chris/Documents/ZebedeeApps/booty/node_modules/core-js/internals/wrap-error-constructor-with-cause.js:37:62)
at FirebaseMessagingError.Error (/Users/chris/Documents/ZebedeeApps/booty/node_modules/core-js/modules/es.error.cause.js:28:43)
at FirebaseMessagingError.FirebaseError [as constructor] (/Users/chris/Documents/ZebedeeApps/booty/node_modules/firebase-admin/lib/utils/error.js:44:28)
at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/Users/chris/Documents/ZebedeeApps/booty/node_modules/firebase-admin/lib/utils/error.js:90:28)
at new FirebaseMessagingError (/Users/chris/Documents/ZebedeeApps/booty/node_modules/firebase-admin/lib/utils/error.js:279:16)
at Object.createFirebaseError (/Users/chris/Documents/ZebedeeApps/booty/node_modules/firebase-admin/lib/messaging/messaging-errors-internal.js:57:12)
at /Users/chris/Documents/ZebedeeApps/booty/node_modules/firebase-admin/lib/messaging/messaging-api-request-internal.js:79:51
at processTicksAndRejections (internal/process/task_queues.js:93:5) {
errorInfo: {
code: 'messaging/authentication-error',
message: 'An error occurred when trying to authenticate to the FCM servers. Make sure the credential used to authenticate this SDK has the proper permissions. See https://firebase.google.com/docs/admin/setup for setup instructions. Raw server response: "<HTML>\n' +
'<HEAD>\n' +
'<TITLE>PROJECT_NOT_PERMITTED</TITLE>\n' +
'</HEAD>\n' +
'<BODY BGCOLOR="#FFFFFF" TEXT="#000000">\n' +
'<H1>PROJECT_NOT_PERMITTED</H1>\n' +
'<H2>Error 401</H2>\n' +
'</BODY>\n' +
'</HTML>\n' +
'". Status code: 401.'
},
codePrefix: 'messaging'
}
Any ideas?

Needs to enable Cloud Messaging API (Legacy) : https://stackoverflow.com/a/72901323/6890201
Firebase configuration

Related

samlify ERR_EXCEPTION_OF_ASSERTION_DECRYPTION

When I trying to make a Okta validation with a SAML using samlify on my project
import * as samlify from 'samlify';
import * as fs from 'fs';
import * as validator from '#authenio/samlify-node-xmllint';
const binding = samlify.Constants.namespace.binding;
samlify.setSchemaValidator(validator);
const filePathIdpMeta = __dirname + '/../' + 'meta1123/meta/Testv2-xml-idp.xml';
const filePathSPMeta = __dirname + '/../' + 'meta1123/meta/Testv2-xml-sp.xml';
const filePathSpKey = __dirname + '/../' + 'meta1123/key/encryptKey.pem';
console.info("----PATHS----")
console.info(filePathIdpMeta);
console.info(filePathSPMeta);
console.info(filePathSpKey);
const idp = samlify.IdentityProvider({
metadata: fs.readFileSync(filePathIdpMeta),
messageSigningOrder: 'sign-then-encrypt',
isAssertionEncrypted: true,
wantAuthnRequestsSigned: false
})
Currently I getting the following error:
http://www.w3.org/2001/04/xmlenc#rsa-1_5 is no longer recommended due to security reasons. Please deprecate its use as soon as possible.
Error: Not found: child not in parent
at assertPreInsertionValidity1to5 (/home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/node_modules/#xmldom/xmldom/lib/dom.js:798:9)
at _insertBefore (/home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/node_modules/#xmldom/xmldom/lib/dom.js:938:2)
at Document.replaceChild (/home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/node_modules/#xmldom/xmldom/lib/dom.js:1054:3)
at /home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/src/libsaml.ts:681:15
at Object.decrypt (/home/my_local_path/Service_Providers/sp-oauth0/node_modules/#authenio/xml-encryption/lib/xmlenc.js:202:16)
at /home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/src/libsaml.ts:670:23
at new Promise (<anonymous>)
at Object.decryptAssertion (/home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/src/libsaml.ts:655:14)
at /home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/src/flow.ts:225:34
at step (/home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/build/src/flow.js:33:23) {
code: 8
}
[FATAL] when parsing login response sent from okta Error: ERR_EXCEPTION_OF_ASSERTION_DECRYPTION
at /home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/src/libsaml.ts:675:27
at Object.decrypt (/home/my_local_path/Service_Providers/sp-oauth0/node_modules/#authenio/xml-encryption/lib/xmlenc.js:214:12)
at /home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/src/libsaml.ts:670:23
at new Promise (<anonymous>)
at Object.decryptAssertion (/home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/src/libsaml.ts:655:14)
at /home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/src/flow.ts:225:34
at step (/home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/build/src/flow.js:33:23)
at Object.next (/home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/build/src/flow.js:14:53)
at fulfilled (/home/my_local_path/Service_Providers/sp-oauth0/node_modules/samlify/build/src/flow.js:5:58)
at processTicksAndRejections (internal/process/task_queues.js:93:5)

Google Sign-In idToken with createSessionCookie causing error - there is no user record corresponding to the provided identifier

Stack:
Google Sign-in (Vanilla JS - client side),
Firebase Functions (ExpressJS)
Client-Side:
My Firebase function express app uses vanilla javascript on the client side. To authenticate I am making use of Firebase's Google SignIn feature client-side javascript web apps, found here.
// Firebase setup
var firebaseConfig = {
apiKey: "AIza...",
authDomain: "....firebaseapp.com",
databaseURL: "https://...-default-rtdb.firebaseio.com",
...
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);
function postIdTokenToSessionLogin(idToken, csrfToken) {
return axios({
url: "/user/sessionLogin", < ----- endpoint code portion found below
method: "POST",
data: {
idToken: idToken,
csrfToken: csrfToken,
},
});
}
// ...
// On sign-in click
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth()
.signInWithPopup(provider)
.then(async value => {
const idToken = value.credential.idToken;
const csrfToken = getCookie('_csrf');
return postIdTokenToSessionLogin(idToken, csrfToken);
}).then(value => {
window.location.assign("/user/dashboard")
}).catch((error) => {
alert(error.message);
});
Note I am using value.credential.idToken (most sources imply to use this, but haven't found an example saying use this specifically)
Directly after calling signInWithPopup, a new account is created in my Firebase Console Authentication matching the gmail account that was just signed in.
Server-side:
Once I authenticate, I create an axios request passing in the {user}.credential.idToken and following the server-side setup here (ignoring the CSRF - this just doesn't want to work).
In creating the session, I use the following code in my firebase functions express app, the endpoint which is router.post('/sessionLogin', (req, res) => (part of /user route prefix):
// Set session expiration to 5 days.
const expiresIn = 60 * 60 * 24 * 5 * 1000;
const idToken = req.body.idToken.toString(); // eyJhbGciOiJSUzI1NiIsImt...[936]
admin
.auth()
.createSessionCookie(idToken, {expiresIn}) < ----------- Problem line
.then((sessionCookie) => {
// Set cookie policy for session cookie.
const options = {maxAge: expiresIn, httpOnly: true, secure: true};
res.cookie('session', sessionCookie, options);
res.end(JSON.stringify({status: 'success'}));
}).catch((error) => {
console.error(error);
res.status(401).send('UNAUTHORIZED REQUEST!');
});
On the createSessionCookie call, I get the following error & stack trace:
Error: There is no user record corresponding to the provided identifier.
at FirebaseAuthError.FirebaseError [as constructor] (C:\Users\CybeX\Bootstrap Studio Projects\future-design\functions\node_modules\firebase-admin\lib\utils\error.js:44:28)
at FirebaseAuthError.PrefixedFirebaseError [as constructor] (C:\Users\CybeX\Bootstrap Studio Projects\future-design\functions\node_modules\firebase-admin\lib\utils\error.js:90:28)
at new FirebaseAuthError (C:\Users\CybeX\Bootstrap Studio Projects\future-design\functions\node_modules\firebase-admin\lib\utils\error.js:149:16)
at Function.FirebaseAuthError.fromServerError (C:\Users\CybeX\Bootstrap Studio Projects\future-design\functions\node_modules\firebase-admin\lib\utils\error.js:188:16)
at C:\Users\CybeX\Bootstrap Studio Projects\future-design\functions\node_modules\firebase-admin\lib\auth\auth-api-request.js:1570:49
at processTicksAndRejections (internal/process/task_queues.js:93:5)
This is part of the sign-in flow with a existing Gmail account.
What is causing this?
After many hours of searching, Googling - I have seen the light.
For some additional context, this error featured heavily in my struggle "Firebase ID token has invalid signature." - I will get to that in a second.
Further, another issue I also faced was using a local auth emulator for web client-side (javascript), see this for setup.
TL;DR to solve the immediate problem
Client-side remained largely the same, however the documentation provided by Firebase was inaccurate/misleading - thanks to this post, I found the solution. Thus, it follows...
Which is the ID Token? (Client-side):
The examples from here (to allow signInWithPopup), the response (if successful) results in
...
.signInWithPopup(provider)
.then((result) => {
/** #type {firebase.auth.OAuthCredential} */
var credential = result.credential;
// This gives you a Google Access Token. You can use it to access the Google API.
var token = credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
})
Looking for an idToken, I found one using result.credential.idToken but no where on the internet on if this was infact the correct token to use.
I ran into this error using the provided idToken above:
Firebase ID token has incorrect "aud" (audience) claim. Expected
"[insert your **projectId**]" but got
"59895519979-2l78aklb7cdqlth0eob751mdm67kt301.apps.googleusercontent.com".
Make sure the ID token comes from the same Firebase project as the
service account used to authenticate this SDK.
Trying other tokens like result.credential.accessToken responded with various verification errors - what to do?
Mention earlier, this solution on Github suggested to use firebase.auth().currentUser.getIdToken() AFTER you have signed in. An example (building on my previous code) is to do the following:
...
.signInWithPopup(provider)
.then((result) => {
// current user is now valid and not null
firebase.auth().currentUser.getIdToken().then(idToken => {
// send this ID token to your server
const csrfToken = getCookie('_csrf');
return postIdTokenToSessionLogin(idToken, csrfToken);
})
})
At this point, you can verify your token and createSessionCookies to your heart's desire.
BUT, a secondary issue I unknowingly created for myself using the Authentication Emulator.
To setup for client-side use:
var auth = firebase.auth();
auth.useEmulator("http://localhost:9099");
To setup for hosting your firebase functions app (assuming you are using this with e.g. nodejs + express, see this for setup, ask in comments, can provide more details if needed)
Using Authentication Emulator caused the following errors AFTER using the above mentioned "fix". Thus, DO NOT RUN the local authentication emulator (with Google sign-in of a valid Google account) as you will consistently get.
Firebase ID token has invalid signature. See
https://firebase.google.com/docs/auth/admin/verify-id-tokens for
details on how to retrieve an ID token
You can use all your local emulators, but (so far in my experience) you will need to use an online authenticator.

Firestore admin in Node.js Missing or insufficient permissions

I am trying to access Firestore using Firebase Admin on Node.js v8.4.0 in a Firebase Cloud Function running locally using firebase functions:shell on Windows 10.
firebase -v 4.2.1
"dependencies": {
"firebase-admin": "^6.0.0",
"firebase-functions": "^2.0.5"
}
After attempting to use firebase admin from my apps code, I attempted to run the quick start example in https://github.com/firebase/functions-samples/blob/master/quickstarts/uppercase-firestore/functions/index.js.
This is the actual code run:
'use strict';
// [START all]
// [START import]
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp({})
// [END import]
// [START addMessage]
// Take the text parameter passed to this HTTP endpoint and insert it into the
// Realtime Database under the path /messages/:documentId/original
// [START addMessageTrigger]
exports.addMessage = functions.https.onRequest((req, res) => {
// [END addMessageTrigger]
// Grab the text parameter.
const original = req.query.text;
// [START adminSdkAdd]
// Push the new message into the Realtime Database using the Firebase Admin SDK.
return admin.firestore().collection('messages').add({original: original}).then((writeResult) => {
// Send back a message that we've succesfully written the message
return res.json({result: `Message with ID: ${writeResult.id} added.`});
});
// [END adminSdkAdd]
});
// [END addMessage]
// [START makeUppercase]
// Listens for new messages added to /messages/:documentId/original and creates an
// uppercase version of the message to /messages/:documentId/uppercase
// [START makeUppercaseTrigger]
exports.makeUppercase = functions.firestore.document('/messages/{documentId}')
.onCreate((snap, context) => {
// [END makeUppercaseTrigger]
// [START makeUppercaseBody]
// Grab the current value of what was written to the Realtime Database.
const original = snap.data().original;
console.log('Uppercasing', context.params.documentId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
// Setting an 'uppercase' sibling in the Realtime Database returns a Promise.
return snap.ref.set({uppercase}, {merge: true});
// [END makeUppercaseBody]
});
// [END makeUppercase]
// [END all]
However, I still get the permission denied error.
This is the output I get:
firebase > makeUppercase({original:'alphabets'},{params:{documentId:'mydoc'}})
'Successfully invoked function.'
firebase > info: User function triggered, starting execution
info: Uppercasing mydoc alphabets
info: Function crashed
info: { Error: 7 PERMISSION_DENIED: Missing or insufficient permissions.
at Object.exports.createStatusError (C:\projects\myproject\functions\node_modules\grpc\src\common.js:87:15)
at Object.onReceiveStatus (C:\projects\myproject\functions\node_modules\grpc\src\client_interceptors.js:1188:28)
at InterceptingListener._callNext (C:\projects\myproject\functions\node_modules\grpc\src\client_interceptors.js:564:42)
at InterceptingListener.onReceiveStatus (C:\projects\myproject\functions\node_modules\grpc\src\client_interceptors.js:614:8)
at callback (C:\projects\myproject\functions\node_modules\grpc\src\client_interceptors.js:841:24)
code: 7,
metadata: Metadata { _internal_repr: {} },
details: 'Missing or insufficient permissions.' }
My security rules are completely open but that did not resolve the error.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
I also thought that this might be an authentication issue so I have tried the following to initialize the app:
1
admin.initializeApp()
2
admin.initializeApp(functions.config().firebase);
3
var serviceAccount = require('path/to/serviceAccountKey.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
});
The last one with my own credentials file and project configuration. All of these attempts still give me the missing permissions error.
Update:
I deployed these functions to the cloud and they seem to be working perfectly but when running locally, I'm still getting a Error:7 Permission Denied.
Update 2:
Set application default credentials using gcloud auth application-default login as per suggestion by #Doug Stevenson. Ensured environment variable GOOGLE_APPLICATION_CREDENTIALS is not set. Attempted the code in 1,2 and 3 above as well as 4 below with no success. Encountered the same error.
4
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "https://myapp-redacted.firebaseio.com"
});
I hope that by now you've already resolved your issue. The same thing just happened to me and I'm sharing what it was in my case with the hope that it will help others.
We manage several firebase projects - productions, dev, staging etc.
we init the admin sdk with this:
let serviceAccount = require("../serviceAccountKey.json");
const databaseUrl = functions.config().environment.databaseurl
const storageBucket = functions.config().environment.storagebucket
const isDev = "true" === functions.config().environment.dev
// if dev environment
if (isDev) {
serviceAccount = require("../serviceAccountKey-dev.json");
}
admin.initializeApp({
projectId: functions.config().environment.projectid,
credential: admin.credential.cert(serviceAccount),
databaseURL: databaseUrl,
storageBucket: storageBucket
});
You know how you need to do
firebase functions:config:get > .runtimeconfig.json
for the emulation to work. Well, my runtimeconfig was containing the wrong configuration. I was loading my serviceAccountKey-dev, but I was trying to access a different project. The second I fixed my runtimeconfig - it worked for me.
I had the same issue and was resolved by going to the cloud console then granting the role Firebase Admin SDK admin service agent to the app engine service account which is in the following format {PROJECT_ID}#appspot.gserviceaccount.com.
Ultimately I just had to run gcloud auth application-default login to make sure I was logged in with the correct Google account.
It is required that the client, the firebase function, to have access to the resource, firebase firestore. Following the least privilege principle you would need to:
Create a role within Google Cloud IAM with the following permissions:
datastore.entities.get, datastore.entities.update.
Also in IAM, create a service account and assign it the recently created role.
Update your firebase function selecting the new service account.
I have not found a way to assign the service account while deploying with firebase-cli. Here a guide for configuring permissions of the functions https://cloud.google.com/functions/docs/securing/function-identity.

Firebase functions logs error working with Cloud Firestore and Node JS

The Firebase functions by Nodejs is not retrieving the results from the data stored in the Cloud Firestore Database. Giving a:
TypeError of Undefined user_id
NodeJS Code
'use-strict'
const admin = require('firebase-admin');
const functions = require('firebase-functions');
const serviceAccount = require("./serviceAccountKey.json");
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.firestore.document("Users/{user_id}/Notifications/{notification_id}").onWrite(event => {
const user_id = event.params.user_id;
const notification_id = event.params.notification_id;
console.console.log("User ID: " + user_id + " | Notification ID: " + notification_id);
});
Error in Logs of Firebase Functions
TypeError: Cannot read property 'user_id' of undefined at
exports.sendNotification.functions.firestore.document.onWrite.event
(/user_code/index.js:10:33) at Object.<anonymous>
(/user_code/node_modules/firebase-functions/lib/cloud-functions.js:112:27)
at next (native) at /user_code/node_modules/firebase-functions/lib/cloud-
functions.js:28:71 at __awaiter (/user_code/node_modules/firebase-
functions/lib/cloud-functions.js:24:12) at cloudFunction
(/user_code/node_modules/firebase-functions/lib/cloud-functions.js:82:36) at
/var/tmp/worker/worker.js:700:26 at process._tickDomainCallback
(internal/process/next_tick.js:135:7)
Database Rules
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true; } } }
It looks like you're using version 1.0 of the firebase-functions module in your Cloud Functions code. The API has changed in 1.0. Please consult the migration guide to understand what changed in 1.0.
There is no longer a params property on the first argument passed to the the callback for an onWrite trigger. It was split into two arguments, a Change, and an EventContext object. The EventContext argument has a params property you should use now.

Failed to fetch a valid Google OAuth2 access token with firebase-admin

I currently cannot read or write data using firebase-admin in nodejs.
Without logging turned on it just hangs forever and never returns an error and never completes.
With logging turned on however it gives the following (it continues to retry on a decaying schedule - but with the same result):
p:0: Browser went online.
0: set {"path":"/server/saving-data/fireblog/users","value":{"alanisawesome":{"date_of_birth":"June 23, 1912","full_name":"Alan Turing"},"gracehop":{"date_of_birth":"December 9, 1906","full_name":"Grace Hopper"}},"zg":null}
p:0: Buffering put: /server/saving-data/fireblog/users
p:0: Making a connection attempt
p:0: Failed to get token: Error: Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2 access token with the following error: "unable to verify the first certificate".
p:0: data client disconnected
p:0: Trying to reconnect in 0ms
0: onDisconnectEvents
When I catch the (internal to firebase-admin) https request I get the following:
{ Error: unable to verify the first certificate
at TLSSocket.<anonymous> (_tls_wrap.js:1094:38)
at emitNone (events.js:86:13)
at TLSSocket.emit (events.js:188:7)
at TLSSocket._finishInit (_tls_wrap.js:616:8)
at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:446:38) code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' }
I have tried generating a new private key from the console but it continues to return the same error. I have also added the require('ssl-root-cas').inject() as suggested elsewhere but it doesn't change the behavior. Also passing the actual json object to the admin.credential.cert function does not change the behavior.
I am currently on node v7.7.4.
Here is the code I have that produces the error:
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.cert('./serviceAccountKey.json'),
databaseURL: 'https://mtg-decks-bc92d.firebaseio.com'
});
// turn on logging
admin.database.enableLogging(true);
const db = admin.database();
const ref = db.ref('server/saving-data/fireblog');
const usersRef = ref.child('users');
usersRef.set({
alanisawesome: {
date_of_birth: 'June 23, 1912',
full_name: 'Alan Turing'
},
gracehop: {
date_of_birth: 'December 9, 1906',
full_name: 'Grace Hopper'
}
}).then(() => {
console.log('set complete');
});
Several other posts recommend adding the root certificates by calling require('ssl-root-cas').inject();. Can you try that?
Example: Error: unable to verify the first certificate in nodejs
I got this error because my local machines time zone was set manually so the token expiration etc was off.

Resources