12

I'm trying to get the UID of the user authenticated by firebase web sdk, in the cloud function. The cloud function is triggered by onWrite event of cloud firestore.

This function is triggered when the logged in user is creating/updating items to the cafe. The authentication is handled by Firebase Auth. The security rules enable write only for logged in users. So this event could be tied to a user.

export const cfun = functions.firestore.document('cafes/{cafeId}/items/{itemId}').onWrite(async event => {
  // trying to get the uid here
})

There are examples in the docs that deals with the userId, but in all those cases the userId is part of the document path. But in this model the user is not part of the path, as a cafe could have multiple owners and so could be manipulated by many users. So adding userId to the path is not an option.

It looks like a common case for serverless architecture.

#

Update: Functions triggered by firestore doesn't have event.auth populated. Looking for suggestions on modelling the following requirement.

In the data-model, I've got cafes and owners. Each cafe could be owned by many owners and a cafe could be transferred to some-other owner at a later stage. So the cafes are modelled as /cafes/{cafeId} and everything that belongs to the cafe as /cafes/{cafeId}/items/{itemId} etc. We also need to query cafes based on different params, if modelled below users it becomes difficult. For these reasons the cafe cannot be modelled as /users/{userId}/cafes/{cafeId}.

As far as security rules are concerned, I could control write access using get(<>) to determine who gets write access to cafes. There is no problem with the security.

I feel that the execution context should provide all available information and let the developers handle it appropriate for their use case. And for serverless apps userId is a must.

If event.auth is not provided in the function, then this restriction will force items that does not belong to users to be modelled /users/{userId}/<item_name>/{itemId} just for the sake of accessing the userId in the cloud functions. This doesn't feel natural.

Also right now there is no way to figure if the cloud function is triggered because of the changes performed in the console. The event.auth info that is available for firebase database triggered functions will be perfect to handle all cases.

Any suggestions regarding how to remodel this case is appreciated as well.

#

Thanks in advance,

sowdri
  • 2,193
  • 5
  • 23
  • 36
  • Try setting the `uid` of the user making the write on the item's node whenever an item creation/update event is triggered. – bojeil Nov 06 '17 at 05:45
  • You can get the user from `event.auth`. See Jacob's answer here: https://stackoverflow.com/questions/42750060/getting-the-user-id-from-a-database-trigger-in-cloud-functions-for-firebase – Frank van Puffelen Nov 06 '17 at 06:55
  • @FrankvanPuffelen I tested it for functions triggered by firestore, but `event.auth` is always undefined. Can you confirm if this behavior applies for cloud firestore as well. FYI the answer you have referred to is related to firebase database. I tried making changes directly in the console (expecting to see `admin:true`) and also using the API (expecting to see the user info), in both the cases `event.auth` was undefined. Thank you – sowdri Nov 06 '17 at 11:25
  • You shouldn't need to do anything in the console. The current user should be available. I'll ask around, but can you edit your question to show the code that you tried with `event.auth`? – Frank van Puffelen Nov 06 '17 at 15:01
  • As you noticed: it turns out `event.auth` is not populated for triggers from Firestore. The recommended way to get the UID is to put it in the path, which has the added advantage that you can also use the data in security rules. – Frank van Puffelen Nov 06 '17 at 20:17
  • @FrankvanPuffelen Thank you for the response. I've updated the question with more info regarding the requirement and my thoughts. Cloud Firestore is an extra-ordinary product, I've been trying to migrate a monolith (appengine/J) to Cloud Firestore for the past few weeks, and the experience has been great. – sowdri Nov 07 '17 at 01:16
  • 1
    @FrankvanPuffelen any update on this? Thanks – sowdri Nov 10 '17 at 02:37
  • @FrankvanPuffelen The recommended way (having `uid` in the path) is definitely not suitable for all use cases. – artur grzesiak Nov 10 '17 at 11:28

2 Answers2

16

I have been facing a similar issue. In Firebase, it was easy - you simply took the data from event.auth. I would assume this is simply a feature not implemented yet while we are in the beta phase of Firestore. Adding the user id to the path does not work as you previously mentioned as it will constantly be changing depending on the user making the update.

My scenario is that I want to create a "lastUpdatedBy" field in the object being updated. If we were to allow the client to send in a lastUpdatedBy field in the payload, this could be abused by a rogue client (i.e. someone with a authenticated account) trying to impersonate someone else. Therefore in Firebase we relied on a cloud function to populate this field on data change events.

My workaround is to allow the client to insert the "lastUpdatedBy" field but additionally use the Firestore rules to validate that the userId in the payload matches that of the logged in user - otherwise deny the write request.

Something like:

match /collectionA/{docId} {
  allow update: if request.resource.data.lastUpdatedBy == request.auth.uid;
}

Until Google/Firestore guys add the "auth" object to the cloud function I don't see any other workaround but would love to hear differently.

Chris
  • 223
  • 1
  • 5
  • 2
    Has there been any update to being able to retrieve the uid of the person who triggered the event? Which I guess any reference to auth will error eg auth.UserInfo() etc. – Mike Dec 07 '17 at 07:53
  • I'm also running into this issue, and my payments hinge on this feature. – FiringBlanks May 31 '18 at 15:16
5

Since Cloud Functions 1.0 you can get the UID like this

exports.dbCreate = functions.database.ref('/path').onCreate((snap, context) => {
  const uid = context.auth.uid;
  const authVar = context.auth; 
});

Here is a nice post from the FB team for all CF1.0 changes: https://firebase.google.com/docs/functions/beta-v1-diff#event_parameter_split_into_data_and_context

The data of context.auth can be found here: https://firebase.google.com/docs/firestore/reference/security/#properties

Jürgen Brandstetter
  • 7,066
  • 3
  • 35
  • 30
  • 2
    Is this correct for Firestore (not Realtime Database) too? context.auth is listed for Realtime Database, but not for Firestore and I'm getting errors when I try to use it. console.log(context.auth) prints "undefined" (July 2018) – Nick Lothian Jul 02 '18 at 07:21
  • 2
    According to https://stackoverflow.com/a/49827227/280795 and https://stackoverflow.com/a/49723193/280795 this doesn't work in Firestore as of June 2018 – Nick Lothian Jul 02 '18 at 07:26
  • 1
    Still only for Firebase Database, not Firestore. [Here's the feature request](https://github.com/firebase/firebase-functions/issues/300), let's push it! :) – crysxd Jun 17 '19 at 11:36
  • Thanks Jurgen! ```context.auth.uid``` was what I needed! :D – Chris Mar 11 '20 at 13:11