How to revoke an authentication token? - firebase

Say I generated an authentication token, and to save on processing and remote calls, I've set it's expiration data some 30 days in the future.
Now I want to remove this account from my system, is there a way to revoke the authentication token I have given the client?
I don't think that's possible currently, and I can certainly work around that (by not having such high expiration times mostly), but I just wanted to make sure I didn't miss something in the docs.

Firebase now offers the ability to revoke refresh tokens, it's quite fresh - added 04/01/2018.
https://firebase.google.com/docs/auth/admin/manage-sessions#revoke_refresh_tokens

You can't really revoke that specific token (outside of invalidating the secret that generated the token, but that will invalidate all other tokens issued by that secret too - probably not what you want).
You can, however, rely on some information that's specific to the token (perhaps you included a unique user ID as data in the token) and update your security rules to reject any operations that match that value.

Adding to #Alex Redwood's answer
This is the important part:
return admin.auth().revokeRefreshTokens(uid)
.then(() => {
// Get user's tokensValidAfterTime.
return admin.auth().getUser(uid);
})
The example in the documentation has all kinds of nuanced cases, like writing a timestamp to the database to prevent reads until the current token expires, very implementation specific cases. The important part is you call revokeRefreshTokens(uid) on the correct uid, and verify the userRecord has modified the userRecord.tokensValidAfterTime value. This will not expire your active tokens. So it is valuable to have short expiry times to shorten the attack window (A better solution than a database rule that checks a timestamp in my opinion).
From: https://firebase.google.com/docs/auth/admin/manage-sessions#revoke_refresh_tokens

Use the CLI:
firebase logout --token <token>
https://firebaseopensource.com/projects/firebase/firebase-tools/#using_with%20ci%20systems

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.

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 get the state of a Token?

I'm trying to implement a way to restrict the usage of a token only once. After its first use, you must not be able to use it alright by tagging the token on its state with UsedAlready tag and Unused if its not yet used. How do I do that in apigee?. Currently I have the following condition to determine whether if the Token is used already or not.
<Step>
<Condition>(request.queryparam.state NotEquals accesstoken.state)</Condition>
<Name>RF-TokenAlreadyUsed</Name>
</Step>
Looking on my Tracetool though, apigee is throwing out that my accesstoken.state doesn't contain anything, why is that though?. My API proxy and policy for saving a default state on token creation seems to work fine anyway.
PS: Also I did the almost the same thing but using a non existing variable instead using the attribute to associate a token to a particular account. But doing the same thing with state with what I did with an attribute doesnt work. What am I doing wrong though?, am I using the state parameter of apigee right? , cuz state parameter is an optional parameter like scope and I want to make use of the state for token accesibility.
Turns out I was using the state wrong. A state should function like this as stated in this thread.
So in order for me to be able to do what I want to do is that I should add another attribute to the token so that I would know if the token is already used or not. It's not that I havent done a similar thing before using an attribute, rather I thought the use of state is for token accessibility.
Can you explain me why are you more worried about the token state? Until unless you store the token in cache, you will get a new token for your every request.
And you have a built-in policy in APIGEE that validates your token.
Even if you are storing the token in cache, you can set the expiry time for your token.
More over, the cache is used to reduce the traffic and reuse the token.
So I couldn't understand your objective, can you explain more.
#user2462133
To answer your question.
We want the token1 to be valid only only once and after that be invalid for next payment alright . But this token1 will be used for generating a new token2 to access the payment API again as the previously made token is for binding the account on first use. This new token2 will then have another state which will only be valid only once and after successful transaction, it will not be valid for another transaction again even if the token is not yet expired. For another payment, token1 will be called again and check if its still a valid token, and if so, create another token2 for another payment. But if this token1 is already expired. Then the user would have to bind his account again to our service by loggin in to his account (i.e facebook), do the OTP, etc. To be able to gain another token1 that will then only be used once for payment but multiple times for validating whether if the account is binded to our service or not.

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.

Persist user security profile data at custom Claims

My application have to fetch data from external services with the usage of manually provided at profile/management by user api key & api secret.
I'd like to prevent a huge amount of retriving those necessary keys queries to database and persist it somewhere else (assuming that those keys won't be updated too frequently).
From my point of view it could be implemented with next options:
Use MemoryCache provider with SlidingExpiration;
Create a custom Claim and append it into existing Identity claims collection;
Please correct me if I'm wrong, but if I've realized it right - claim's information is a part of data, which is used for serialization/deserialization at frontend<->backend interaction (I'm not quite confident about it, but suppose that it's used within cookies & tokens).
Actually these keys are also required for a several background processes (message queue consumers or scheduled jobs for example).
Would you mind letting me know a proper way for persiting such protected and frequently used fields in an optimized way?
Thank you in advance.
When you login using one of SignInManager's sign-in methods, it sets a cookie on the browser with an access token in it. This cookie contains claims data. So in subsequent authorized requests, you can query the User.Claims field to access the required fields without making a trip to your datastore.
Now whether you choose to use claims or not totally depends on how often you need the API Key / Secret. Your claims are part of the access token. If sending the API key / Secret on every request is justified, claims is the ideal choice.
UPDATE:
Rather than decrypting the tokens at the frontend, it's better to send them to the frontend client along with the access token.
Incase you're not aware of IdentityServer4 or OpenIddict do check them out. It's probably got all that you need.

Resources