firebase / firebase-admin-node

Firebase Admin Node.js SDK
https://firebase.google.com/docs/admin/setup
Apache License 2.0
1.63k stars 371 forks source link

With preferRest: true option set, I am unable to connect to auth emulator #2016

Open sushantdhiman opened 1 year ago

sushantdhiman commented 1 year ago

Describe your environment

Additional related dependencies

@google-cloud/logging@10.3.2
@google-cloud/pubsub@3.2.1
firebase-admin@11.4.0

Describe the problem

With preferRest: true option set, I am unable to connect to auth emulator

Steps to reproduce:

Here is my firebase initialization code

import { initializeApp, applicationDefault } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';
import { initializeFirestore } from 'firebase-admin/firestore';

export const app = initializeApp({                                                                                                
  credential: applicationDefault(),
  projectId: process.env.GCLOUD_PROJECT,
});

export const auth = getAuth(app);
---export const firestore = initializeFirestore(app);
+++export const firestore = initializeFirestore(app, { preferRest: true });

When I run my application with preferRest: true, I get this error. It seems like emulators are not detected with this option.

Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.
  at GoogleAuth.getApplicationDefaultAsync (/home/node/app/node_modules/google-auth-library/build/src/auth/googleauth.js:209:19)
  at GoogleAuth.getClient (/home/node/app/node_modules/google-auth-library/build/src/auth/googleauth.js:635:17)
  at GrpcClient.createStub (/home/node/app/node_modules/google-gax/src/fallback.ts:280:27)
Caused by: Error: 
  at L0.getAll (/home/node/app/lib/server.js:113:509361)
  at ta.get (/home/node/app/lib/server.js:113:32416)
  at Function.findByIdentifier (/home/node/app/src/database/user.ts:115:44)\n

Application works correctly without preferRest option.

lahirumaramba commented 1 year ago

Hi @sushantdhiman, preferRest option is in firestore so it is a bit strange that you are experiencing issues with the auth API. You might have already done this, but just to confirm did you set the environment variables for Firestore and Auth emulator?

export FIREBASE_AUTH_EMULATOR_HOST="localhost:9099"
export FIRESTORE_EMULATOR_HOST="localhost:8080"
lahirumaramba commented 1 year ago

I am also unable to reproduce this.

Gbuomprisco commented 1 year ago

I am experiencing the same.

From what I can tell, the function refreshTokenNoCache is using the constant below:

 const url = OAuth2Client.GOOGLE_OAUTH2_TOKEN_URL_

By logging the response error, I can see the URL is set to 'https://oauth2.googleapis.com/token'.

When the error occurs in generateServiceStub, the callback function does not exist, which swallows this error and instead reports that callback is undefined. So, to see the actual error, I needed a console.log when catching the error.

NB: I upgraded an existing project that worked OK; the variables above are set correctly. Hope it helps!

sushantdhiman commented 1 year ago

did you set the environment variables for Firestore and Auth emulator?

I can confirm those environment variables are set.

Applications works correctly without preferRest: true, I am also facing this exact issue in another project.

ganey commented 1 year ago

I have this issue too. When you enable preferRest, it seems like the auth emulator tries to setup credentials for the google-gax fallback?

It has the correct address set in the env passed into createStub and customServicePath is true, but this.auth.getClient() errors out.

https://github.com/googleapis/gax-nodejs/blob/main/src/fallback.ts#L280

lahirumaramba commented 1 year ago

Hey everyone, thank you for your patience on this. We have identified the root cause and the team is currently working on a fix.

sushantdhiman commented 1 year ago

@lahirumaramba Can you suggest some workaround while https://github.com/googleapis/gax-nodejs/issues/1409 is open? Perhaps we can mock/stub some methods so it works for tests?

maccman commented 1 year ago

Also running into this.

MarkDuckworth commented 1 year ago

We have some momentum on a fix, but in the meantime there are two possible workarounds.

First, you could disable preferRest when using the emulator.

    const db = initializeFirestore(app, {
        preferRest: !process.env.FIRESTORE_EMULATOR_HOST
    });

Second, you can create this auth object for use with the emulator.

import { initializeApp, applicationDefault } from 'firebase-admin/app';
import { initializeFirestore } from 'firebase-admin/firestore';

export const app = initializeApp({
    credential: applicationDefault(),
    projectId: process.env.GCLOUD_PROJECT,
});

// Workaround pt 1 - add this createEmulatorAuth function
function createEmulatorAuth() {
    return {
        async getClient() {
            return {
                async getRequestHeaders() {
                    return {Authorization: 'Bearer owner'};
                }
            };
        },
    };
}

async function main() {
    const db = initializeFirestore(app, {
        preferRest: true
    });

    // Workaround pt 2 - Pass emulator auth here
    if (process.env.FIRESTORE_EMULATOR_HOST) {
        db.settings({
            auth: createEmulatorAuth()
        });
    }

    await db.collection("foo").add({time: Date.now()})
    const result = await db.collection("foo").get();

    console.log(result.size);
}
main();
johnnyoshika commented 1 year ago

I don't seem to have any trouble connecting to auth emulator with preferRest. My config:

config/firebase.ts

import { initializeApp } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';
import { getFirestore } from 'firebase-admin/firestore';
import { getStorage } from 'firebase-admin/storage';

const app = initializeApp();

const auth = getAuth(app);

const db = getFirestore(app);

db.settings({
  ignoreUndefinedProperties: true,
  preferRest: true,
});

const storage = getStorage(app);

export { auth, db, storage };
johnnyoshika commented 1 year ago

I stand to be corrected. Although my app runs fine with auth emulator when preferRest is true, unit tests on Linux and macOS time out and fail. Interestingly this wasn't a problem on Windows, but this fixed Linux and macOS for me:

db.settings({
  ignoreUndefinedProperties: true,
  preferRest: !process.env.JEST_WORKER_ID,
});