Search code examples
firebasegoogle-cloud-firestore

Firestore Emulator: How to generate Firebase ID tokens?


In my project, I am using the Firestore REST API and using bearer tokens to authenticate users (apply the database rules). I am now trying to write E2E tests for my application, but I don't know how to generate the tokens. I have tried generating them using the admin SDK like so:

process.env.FIRESTORE_EMULATOR_HOST = 'localhost:8080';

  const db = admin.initializeApp({
    projectId: PROJECT_ID,
    credential: admin.credential.cert(config.fireStore)
  });
  const token = await db.auth().createCustomToken(uid);

But the token produced does not work (getting PERMISSION_DENIED) which doesn't totally surprise me because the service account in the config is the one from downloaded from Firestore (I'm not sure if this would work with the emulator?)

I have also tried initialising the admin SDK credentials using: admin.credential.applicationDefault() but then I get the following error: Error: Failed to determine service account. Make sure to initialize the SDK with a service account credential. Alternatively, specify a service account with iam.serviceAccounts.signBlob permission. Original error: Error: Error while making request: getaddrinfo ENOTFOUND metadata metadata:80. Error code: ENOTFOUND

Any help or advice on how to do this would be much appreciated.


Solution

  • When you call db.auth().createCustomToken(uid) you get a Custom Auth token signed by your service account. This is not a Firebase ID Token. It's meant to be sent from the server to the client (app, web browser, etc) and then that client can call signInWithCustomToken() in order to initialize an auth session on the device based on the claims made by the server.

    Once that's done you could get a Firebase ID Token from the client using auth.currentUser.getIdToken()

    So basically in your test you're going to need to use the Node.js Admin SDK (firebase-admin) and the JS Client SDK (firebase) simultaneously to simulate user sessions. That should be fine since the client SDK can work in Node.js or Browser environments but it's not a well-lit or well-tested path!

    Everything I mentioned above will work for testing against a real live Firebase project. However if you're using the emulator, the @firebase/testing SDK provides utilities for simulating auth in tests. This generates tokens which are accepted by the Firestore emulator but are not accepted in production. So you can do:

    const testing = require('@firebase/testing');
    
    const testApp = initializeTestApp({
      projectId: 'your-project-id',
      auth: { uid: 'some-uid' }
    });
    const testAuth = testApp.auth();
    
    const token = testAuth.currentUser().getIdToken();