Skip to content
This repository has been archived by the owner on Sep 13, 2024. It is now read-only.

How to access admin.auth()? #34

Open
Acterion opened this issue Apr 28, 2021 · 10 comments
Open

How to access admin.auth()? #34

Acterion opened this issue Apr 28, 2021 · 10 comments

Comments

@Acterion
Copy link

Acterion commented Apr 28, 2021

Is it even possible to access the admin object from the migration script?

I want to change some filed names inside customUserClaims, so I thought it would be nice to create a migration for that. But for doing that I would need to access admin.auth().listUsers() and then run setCustomUserClaims() for each individual user.

I've added const admin = require("firebase-admin"); before the script, which amazingly worked, but that feels like a workaround and not a proper solution.

I would suggest adding admin object to MigrateOptions

@vandres
Copy link

vandres commented May 23, 2021

Would also like to know, what the way to go is, when needing auth data.

@kiptoomm
Copy link
Contributor

I have the same need.

For me, the following error occurs:

{
  errorInfo: {
    code: 'auth/invalid-credential',
    message: '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.'
  },
  codePrefix: 'auth'
}

What I did:

  1. tell fireway that we're using the local emulator:
    export FIRESTORE_EMULATOR_HOST="localhost:8080"
  2. run the migration:
    fireway migrate

What to do to get the firebase-admin functions working with Fireway?

By the way, I came across this package (looks like a fireway fork?) that seems to support --emulator as an option. FWIW, it seems to me like a neater solution.

@kevlened
Copy link
Owner

I'd accept a PR for an --emulator option!

I'd rather expose the result of admin.auth() directly. This provides the ability to do a --dryrun for auth in the future. I would also accept PRs for that, assuming there were tests.

@kiptoomm
Copy link
Contributor

I'd rather expose the result of admin.auth() directly

I'm not sure I understand this.

FWIW, here's a snapshot of the migration script that produces the error I shared above:

// v0.0.2_set_custom_claims.js

const { listAllUsers } = require("./helpers");

/**
 * set custom claims on all firebase auth users
 * */

module.exports.migrate = async ({ firestore }) => {
  /**
   * set the custom claims for all users
   */
  const setCustomClaims = async () => {
    // fetch all user entries from firebase auth
    let firebaseAuthUsers = [];
    await listAllUsers(firebaseAuthUsers); // firebaseAuthUsers is a List<JSON> representing the auth user objects
    console.log("firebaseAuthUsers: ", firebaseAuthUsers);

    // for each auth user:
    for (authUser in firebaseAuthUsers) {
      // set auth custom claims ... 
    }
  };

  // run the function
  setCustomClaims();
};

// helpers.js

const admin = require("firebase-admin");
/**
 * enumerates all [firebase auth] users from the firebase project
 * adapted from https://firebase.google.com/docs/auth/admin/manage-users#list_all_users
 *
 * @param {List<JSON>} usersList empty list to append the results into
 * @param {string} nextPageToken
 * @returns {List<JSON>} a list of JSON objects representing users
 *
 */
exports.listAllUsers = function(usersList, nextPageToken) {
    // List batch of users, 1000 at a time.
    return admin
        .auth()
        .listUsers(1000, nextPageToken)
        .then((listUsersResult) => {
            listUsersResult.users.forEach((userRecord) => {
                usersList.push(userRecord.toJSON());
            });

            if (listUsersResult.pageToken) {
                // List next batch of users.
                return listAllUsers(usersList, listUsersResult.pageToken);
            }

            // the compiled list, after all the batches are fetched
            return usersList;
        })
        .catch((error) => {
            console.log('Error listing users:', error);
        });
};

@kevlened
Copy link
Owner

I'd rather expose the result of admin.auth() directly

I'm not sure I understand this.

I want to reduce surface area, because in my experience, more surface area is more that can break. If you need auth, fireway can expose auth.

FWIW, here's a snapshot of the migration script that produces the error I shared above:

If it runs against localhost, but not remotely, continue to do what you're doing now (const admin = require("firebase-admin")), but configure it with your remote credentials. Ideally, this would be handled nicely by fireway, but isn't right now.

@kiptoomm
Copy link
Contributor

kiptoomm commented May 26, 2021

If you need auth, fireway can expose auth.

It'd be nice to see how to use the fireway-provided auth.

If it runs against localhost, but not remotely, continue to do what you're doing now

It doesn't work on localhost right now (the error I shared is from localhost - '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.').

To rephrase the issue: I don't know how else on localhost (besides setting the env variable) to tell fireway that I am using the Firebase emulator and that I don't have a real project ID or other credentials since I'm using the emulator.

Thanks for your patience, I am new to JS/NoSQL :)

@kevlened
Copy link
Owner

If you need auth, fireway can expose auth.

It'd be nice to see how to use the fireway-provided auth.

It doesn't work today, but ideally your migration file could look something like:

module.exports = async ({ firestore, auth }) => {
  // your migration logic
};

If it runs against localhost, but not remotely, continue to do what you're doing now

It doesn't work on localhost right now (the error I shared is from localhost - '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.').

To rephrase the issue: I don't know how else on localhost (besides setting the env variable) to tell fireway that I am using the Firebase emulator and that I don't have a real project ID or other credentials since I'm using the emulator.

Ah. An --emulator flag would make this less painful. For now, manually using the FIRESTORE_EMULATOR_HOST env variable is probably your best bet. There might be a way to use @firebase/testing, but I haven't explored that.

Thanks for your patience, I am new to JS/NoSQL :)

No worries! Happy to help.

@kiptoomm
Copy link
Contributor

For now, manually using the FIRESTORE_EMULATOR_HOST env variable is probably your best bet

It normally works for me but it's not working for me in this particular use case (i.e., when using the firebase admin SDK's modules - auth in this case) :(

I have also tried running the script against a remote database by providing the --projectId option (and unsetting FIRESTORE_EMULATOR_HOST) but I still get the same error

@dsvgit
Copy link

dsvgit commented Nov 2, 2021

app property which is provided by fireway seems like works for me

  • --dryrun option doesn't work for auth updates (only for firestore)
  • I had to use --forceWait option to make it works
const clearClaims = async (auth, authUser) => {
  if (authUser.customClaims) {
    return await auth.setCustomUserClaims(authUser.uid, null);
  }
};

const updateClaims = async (auth, authUser, changes) => {
  const newCustomClaims = { ...authUser.customClaims, ...changes };
  return await auth.setCustomUserClaims(authUser.uid, newCustomClaims);
};

module.exports.migrate = async ({ firestore, app }) => {
  const auth = app.auth();

  const usersSnapshot = await firestore.collection("users").get();
  usersSnapshot.forEach(async (snapshot) => {
    const userId = snapshot.id;
    try {
      const authUser = await auth.getUser(userId);

      // await clearClaims(auth, authUser);
      // console.log(authUser.customClaims);

      await updateClaims(auth, authUser, { admin: true });
    } catch (error) {
      // console.log(error, userId);
    }
  });
};

@xd2
Copy link

xd2 commented Jan 1, 2022

Hi. In my case, the fireway app.auth() (v1.0.2) was failing using emulator even with --forceWait as suggested by @dsvgit (even with the --dryRun switch).
I was getting the following error

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.
at FirebaseAuthError.FirebaseError [as constructor]
hereunder my command line to start the migration
FIRESTORE_EMULATOR_HOST=localhost:8080 yarn fireway migrate --require=ts-node/register --forceWait --dryRun

I end up configuring my env using these two variables:

GCLOUD_PROJECT=myproject
FIREBASE_AUTH_EMULATOR_HOST=localhost:9099

Local migration using 'app.auth' works properly with the emulator now !

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants