Firebase logout clients when email or password changed - swift

I have a functionality in my app where the users can change their password or update their email address.
According to this document https://firebase.google.com/docs/auth/admin/manage-sessions i would assume that when the same user is logged in on another device at the same time that the refresh token should be revoced when the password changes and the user should be logged out.
It seems that this does not happen in my case. I use these two listeners to detect the changes:
addStateDidChangeListener
addIDTokenDidChangeListener
They are normally working when I login/logout via the Firebase login and logout functions but they don't get called when I change the password on another device.
Is there any way to get notified when a user change the password on another device?

Firebase Authentication uses ID tokens to maintain the authentication state on the client, and these tokens are valid for one hour. Only once the SDK needs to refresh the token (which it automatically does about 5 minutes before the token expires) is it guaranteed to detect the change, so that may take up to an hour.
If you want to force refreshing of the token before it expires, you can call reload on the User object.

Related

Next-auth (JWT) logging sessions

According to Next-auth documentation as we are using credentials provider to connect to our users collection for usernames and passwords Next-Auth does not use the session database to check if a session is active.
If you use a custom credentials provider user accounts will not be persisted in a database by NextAuth.js (even if one is configured). The option to use JSON Web Tokens for session tokens (which allow sign in without using a session database) must be enabled to use a custom credentials provider.
I am wanting to add a _middleware that will allow me to store and check that the latest JWT sessions inside our session database matches the latest one that the user is currently using.
Reason being is that if I have two devices technically I would be able to login on both devices and at the moment their is no real way to discern if the user from PC2 is also login on PC1.
So my theory and not sure if this will work is to add the following.
callbacks: {
jwt: async ({ token, user }) => {
console.log("running JWT - because of custom login")
user && (token.user = user)
(ADD CODE HERE TO SAVE TOKEN & CHECK IF TOKEN IS LATEST TOKEN + VALID - INSIDE SESSION DATABASE)
(IF OLD-TOKEN IS NO LONGER VALID OR THE LATEST TOKEN LOG THE USER OUT)
console.log("TOKEN IS "+ JSON.stringify(token))
return token
},
session: async ({ session, token, user }) => {
console.log(JSON.stringify(session) +" / "+ JSON.stringify(token) +"/"+ JSON.stringify(user));
session.user.tokenID = token //ADD CODE HERE TO SAVE TOKEN TO SESSION COOKIE
session.user = user
return session
}
},
Then if I create a middleware that checks this tokenID and matches it with the session database and if it is the latest result from said user.
For example.
Say PC1 (user1) login here
{
_id: 1
tokenID: 918171-918171-81716-0887
userid: 00-00-00-001
expire: "2022-05-23T12:47:04.593Z"
}
But then PC2 also (user1) login again and created a new session
{
_id: 2
tokenID: 71888-651777-616666-0117
userid: 00-00-00-001
expire: "2022-05-24T12:47:04.593Z"
}
What I would need the middleware to do (which a simple mongodb query could do) is check if their is an older session stored for the same userID if so then logout from PC1.
Now there are a few things that I can see going wrong with this idea.
Other Provider Sessions (which use session DB) making it harder to validate
Every time you call the signup page or session it seems to re-run the JWT section - which in theory is fine, as we could use a findOne Update function which if token is in session then just update the expiry - however that would cause say PC1 refreshes after PC2 logged in then PC1 expire time might be longer then PC2 expire time (but a simple sort function would allow us to see if the ID was older then PC2 if so logout).
JWT changes token every time you reload the page
How would this help privacy and the user data?
By not storing the user details inside a session cookie we would not be exposing the data to hackers or other plugins like FB or Google as the user data would only be linked to a token ID. Which to request the user data you would have to make sure the tokenID was valid first and then be allowed to fetch user data.
I understand that Next-Auth may not want too do this, and this is why I ask the question what is the best practice to do what I am wanting to achieve.
This answer is based on the confirmation that the issue is that you want to be able to only have users able to be signed in to one computer/device at time and that you are using usernames and passwords to authenticate them.
In that scenario you also need to have a database that records a token in each JWT issued. It is not possible to solve for that problem without a database.
Here is how you can solve for it it using JWT and a database:
On every new sign in, you would need to use the jwt callback to add something like a UUID to each JWT and then record that UUID , the User ID and the time the JWT expires in a database.
In that callback, if there are other entries in the database for the same User ID you should mark them as invalid (or delete them from the database).
Everytime an existing JWT is read in that same callback you would need to check to see if the UUID in the database was still valid (i.e. still exist / doesn't point to a UUID that corresponds to a JWT flagged as expired) and if it is no longer valid, don't return a valid JWT.
You might also want to add special handling in the session callback that does something similar to improve the user experience by gracefully handling it in the User Interface of the computer they are being signed out of.
Effectively this has all the downsides of a JWT with all the downsides of a session database (although there are niche cases where this is a preferable approach).
I would not recommend using usernames and passwords or limiting users to only being able to sign in to one computer at at time, however given those unusually specific constraints (which also necessitates a solution that has a negative impact on performance) you may want to consider a different authentication solution and/or think how else you could address the underlying need this is trying to address (and if it's worth the cost and complexity).

How to properly use refresh token on different devices?

After successful authorization on the client, two tokens are saved in cookies, one of them is access the other is refresh, when the access token expires, the client sends a refresh and the server issues two new tokens to the client, the point is, the fact is that if the user logs in from another device, for example, from a phone , then the refresh token on the first device will already be invalid. Refresh token is stored in the database table users, in the table fields: email, password, refresh_token.
That is, when authorizing on another device, the refresh_token field will be updated and on the first device, the refresh token is already invalid in the cookies. How to make it possible to use the refresh token, not only on one device? I saw that I needed to do something with the device identifier, but did not understand, I also did not understand where to get this device identifier. I would be grateful if you describe in detail how to properly organize this process.

FIRAuthErrorDomain Code=17014 error when deleting from firebase auth b/c not re-logged into my iOS app (swift)?

I'm building an app that uses firebase auth (and firestore database) but only facebook login.
The user only has to login once with my app and from then on when they continue to open the app I check
if Auth.auth().currentUser != nil
{
}
And so if there is a logged in user already then i don't make them re-login.
But when they want to delete their account from the app the error message pops:
Error Domain=FIRAuthErrorDomain Code=17014 "This operation is sensitive and requires recent authentication. Log in again before retrying this request." UserInfo={NSLocalizedDescription=This operation is sensitive and requires recent authentication. Log in again before retrying this request., error_name=ERROR_REQUIRES_RECENT_LOGIN}
Do I really need to get the user to login again? Because that means i'll have to get them to re-login with facebook and sending them back to fb's page so they can delete their account seems overkill. Is there a token I can just back to confirm login?
This is possibly a separate question...but when i deleted the user directly from firebase console so there were no users left in the system I'm pretty sure from the app sitting on the client
Auth.auth().currentUser was not nil and still contained the uid of the users account that i deleted. Not entirely sure why that is.
Thanks.
This is done for security reasons. Reauthentication is a sensitive operation. Same as when you try to update your password (you need to enter the old one), update your email, update a credit card, shipping address for an e-commerce app, etc. It may not be convenient but it is necessary for the user's security.
For good practice, you should enforce this. However, if you need to delete a user from your server, you can always use Admin SDK admin.auth().deleteUser(uid) API: https://firebase.google.com/docs/auth/admin/manage-users#delete_a_user
Separately, you need to either reload the user or try to refresh the token getIDToken to force it to detect the deletion of the user.

Tracking multiple logins from the same user with Stormpath

I'm working on a mobile application that uses Stormpath on the server side for authentication and authorization. I need to support the same user signing in on more than one device, but I want to be able to keep track of it and limit it if I want to.
My application currently uses Stormpath to sign in the user using email/password or MDN/password and upon successful login returns a JWT token to use for API access to the server.
I'm thinking of the following approach:
Keep a list of sessions in the user's account. Every time the user signs in, a new entry is added with the device_id and the JWT provided. When the user signs off, the entry is removed or marked inactive.
When a user tries to sign in on another device, if I want to restrict to only one active device, I would set the other entry to disabled and expire the JWT so the application can detect it and require login again.
If I wanted to restrict the user to a maximum of n sessions, I could just count the entries and force the user to sign off on one of the other sessions before allowing her/him to sign in on the new device
Is this a good approach? Is there a better way to do it? What are the issues with this method?
I work at Stormpath on the mobile SDKs. You can use the access / refresh token feature that we have to accomplish this.
Every time the user signs in, an access and refresh token are created. When the user signs off, the refresh token is deleted, as well as the access token.
When a user tries to sign in on another device, if you want to restrict to only one active device, you can delete all of the other access & refresh tokens.
If you wanted to restrict the user to a maximum of n sessions, I could just count the entries and force the user to delete one of the refresh tokens before allowing her/him to sign in on the new device. You would then go through the access tokens, and delete ones with a matching "rti" (refresh token ID)
Several notes about implementing this:
If you're using a Stormpath Framework integration, the default is to verify an access token locally (instead of sending it to Stormpath to validate). This is because they have a signature that can be validated by the SDK. However, to log out a user, you'll either have to set this to remote validation, or use a short access token life (and use the refresh tokens to control each "session")
Refresh tokens can't store "customData", so you'll have to maintain metadata about the refresh token in either the account's customData, or in your own database.
Alternatively, you could "create" API Keys for each user, and use that instead of sessions for each user. You can use the API Key name or description attributes to keep track of where the user signed in from / etc.

Play Framework - Disconnect an user

I'm doing a Scala - Play application and I want to disconnect an user when an admin change his right. For exemple, is an user is logged and an admin upgrade his account to the admin type, I want disconnect this user.How can I do that ?
If you stored the userId in the Session you will need to add the rights of the user in the Session.
So that when the user connects, you can check his rights from the session to the ones in your database. If they don't match you can redirect the user to the login page.
just to cover all the bases since the answer here depends specifically on how your application determines if a user is logged in:
if user auth is done with a token generated by a secret that is stored on your user model and checked for validity on every request, you can generate a new secret for the user and all existing tokens will become invalid.