Does anyone know why Firebase admin auth in node.js doesn't use ADC (Application Default Credentials)? I always have to set GOOGLE_APPLICATION_CREDENTIALS to a credentials file to get auth to work. Everything else (firestore, compute, storage etc.) works fine with ADC.
For instance, this code works only when GOOGLE_APPLICATION_CREDENTIALS is set to a valid credentials file, even though I'm logged into my Firebase project and my gcloud project:
import * as admin from 'firebase-admin'
admin.initializeApp()
async function listAllUsers(users: any[], matchRegex: RegExp, nextPageToken?: string) {
// List batch of users, 1000 at a time.
const listUsersResult = await admin.auth().listUsers(1000, nextPageToken)
.catch(function (error) {
console.log('Error listing users:', error);
});
if (listUsersResult) {
listUsersResult.users.forEach(function (userRecord) {
if (matchRegex.test(userRecord.email || '<none>') ||
matchRegex.test(userRecord.displayName || '<none>') ||
matchRegex.test(userRecord.uid))
users.push(userRecord.toJSON())
});
if (listUsersResult.pageToken) {
// List next batch of users.
console.log(`next batch...`)
listAllUsers(users, matchRegex, listUsersResult.pageToken);
}
}
}
If that env var is not set, I get this error:
Error listing users: FirebaseAuthError: Failed to determine project ID for Auth.
Initialize the SDK with service account credentials or set project ID as an app option.
Alternatively set the GOOGLE_CLOUD_PROJECT environment variable.
But setting GOOGLE_CLOUD_PROJECT is not enough either. When I do that, I get:
Error listing users: FirebaseAuthError: //cloud.google.com/docs/authentication/. Raw server response: "{"error":{"code":403,"message":"Your application has authenticated using end user credentials from the Google Cloud SDK or Google Cloud Shell which are not supported by the identitytoolkit.googleapis.com. We recommend configuring the billing/quota_project setting in gcloud or using a service account through the auth/impersonate_service_account setting. For more information about service accounts and how to use them in your application, see https://cloud.google.com/docs/authentication/.","errors":[{"message":"Your application has authenticated using end user credentials from the Google Cloud SDK or Google Cloud Shell which are not supported by the identitytoolkit.googleapis.com. We recommend configuring the billing/quota_project setting in gcloud or using a service account through the auth/impersonate_service_account setting. For more information about service accounts and how to use them in your application, see https://cloud.google.com/docs/authentication/.","domain":"usageLimits","reason":"accessNotConfigured","extendedHelp":"https://console.developers.google.com"}],"status":"PERMISSION_DENIED"}}"
at FirebaseAuthError.FirebaseError [as constructor] (/c/dss/Product/Horizon/horizon/packages/renderer/node_modules/firebase-admin/lib/utils/error.js:43:28)
at FirebaseAuthError.PrefixedFirebaseError [as constructor] (/c/dss/Product/Horizon/horizon/packages/renderer/node_modules/firebase-admin/lib/utils/error.js:89:28)
at new FirebaseAuthError (/c/dss/Product/Horizon/horizon/packages/renderer/node_modules/firebase-admin/lib/utils/error.js:148:16)
at Function.FirebaseAuthError.fromServerError (/c/dss/Product/Horizon/horizon/packages/renderer/node_modules/firebase-admin/lib/utils/error.js:187:16)
at /c/dss/Product/Horizon/horizon/packages/renderer/node_modules/firebase-admin/lib/auth/auth-api-request.js:1490:49
As I said though, all other Firebase admin features seem to work fine with ADC; they automatically pick up the current project and my logged in account.
Related
This question already has answers here:
Firebase Cloud Functions Firestore Trigger produces: Error: 7 PERMISSION_DENIED: Missing or insufficient permissions
(9 answers)
Closed 10 months ago.
I'm attempting to use Google Cloud Functions to interact with a Google Firestore collection. Going through the documentation, it seems like the suggested library to use is: https://www.npmjs.com/package/#google-cloud/firestore
the documentation on the library indicates it should only be used in a secure environment (like Cloud Functions) and that it would take advantage of ADC ( Application Default Credentials ). However when I implement it in a cloud function I'm getting the following response from the library call to firestore.get()
{
"code": 7,
"details": "Missing or insufficient permissions.",
"metadata": {}
}
For the sake of this article lets say the project id is "mobile-site"
I made a firestore collection:
"site"
which has a single document:
{
"hi": "there"
}
and a cloud function to call which attempts to get the document:
const firestore_1 = require("#google-cloud/firestore");
const projectId = 'mobile-site';
const firestore = new firestore_1.Firestore({ projectId });
/**
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
exports.helloWorld = (req, res) => {
return TestStuff().then(v => {
return res.status(200).send(v);
}).catch(reason => {
return res.status(500).send(reason)
})
};
//
function TestStuff() {
const collection = firestore.collection('site');
return collection.get().then(query => {
// this code is never invoked
return {query, version: '1.0.1'}
});
}
As a troubleshooting step, I did the exact same thing above on a personal account, and everything works swimmingly. So its just my company's enterprise solution that causes an issue.
Before you say "check the Firestore Security Rules", this sdk circumvents the firestore rules since its in network and attached to a service account. Speaking of which, here is the service account role/permissions, I've added quite a few in an effort to resolve the issue, so some may be completely unrelated, will use an example name:
IAM & Admin
example-service-account#appspot.gserviceaccount.com
Editor
Service Account User
Viewer
Cloud Functions
example-service-account#appspot.gserviceaccount.com
Cloud Functions Admin
Cloud Functions Invoker
Cloud Functions Service Agent
Editor
Firebase Admin
Any help or suggestions to resolve or troubleshoot further are appreciated.
Turns out the issue is there are multiple IAM "principal" role assignment areas.
Previously I had verified the service account settings here:
https://console.cloud.google.com/
Menu > IAM & ADMIN > Service Accounts > ( Clicking on the default service account ) > Permissions
However the location that resolved the issue for me was found here:
https://console.cloud.google.com/
Menu > IAM & ADMIN > IAM
In my case the service account in question wasn't even present, so it had to be added. My administrators attached the following roles:
Firebase Admin SDK Administrator Service Agent
Service Account Token Creator
And now the functions work
User "Marc Anthony B" pointed me to
Firebase Cloud Functions Firestore Trigger produces: Error: 7 PERMISSION_DENIED: Missing or insufficient permissions
which was extremely close but not exactly the same since I am using a different library and the Role assignment is different.
I am struggling to connect to the emulated Firebase Auth service via the Firebase Admin SDK. I cut down the code to really make the problem stand out, and hope someone can help.
This is the code of the test.js I run (in NodeJS):
// Someone said these two lines should allow the firebase-admin
// SDK to connect to the emulators, but... no.
process.env['GCLOUD_PROJECT'] = 'my-firebase-project-id'
process.env['FIRESTORE_EMULATOR_HOST'] = 'localhost:8080'
const admin = require('firebase-admin')
const app = admin.initializeApp()
const auth = app.auth()
console.log('I have an auth service object')
auth.listUsers().then(users => console.log(users))
I run the emulators like this:
firebase emulators:start --only auth
When I run the test.js file, I get this:
PS C:\...\functions> node .\test.js
I have an auth service object
(node:18232) UnhandledPromiseRejectionWarning: Error: Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2 access token with the following error: "Error fetching access token: Error while making request: getaddrinfo EAI_AGAIN metadata.google.internal. Error code: EAI_AGAIN".
at FirebaseAppError.FirebaseError [as constructor] (C:\...\functions\node_modules\firebase-admin\lib\utils\error.js:44:28)
at FirebaseAppError.PrefixedFirebaseError [as constructor] (C:\...\functions\node_modules\firebase-admin\lib\utils\error.js:90:28)
at new FirebaseAppError (C:\...\functions\node_modules\firebase-admin\lib\utils\error.js:125:28)
at C:\...\functions\node_modules\firebase-admin\lib\app\firebase-app.js:87:19
at processTicksAndRejections (internal/process/task_queues.js:97:5)
I run this on Windows with the following versions of firebase:
"firebase-admin": "^10.0.2",
"firebase-functions": "^3.18.1",
I read about getting a secret credentials key and adding its path like this:
process.env['GOOGLE_APPLICATION_CREDENTIALS'] = 'C:\\...\\functions\\.runtimekey.json'
And that 'works' in as much as I then can access the real cloud auth instance (as long as the emulators is off) but that isn't what I want. I want to connect firebase-admin and get a list of users in the emulated Auth instance.
Many thanks for any help you can offer!
Set the environment variable FIREBASE_AUTH_EMULATOR_HOST
export FIREBASE_AUTH_EMULATOR_HOST=localhost:9099
Do not include the protocol scheme (i.e http/https)
Or in your case:
process.env['FIREBASE_AUTH_EMULATOR_HOST'] = 'localhost:9099'
Then you can initialise the app as per normal
admin.initializeApp()
Worked for me (after I finally figured out not to include the protocol scheme)
source: https://firebase.google.com/docs/emulator-suite/connect_auth#admin_sdks
Without having changed anything in my Firebase callable functions code, but having re-deployed them, now they suddenly start rejecting all function invocations from my app with the error shown below. I would like NOT to use App Check until I am ready to make the changes needed. How do I stop my callable (https.onCall) Firebase functions from rejecting invalid App Checks, and instead only reject invalid Authentication?
Failed to validate AppCheck token. FirebaseAppCheckError: Decoding App Check token failed. Make sure you passed the entire string JWT which represents the Firebase App Check token.
at FirebaseAppCheckError.FirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:44:28)
at FirebaseAppCheckError.PrefixedFirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:90:28)
at new FirebaseAppCheckError (/workspace/node_modules/firebase-admin/lib/app-check/app-check-api-client-internal.js:187:28)
at /workspace/node_modules/firebase-admin/lib/app-check/token-verifier.js:82:19
at processTicksAndRejections (internal/process/task_queues.js:97:5) {
errorInfo: {
code: 'app-check/invalid-argument',
message: 'Decoding App Check token failed. Make sure you passed the entire string JWT which represents the Firebase App Check token.'
},
codePrefix: 'app-check'
}
Callable request verification failed: AppCheck token was rejected. {"verifications":{"app":"INVALID","auth":"VALID"}}
The code rejecting all requests due to invalid App Check is super simple:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.example = functions.https.onCall((data, context) => {
return "test";
}
Package.json:
"engines": {
"node": "12"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^9.10.0",
"firebase-functions": "^3.14.1"
},
I had the same experience. The docs say that you are supposed to check like this[1]:
if (context.app == undefined) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called from an App Check verified app.')
}
But, this is not the case in my experience, the App Check starts to be enforced immediately the moment you add App Check to your app.
EDIT:
moreover, even without doing any check in my code, I can see this in the logs whenever I call one of my functions:
Callable request verification passed {"verifications":{"auth":"VALID","app":"VALID"}}
So it seems App Check happens automatically, at least in Callable Functions. If you want to bypass AppCheck in one of your functions, you might want to try an HTTP function instead (not Callable).
[1] Source https://firebase.google.com/docs/app-check/cloud-functions
From protocol specification for https.onCall:
Optional [Header]: X-Firebase-AppCheck: The Firebase App Check token provided by the client app making the request. The backend automatically verifies this token and decodes it, injecting the appId in the handler's context. If the token cannot be verified, the request is rejected. (Available for SDK >=3.14.0)
My guess is that calls to your callable function contains an invalid App Check token.
If you haven't configured DeviceCheck or/and App Attest attestation providers on your project but have included the App Check library on your client, your client code may be including a dummy App Check token when calling your function (full details on this github issue).
Firebase team is working through changes to make the experience less confusing. Please follow along in http://github.com/firebase/firebase-functions/issues/967 and https://github.com/FirebaseExtended/flutterfire/issues/6794 for status.
My problem is the same when I run the app on an emulator, after running on a real device is running without any problems. That's what happens to me.
I am trying to auth using emulator with a firebase custom token sent from functions to a web page.
But it returns a HTTP400 from www.googleapis.com when i use it with signInWithCustomToken();.
/** BACKEND SIDE **/
const token = await createFirebaseToken(userid);
res.status(200).send(token);
/** FRONTEND SIDE **/
const res = await axios.post(endpoints.signin,{/* oauth data*/});
await document.fApp.auth().signInWithCustomToken(res.data); /* HTTP400 here */
The custom token is decoded as below:
{
"https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
"iat": 1605605206,
"exp": 1605608806,
"iss": "firebase-auth-emulator#example.com",
"sub": "firebase-auth-emulator#example.com",
"uid": *userid*
}
Is it supposed to send to googleapis.com rather than the local emulator?
I did set firebase web config with authDomain:"localhost:9099", and used powershell env to point GOOGLE_APPLICATION_CREDENTIALS to a service-account.json, throw me a warn on startup though:
Your GOOGLE_APPLICATION_CREDENTIALS environment variable points to ***. Non-emulated services will access production using these credentials. Be careful!
Get also this warn 3 times when i call admin.auth().createUser(), dunno if that's related:
Received service account token strange token not from service-account.json Assuming that it owns project *** my project-id ***
Forgot to set
firebase.auth().useEmulator('http://localhost:9099/');
as stated in the documentation.
Now customToken are sent to the emulator.
I'm building a site with emberfire and using firebase. Everything seems to be set up fine regarding credentials, and I can read/write to the firebase database while developing locally, but every time I try to authenticate via the Google Popup it pops up fine with my Google accounts listed, and when I select my account it errors with this error object:
code: "auth/network-request-failed", message: "A network error (such as timeout, interrupted connection or unreachable host) has occurred."
I assume I'm simply missing a setting somewhere but for the life of me I cannot find it. Here's the code to get the popup and response:
const provider = new firebase.auth.GoogleAuthProvider();
this.firebaseApp.auth().then(auth => {
/* Firebase SDK calls */
auth.signInWithPopup(provider).then(function(response) {
console.log(response);
}).catch(function(error) {
console.log(error);
});
});
I am using EmberJS with emberfire.
In my environment I had host: 'api', and when removing that authentication works, so it must be one of the adapters that is used.