Firebase auth propagation time for Firestore rules? - firebase

Am calling these functions in sequence (javascript 'firebase`):
firebase
.auth()
.signInWithEmailLink(email, url)
.then(userCred => {
return firebase.firestore().collection('users').doc('someDocID').get()
....
I have security rules setup in firestore that allows read of 'users/:docId' only if the email field in that document equals the auth email:
allow get: if request.auth.token.email == resource.data.email
The problem is we get a missing or insufficient permissions error on the get().
For sure the email in the database is correct so the only problem could be is that request.auth.token.email is not valid.
Could it be that signing in (using any sign in function), even if resolves, doesn't guarantee the auth token is updated and can be immediately used ?
Is there propagation time? should we call getIdToken() after sign in resolves to make sure the token is valid before making database/firestore calls?

When a user actively signs in (by calling any of the signInWith... methods), the client gets an ID token, which contains the up-to-date values for that user. That ID token is valid for an hour, and is then auto-refreshed by the client/SDK.
So right after the signIn... call, the profile should contain the latest values. If you think something is going wrong there, I'd recommend logging the values right before making the call to Firestore.

Related

Does FirebaseAuth getIdToken ever change for a user?

My users will use FirebaseAuth to get their id token, then send this to the server, where it’s authenticated with verifyIdToken. Currently, I’m using the uid property of the result as a key in my db. To make things more efficient, I would like to hash the id token, and use that as a key in my db instead. For this to work, getIdToken must always return the same thing for any given user. Can I rely on this to be true?
To clarify, the user will still be authenticated with verifyIdToken at first. But once they’re in the db, I will just use a query on the db to authenticate them instead.
ID tokens expire after one hour (for security reasons) and are refreshed automatically by the Firebase client SDK. If you want to pass an ID token to your backend, you should only pass a fresh token, otherwise it will not validate on your backend when you go to verify it. I suggest reading that linked documentation to get more details, including how to use a listener to get the token immediately when it's refreshed.

Do I need to use verifyIdToken on the context.auth object in firebase cloud functions?

Using a callable function, my intention is to only allow logged-in users to make calls to this.
export const sendMessage = functions.https.onCall(async (data, context) => {
From the context param there, I have the auth token. But I'm just very new to firebase in general and I don't want to make a critical mistake by misunderstanding here. If I'm logged out, context.auth becomes null-- easy enough. But do I need to be sure that the auth token is valid and not "made up" by calling verifyIdToken anyway?
If not, then is simply checking that context.auth isn't null enough to be sure that the user is logged-in?
But do I need to be sure that the auth token is valid and not "made up" by calling verifyIdToken anyway?
No, that happens automatically. You can be sure the context.auth is verified if not null. As stated in the documentation:
The functions.https.onCall trigger automatically deserializes the request body and validates auth tokens.

Storing Firebase Auth UID in Cookie while using Firestore - Is this secure?

I was recently having an argument with another programmer mate of mine regarding storing Firebase Auth UID (just the uid nothing else) in a cookie with sameSite: 'strict' enabled.
What's the argument about
Currently, I am working in a Nuxt JS project where I am saving the user's uid on onAuthStateChange() event in a cookie with sameSite: 'strict' enabled so that I can grab it in my serverMiddleware code and do stuff with it.
I have checked this firebase doc about managing cookie and it shows how to store the JWT idToken in a cookie and then in the server decode it.
In fact, that is who I initially coded my work. But due to some requirements, it was super helpful if I store the uid instead. So, I did that. Then I started reading about how can I hack my own data to see if anyone can harm my data from the uid in the cookie.
Then I stumbled upon to this firebase doc: Use the Cloud Firestore REST API which shows how to get the firestore data using REST API and I figured out that you need to provide Google OAuth 2.0 token in the header of the API call in order for it to work, otherwise even if you put the correct URL with all the collection name and everything (which is hard for an outsider to know, but lets assume he knows), you will get nothing but this:
{
"error": {
"code": 403,
"message": "Missing or insufficient permissions.",
"status": "PERMISSION_DENIED"
}
}
I have also tried to run code in browser console in order to hack the data out of my project. But That didn't work as well.
Now in order to get the Google OAuth 2.0 token, the person must need login access to my account which is not that easy as I have a unique long password along with 2 Step Authentication with phone OTP & push notification. Besides if anyone has login access to my Google account, he can easily go to console.firebase.com and see the data, so at that point, nothing will matter.
But I did say that if anyone is using firebase Realtime database then I will not recommend storing the uid in a cookie as the realtime database provides easy REST API without any authentication layer to fetch data. At that time I would recommend using JWT idToken instead.
So, what's the final question?
The final question is this:
If someone is using firebase auth & firebase cloud firestore (not realtime database) using firebase SDK in his project, is it secure to store just the uid in cookie instead of storing JWT idToken if it will reduce the code complexity and code execution time over using idToken?
I would love to know your thoughts on these as there are many super experienced devs beside two programmers arguing.
My friend keeps telling me that storing uid in the cookie is not sure, but when I asked him why exactly, he had no concrete answer. As what is secure and what is not a universal thing and changes as you change your tools. But in this exact context what do you guys think? I know that normally in most cases it is not a secure thing, but I am asking about this specific context only.
It is in fact fairly common to expose the UID of a user to other user to identify that user. See Firebase - Is auth.uid a shared secret?
There is nothing insecure about storing the UID in a cookie, nor in reading that cookie in your middleware. But if your middleware then assumes that the UID is the authenticated user, you have a security risk.
What is keeping any other user from putting your or my UID into that cookie, and thus getting access to your or my data?
Also note that UIDs don't change over time, so if ever one (even inadvertently) leaks, you could impersonate that user forever.
ID tokens on the other hand have a limited lifespan (currently about an hour), which limits the risk if they accidentally get exposed.

How to logout the user using Firebase Admin SDK?

So, I have created a cloud function using Firebase Admin SDK. The purpose of that function is to disable the user and after successfully disabling it, I want that user to be logged out from my application. I have disabled user but can't figure out how to log out the user.
I was wondering if there is any function of a workaround to achieve this?
A user that is signed in to your app has a ID token that is valid for up to an hour. Once that token has been created, there is no way to revoke it.
The typical way to handle your use-case is to also flag the user in a server-side database once you disable their account, and then check that flag in any operations.
For example, if your using the Firebase Realtime Database, and disable the user with Node.js, the code to also flag the user in the database could look like this:
// Disable the user in Firebase Authentication to prevent them from signing in or refreshing their token
admin.auth().updateUser(uid, {
disabled: true
}).then(function() {
// Flag the user as disabled in the database, so that we can prevent their reads/writes
firebase.database().ref("blacklist").child(uid).set(true);
});
And you can then check this in the server-side security rules with something like this:
{
"rules": {
".read": "auth.uid !== null && !root.child('blacklist').child(auth.uid).exists()"
}
}
This rule allows all users that are signed in (auth.uid !== null) full read access to the database, but blocks users who you've flagged (!root.child('blacklist').child(auth.uid).exists()).
For an (even) more elaborate example of this approach, see the documentation on session management.
There are two main types of tokens used in Firebase Auth that are relevant to your question here:
Refresh token
ID token (aka, access token)
Firebase ID tokens are short lived and last for an hour; the refresh token can be used to retrieve new ID tokens. Refresh tokens expire only when one of the following occurs:
The user is deleted
The user is disabled
A major account change is detected for the user. This includes events like password or email address updates.
https://firebase.google.com/docs/auth/admin/manage-sessions
So in your case, when you disable the user, the refresh token will be automatically revoked. This means that once the short-lived ID token expires, they won't be able to retrieve a new one.
But you want them to be logged out immediately after being disabled. There are two main considerations here:
if you control the well-behaved client application, you can voluntarily log them out in the client
if you want to truly protect against malicious actors, you can revoke the ID token on the backend
Voluntarily logging out in a well-behaved client
If the token is revoked via the Admin SDK, the client is informed of the revocation and the user is expected to reauthenticate or is signed out:
https://firebase.google.com/docs/auth/admin/manage-sessions#respond_to_token_revocation_on_the_client
However, the docs are very misleading here. There is no built-in behaviour to automatically inform the client of a revocation. Instead, you can follow the suggestions in this thread (https://groups.google.com/g/firebase-talk/c/cJjo9oknG6g/m/XG24x8SqEgAJ) which talk about how to implement this behaviour. The two main options presented are:
Use Firebase Realtime Database to build your own real-time "push" mechanism to detect revocations
Use currentUser.getIdToken(true) to force-fetch a new id token, which will detect the refresh token revocation, and log the user out (you should get an even on the onAuthStateChanged listener).
For option 2, note the parameter true passed in to forceRefresh. This is generally not a good option - you don't want to force refresh on every API request, but if you don't, it's hard to know when to do a force refresh.
When you refresh the page, the Firebase client SDK will typically automatically perform a force refresh.
Server-side detection
When a user's ID token is to be verified, the additional checkRevoked boolean flag has to be passed to verifyIdToken. If the user's token is revoked, the user should be signed out on the client or asked to reauthenticate using reauthentication APIs provided by the Firebase Authentication client SDKs.
https://firebase.google.com/docs/auth/admin/manage-sessions#detect_id_token_revocation_in_the_sdk
Note that using the checkRevoked=true option results in a network request from your backend to Firebase's backend, which is expensive. Again, it's hard to know when it's worth using checkRevoked. Perhaps it's worth the cost to always perform the network check on a small subset of highly sensitive APIs.
Summary
You should read through the docs in full (https://firebase.google.com/docs/auth/admin/manage-sessions) and see which approach suits you best.
Frank van Puffelen has already covered the other standard option - using rules to guard Firebase backend services.
But in general, there isn't anything that helps out of the box. If you understand the concept behind refresh tokens and id tokens, you'll notice that it's fundamentally not possible to revoke the ID token while retaining the performance benefits (ie, reducing network traffic) that is the entire reason for using the refresh+id model to begin with.
I'd just let the token expire, and accept that any "disable" can be delayed by up to 1 hour.

What is the difference between auth.uid and auth.token.sub in Firebase Realtime Database security rules

What is the difference between auth.uid and auth.token.sub in Firebase Realtime Database security rules?
I assume they are the same (user has only 1 uid) but they have different descriptions, would love to know a definitive answer.
auth.uid : A unique user id, guaranteed to be unique across all providers.
auth.token.sub : The user's Firebase UID. This is unique within a project.
Cheers
They are exactly the same. auth.uid is provided for backwards compatibility (auth.token didn't used to exist in the Security Rules) and ease-of-use: sub is not a commonly understood term for an ID, whereas uid is a bit easier to understand and you don't have to dive into the token contents.
auth.token.sub is the id encoding in a token. The Firebase Admin SDKs have a built-in method for verifying and decoding ID tokens. If the provided ID token has the correct format, is not expired, and is properly signed, the method returns the decoded ID token. You can grab the uid of the user or device from the decoded token.
So that mean inside the token.sub you have the uid of the user. But without the sdk you cannot see the real value cause is not decode. This is for security.
If you want to use this you need to decode this with example the verifyIdToken() method.
Example on Node.js
// idToken comes from the client app (shown above)
admin.auth().verifyIdToken(idToken)
.then(function(decodedToken) {
var uid = decodedToken.uid;
// ...
}).catch(function(error) {
// Handle error
});
Link here https://firebase.google.com/docs/auth/admin/verify-id-tokens
Hope that be helpful.

Resources