firebase-functions: ^2.0.2
firebase-tools: ^4.0.2
firebase-admin: ^5.13.1
functions.firestore
.document('test/model')
.onWrite((data, context) => {
expect(context.auth.uid).toEqual('the-user-id')
})
Here's a test case at brianmhunt/bug-reports on the firestore-functions-auth branch. Just clone, yarn install and yarn test (or npm ^^).
Note that the test case is not testing production behaviour, but the firebase-functions-test behaviour, however I would expect the test and production behaviour to be fixed in tandem.
The EventContext for a Firestore call should contain the user authentication information, as it does with Callable and Realtime database calls, i.e. context.auth should be populated.
The context.auth is undefined for firestore onWrite and related functions.
Hi, this is a good feature request, and it is on our backlog.
Hey @laurenzlong just wanted to follow up on this and see if there's any progress or if there's anything the community can do to help move things along. I'd love to move some projects over to firestore but not having this auth data in cloud functions makes it impossible to handle some security setup I use. I'd be happy to take a look and open a PR if there was some direction on what needed to get going. Thanks in advance.
Is there any workaround for this feature? We really need this feature to compensate user some reward whenever a user creates a new record.
It's already documented in the official reference: https://firebase.google.com/docs/reference/functions/functions.EventContext#.auth
@ohtangza Note the comment in the second paragraph of the docs you linked:
This field is only populated for Realtime Database triggers and Callable functions.
Is there any workaround for this feature? We really need this feature to compensate user some reward whenever a user creates a new record.
I ended up doing this for now: https://stackoverflow.com/a/50842161/637751
@christianlacerda Thanks! I am also implementing the same way.
@brianmhunt Oh, you are right. I hope this be implemented near in the future.
@christianlacerda @ohtangza that's only a workaround if all you need is the user's id. We need the user's id token to send to our own backend, to protect our endpoint. Our current workaround for that is sending a shared secret along with the request, but now we also need to send the user's id because that secret doesn't have any user context.
Hi this feature is a deal breaker for me, no concept of who made an update inside the trigger is an oversight, at present this hinders our ability to select firestore as a platform, I look forward to hearing about this feature coming available. asap
Hi, would really appreciate an update on this, seems like a pretty important feature. Cheers.
Hi everyone, thanks for your patience! This feature request is unfortunately more involved than we originally thought, and will take longer to implement. However, we are doing our best to move this forward.
Internal bug reference: 68792946
@thechenky Thanks for the info. At least, the community would be sure that this feature eventually will be shipped.
What's the status on this feature?
@mqln unfortunately this issue is blocked on a list of other issues that need to be resolved before this is implemented.
@thechenky Glad that at least it's queued. Thanks for your update.
@thechenky are there any recommended workarounds until this is implemented? my use case is as follows:
I have a trip document which has an expenses subcollection and a members subcollection. Every time the expenses subcollection is modified I want to send a push notification to all the members in the members subcollection (say via FCM), minus the person that actually made the modification. The most forward way seemed to be to get the UID from the trigger but that evidently is not possible given the current situation. Perhaps there is another way of doing this though that I'm missing.
Thanks!
@yqiang you could include the uid of the person in the expense object, retrieve the latest on that array and don't send that uid the notification. We've also worked around this like that, by adding an extra attribute to the data (in our case just a 'sync' boolean that we remove after update)
@jrnk I see – I think I can give that a try, thanks!
Hi everyone, I checked back in on this issue to see how it's progressing. Still blocked, but I linked to this thread and urged the priority of this issue to be increased. Let's see how it goes. Will post back here with updates when I have them. Thanks for your patience!
Thanks, really appreciate the updates.
This is an important feature, in my case it play a big role in security and detect hacker. Hope it priority is increased. Thanks for the hard work
Hi there again everyone - another update. It has been decided that unfortunately native support for context.auth for Firestore triggers will not be implemented due to technical constraints. However, there is a different solution in the works that hopefully will satisfy your use case, but I cannot share details. On this forum we generally keep open only issues that can be solved inside the functions SDK itself - I've kept this one open since it seemed important and I wanted to provide some updates on the internal bugs tracking this work. Now that a decision has been reached, I'm going to close this out. Thanks again for everyone's patience and I'm sorry I don't have better news. Please use the workaround referenced in https://github.com/firebase/firebase-functions/issues/300#issuecomment-428593485.
Is there another issue we can track for this “different solution”? Or will you comment on this ticket when information is available?
Thanks for the update @thechenky. I can only imagine the the design constraints for a system like Firestore and the challenges that present for the issue here, and while this didn't get resolved the way everyone hoped I am grateful for the effort to resolve this issue and keep everyone informed.
Surprised I didn't see it mentioned in this thread, but if you have to do user-agent validation/authorization checks, that logic can be managed within Firestore rules, which does have access to the auth context
Second what @eazeye said. Surprised it wasn't mentioned earlier.
@adamduren it wasn't mentioned because the scope of the requirement is bigger than just user-agent validation/authorization checks.
Consider the following example: I have an object A that can be accessed by a team of users. It is important in such a system to keep a record of who deleted object A. Ideally you'd be able to write an onDelete trigger to add a record - personB, deleted object X, datetime etc.
However, since we don't have access to who the actor is in triggers... we'd have to do this client side for every delete action. This means if you write a web app and a mobile app, you'd have to duplicate the logic in both clients to log the actors, rather than using the trigger to take care of it.
@xaksis valid point, had not considered that use case.
A workaround, although far from perfect, would be to have onRequest cloud functions called for document mutations. You could then have a auditTrail wrapper function that wraps any function you want to monitor audits to.
This would enable the keeping of an audit trail since HTTP requests have the auth context. However I recognize the overhead this creates for simple use cases and introduces the issue of security rules being ignored for cloud functions so extra care would have to be taken.
@adamduren yeah, we actually went that route but it has a lot of disadvantages. Some you identified, others:
Summary of how I solved this / a workable solution similar/same as (?) Comment #300:
On client
Add logged in/current user's uid (e.g. as creatorId) to entity they're creating. Access this uid by storing the firebase.auth().onAuthStateChanged() User object in your app state.
In Firebase Firestore/Database
Add a Security Rule to create to validate that the client-supplied creatorId value is the same as the authenticated user's uid; Now you know the client isn't spoofing the creatorId and can trust this value elsewhere.
e.g.
match /entity/{entityId} {
allow create: if madeBySelf();
}
function madeBySelf() {
return request.auth.uid == request.resource.data.creatorId;
}
In Firebase Functions
Add an onCreate trigger to your created entity type to use the client-supplied, and now validated, creatorId to look up the creating user's profile info, and associate/append this info to the new entity doc.
This can be accomplished by:
Creating a users collection and individual user documents when new accounts are created, and populating the new user doc with app-useful fields (e.g. displayName). This is required because the fields exposed by the Firebase Authentication system are insufficient for consumer app uses (e.g., displayName and avatarURL are not exposed) so you can't just rely on looking up the creating user's info that way.
e.g. (using ES6)
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
const APP = admin.initializeApp()
export const createUserRecord = functions.auth.user()
.onCreate(async (userRecord, context) => {
const userDoc = {
id: userRecord.uid,
displayName: userRecord.displayName || "No Name",
avatarURL: userRecord.photoURL || '',
}
return APP.firestore().collection('users').doc(userRecord.uid).set(userDoc)
})
creatorId value, and useful user objects, add an onCreate trigger to your entity type (or all your created entities) to look up the creating user's info and append it to the created object. export const addCreatorToDatabaseEntry = functions.firestore
.document('<your entity type here>/{entityId}')
.onCreate(async (snapshot, context) => {
const userDoc = await APP.firestore().collection('users').doc(snapshot.data().creatorId).get()
return snapshot.ref.set({ creator: userDoc.data() }, { merge: true })
})
This clearly leads to a lot of duplicated user info data throughout your system -- and there's a bit of clean up you can do ('creatorId` is duplicated on the created entity in the above implementation) -- but now it's super easy to show who created what throughout your app, and appears to be 'the Firebase way'.
Hope this helps. I've found Firebase to be super amazing in some ways, and make some normally easy things (like this) harder than they 'should' be; on balance though am a major fan.
I was developing a tracking system about who made a difference for a specific document. I guess there are use cases where we cannot find a programmatical workaround.
The issue with this is that we now also have no way of using any of the custom user claims for permission based handling when a document is updated (in the fn itself) .. ugh
@thechenky Hi! You mentioned there would be a different solution that might be able to satisfy our use cases. Was it implemented already?
I have a use case where
revisions collection including their uid;Currently, I have to add a latestEditor field including the user's UID but it would be much easier to just have the context.auth option to get the UID.
I can add a field for identify the user, but I will do not have any guarantees
@thechenky another use case which I can't find an easy workaround: when I need to know which user deleted a document.
For create and update triggers, I can add a field for identifying the user who made a change. However, that's not possible when deleting a document. Or am I mistaken? Is there a way to know which user deleted a document?
Not having this feature is really frustrating. I hope you reconsider and implement it in future versions.
@wceolin this is my problem also, create/update is fine using the solutions in this thread, but delete is not possible since I need to know who deleted it and use their loginToken for a different system that accesses the same database.
Have you found a workaround yet?
@ralcar I found some hacky solutions:
Using queues
queue collection;onCreate Cloud Function to watch for new documents, take the document ID, and delete that document using the Admin SDK.That wasn't an option for my use case, though. So, I came up with an even hackier workaround 😅
Updating before deleting a document
latestEditor field containing the userId;latestEditor field has the same UID of the user trying to delete it;onDelete Cloud Function and check the previous data. Because we've added the latestEditor field, then we can get the UID there :)Alternative (apologies for brevity) for thought:
In certain designs it may make sense for the signatures could be saved separately, outside the model itself. With e.g. JWS some finessing is needed to use indexes and avoid duplication.
There are more complex versions with AES/symmetric crypto, public/private crypto, but I think the above covers the essentials of a cryptographically sound way to accomplish this, or at least get one on the right track.
Hello there, I created a Firestore trigger that logs write actions into a separate history collection, and the only thing missing is a safe way to identify the user.
I hoped the EventContext.auth field would work while the client is signed in with a JWT.
The only solution I found yet is to require a user ID in Firestore Security Rules and compare it with the JWT, then the value passed to the trigger would be safe.
But even if the trigger can get the user ID and put it into its logging collection, it can't remove it from the targeted collection without performing a new request, which will, of course, cost something.
Hi there again everyone - another update. It has been decided that unfortunately native support for
context.authfor Firestore triggers will not be implemented due to technical constraints. However, there is a different solution in the works that hopefully will satisfy your use case, but I cannot share details. On this forum we generally keep open only issues that can be solved inside the functions SDK itself - I've kept this one open since it seemed important and I wanted to provide some updates on the internal bugs tracking this work. Now that a decision has been reached, I'm going to close this out. Thanks again for everyone's patience and I'm sorry I don't have better news. Please use the workaround referenced in #300 (comment).
@thechenky Could you please provide more informations on the mentioned 'different solution'? Thank you.
This is not good. It makes what I would consider a whole class of functions (audit logs, history, etc.) impossible. None of the proposed solutions are clean either. I guess we can just grit our teeth and make a REST API out of other functions. It really does feel like I have just stepped in a dirty puddle on the otherwise nice path that has been my journey with Firebase
I'm not exactly happy this feature never made it.
How would you handle logging who deleted a document?
The only _hacky_ workaround I can think of, is having a deleted field on the document, which the client filters out, and then have the onUpdate trigger log that the document was deleted, and then actually delete() the document.
Hideous.
@larssn Which option is less expensive between deleting by update trigger and deleting by cloud function query ? Thanks
@KaKi87 Cloud functions invocations are much cheaper than firestore writes in general (Pricing).
I think that the API option is preferable. The flow is:
The benefit is that you can also do additional / more complex validation at the same time if you need to.
@chrisfraser Firebase functions run as the account service worker and are not bound to the Firestore rules, so using the API would (without the additional complexity of impersonating the Firebase user/mimicking the rules) circumvent any restrictions in the Firestore rules. So using the API is preferable only insofar as one has a set of Firestore rules that are easy to replicate in a function.
Just ran into this issue while implementing Firestore Cloud Functions.
@thechenky
Is there an update about the different solution?
Thanks!
This is a huge security issue. If I were to delete some records in the Firestore using a where record.owner == userId clause, without context.auth I have to trust whatever userId client side passes through. Since Cloud Function is not subject to Firestore security rules, this could end up enabling bad actors to delete or change other user's data.
This seems to me a bottom line security feature to have. Any workaround for this?
Hi folks, thanks for the feedback on this. We definitely hear you that you'd like to see Cloud Functions for Firebase support context.auth for Firestore functions. Unfortunately, there's no clear path to making it happen in the near future.
This repository is not the correct place to post feature requests for general Cloud Functions / Firestore capabilities -- if you are interested in this feature, please submit a feature request so that we can continue to track the interest in this internally and help prioritize the work that needs to happen to make this feature possible.
In the meantime, when securing Firestore functions we recommend you write the user's uid to the document at create/update time and verify it using security rules. I know this isn't ideal, and I hope you'll make your voice heard in the feature request form above. Thanks, all!
Most helpful comment
Hi this feature is a deal breaker for me, no concept of who made an update inside the trigger is an oversight, at present this hinders our ability to select firestore as a platform, I look forward to hearing about this feature coming available. asap