2

We are moving soon this project to production.

1 - Our Mobile App will create money transfer by posting it to our internal microserve. Such post request will return a CustomToken generated from our internal NodeJs server.

2 - Our internal microservice will replicate such transfer to Firestore and update its state on Firestore accordingly.

3 - Instead of our Mobilie App poll or listen our internal microservice to get the status it will listen to Firestore for getting the status from respective document. In order to listen, it will use the CustomToken returned from post in step 1. Our company wants just take advantage of Real Time Database feature from Google Firestore for this project (reactive approach).

Do you see any consideration/issue when compared what I am doing with this statement: "Google prefers in most cases that you authorize using a service account"? (copied from other related discussion)

The CustomToken is created internally with this NodeJs server and depending on uid extrated from antenticated user authentication/users from Google Firebase

    const admin = require('firebase-admin');

    exports.serviceAccount = {
      "type": "service_account",
      "project_id": "firetestjimis",
      "private_key_id": "ecfc6 ... fd05923",
      "private_key": "-----BEGIN PRIVATE KEY-----\nMIIE .... 5EKvQ==\n-----END PRIVATE KEY-----\n",
      "client_email": "firebase-adminsdk-fg6p9@firetestjimis.iam.gserviceaccount.com",
      "client_id": "102422819688924138150",
      "auth_uri": "https://accounts.google.com/o/oauth2/auth",
      "token_uri": "https://oauth2.googleapis.com/token",
      "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
      "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fg6p9%40firetestjimis.iam.gserviceaccount.com"
    }

     admin.initializeApp({
       credential: admin.credential.cert(exports.serviceAccount)
    });


var uid = "NS .... Ro2"; //copied from https://console.firebase.google.com/project/firetestjimis/authentication/users
var claim = {
  control: true
};
admin.auth().createCustomToken(uid)
  .then(function (customToken) {
    console.log(customToken)
  })
  .catch(function (error) {
    console.log("Error creating custom token:", error);
  });

Our mobile (example in Angular but same idea for IOS and Android) has the SERVICE_ACCOUNT_JSON_FILE I downloaded like this:

environment.ts:

export const environment = {
  production: false,
  firebaseConfig: {
    apiKey: "AIzaSy ... 3DCGihK3xs",
    authDomain: "firetestjimis.firebaseapp.com",
    databaseURL: "https://firetestjimis.firebaseio.com",
    projectId: "firetestjimis",
    storageBucket: "firetestjimis.appspot.com",
    messagingSenderId: "795318872350",
    appId: "1:7953 ... 32b26fb53dc810f"
  }
};

app.component.ts

  public transfers: Observable<any[]>;

  transferCollectionRef: AngularFirestoreCollection<any>;

  constructor(public auth: AngularFireAuth, public db: AngularFirestore) {
    this.listenSingleTransferWithToken();
  }

  async listenSingleTransferWithToken() {
    await this.auth.signInWithCustomToken("eyJh ### CUSTOMTOKEN GENERATED FROM INTERNAL NODEJS SERVER ABOVE ### CVg");
    this.transferCollectionRef = this.db.collection<any>('transfer', ref => ref.where("id", "==", "1"));
    this.transfers = this.transferCollectionRef.snapshotChanges().map(actions => {
      return actions.map(action => {
        const data = action.payload.doc.data();
        const id = action.payload.doc.id;
        return { id, ...data };
      });
    });
  }
}

I understand that both CustomToken creation and its use from our Mobile is relying entirely on Service Account. Am I right? Did I miss some concept and I am using USER CREDENTIAL behind the scene and something that works properly in DEV environment will pop up some surprise when in production? Obviously for this question all comes from my free account but in production it will be paid account but the code and steps will be exactly the same here.

*** Edited after John's comments

Indeed environment.ts goes to Browser. Maybe an Angular Dev with experience to listen Firestore documents can comment if there is an issue

environment.ts

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Jim C
  • 3,957
  • 25
  • 85
  • 162
  • 2
    1/2) Are you including the service account in the client (browser) code? I am not a front end developer (I write backends) but if you are including the service account in client code, you have a GIANT security problem. In other words, don't let the clients see the service account. – John Hanley May 09 '20 at 01:40
  • 1
    2/2) Why are you including the contents of the service account in `exports.serviceAccount`? Just read the contents of the service account JSON key file into memory. Another item is where is all of this code running? The part that runs on a Google Compute service should be using a service account assigned to the instance. Your code should not see the service account at all (there is no need). Look into default service accounts and the Google Metadata server. – John Hanley May 09 '20 at 01:42
  • @JohnHanley if you are talking about the properties in environment.ts certainly it is going to Browser. Is an issue the user be able to figure out apiKey or projectId or appId? I guess he/she can't do anything with these properties. Also, I saw numerous tutuorial guiding to paste such information in environment.ts. I added above a print screen proving it is held in browser. IF it is an issue I am wondering what is the correct way to firebaseconfig in Angular. – Jim C May 09 '20 at 22:59
  • @JohnHanley exports.serviceAccount is part of NodeJs server running in our internal Network. Environment.ts is a file with total different purpose than exports.serviceAccount. Environment.ts navigates to Browser and the properties under exports.serviceAccount never leaves our company Network. exports.serviceAccount is used in our internal Server to generate a customToken. The customTokem indeed goes to Browser and it is used for listen a Firestore document with this part of code async listenSingleTransferWithToken() { await this.auth.signInWithCustomToken( – Jim C May 09 '20 at 23:04

1 Answers1

1

From what I can see the way you currently mint the custom is purely based on the service account, and your implicit knowledge of the UID of the user (the UID you copied from the console). There are no other user credentials involved in the code you shared, nor in other parts of the flow as far as I can see.

I am curious to understand how you are protecting this token generation though: what prevents any other web client from calling your listenSingleTransferWithToken method or otherwise reading the token from the database?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • the token will valid for a short time (one hour) and it will allowed only for reading purpose. So I believe that it is not too terrible. BTW, it seems fragille. Someone can steal this token and use for one hour to read any document of our Firestore Collection. I guess it is a bit harder for a stealer than what I showed above because in fact it isn't an Angular front end. It will be a native (IOS and ANDDROID) mobile app so I believe it is harder to get at least APiKey, messagingSenderId and appId which are compiled with Application. Kindly, tell me just one phrase how would you do it – Jim C May 18 '20 at 02:22
  • 1
    The ID token is a bearer token, which means that anyone who has it can indeed use it. But the token is only sent by Firebase over secured connections. If they are intercepted there, you typically have bigger problems at hand as your secure connection is no longer secure for any use. – Frank van Puffelen May 18 '20 at 03:50
  • 1
    All of this has nothing to do with the configuration data that your app uses to access Firebase services though. What is included in the `firebaseConfig` snippet (for web apps), the `google-services.json` (for Android), and the `google-services.info.plist` (for iOS) is configuration data, and can be shared with your users. See https://stackoverflow.com/q/37482366/209103 – Frank van Puffelen May 18 '20 at 03:52