I use the Firebase realtime database in my Android app where disk persistence is enabled.
When the user opens the app for the first time I read auth status, if that is null I show the login screen and after the login, I will get the username stored against this user id in the realtime database.
If the username is null then I will show username setup screen where he will choose the unique username for him. The problem arises when the connection goes off during the username setup phase. username is not yet written to the database but when he opens the app next time username will not be null as Real-time database gives me value stored in the cache. So I proceed to the home screen but what if someone else chooses the same username during this phase?
I maintain two-way mapping between uid and username as suggested in this answer
I can't use transaction because at a time transcation can be run only on one path but in my case, I have to automatically update two paths.
I also have security rules setup up for maintaining unique username but what to do with the users who are already crossed the username setup screen.
What if someone else chooses the same username during this phase?
Everytime you want to perform a write operation for a user name, you need to make sure it's unique. To check if a user name exists in a Firebase realtime database, a query like this is required:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
Query query = rootRef.child("users").orderByChild("userName").equalTo(userName);
query.addValueEventListener(/* ... */);
So if the user tries to set/change the user name while offline, the above query is added to a queue. Once the user regains the connection, every change that is made while offline, will be updated on Firebase servers. With other words, the above query will be commited on the server. So if a user chooses a user name that already exists in the database while offline, when is back online, he'll receive a message that user name already exists.
I can't use transaction because at a time transcation can be run only on one path but in my case, I have to automatically update two paths.
I cannot use transaction because it can be run only on one path, you cannot use transactions at all. Transactions are not supported for offline use. This is because a transaction absolutely requires round trip communications with server in order to ensure that the code inside the transaction completes successfully. So, transactions can only execute when you are online.
Related
In an Android project I use Firebase with signInAnonymously() and I am getting userId like this
userId = FirebaseAuth.getInstance().getCurrentUser().getUid()
and I use the userId to create children nodes in Firebase Realtime Database that only this user can access based on the access rules of that database.
The problem is that I noticed userId changes randomly and when that happens all content created by that user is lost to them. Is there something I can do to keep the same userId until the app is uninstalled? What other way can I use to ensure steady and exclusive access for that user to a Realtime database child? Can installation id be used?
You can use Firebase Anonymous Authentication to create and use only temporary accounts to authenticate with Firebase. The Anonymous Authentication accounts don't persist across application uninstalls. When an application is uninstalled, everything that was saved locally will be wiped out, including the anonymous auth token that identifies that account. So there is no way you can reclaim that token for the user. So each time you sign in, a new UID is generated.
What other way can I use to ensure steady and exclusive access for that user to a Realtime Database child?
To always have the same UID, then you have to implement the Firebase Authentication with a provider like Google, Facebook, Twitter, and so on.
Changing the email address of the currently logged in user works just fine using the updateEmail method of the user object, however it appears as if that change is not replicated to other logins, which still keep the previous email address.
Is there any notification I need to subscribe to / catch in order to be notified by such a change or is there another way to accomplish that?
Me, as a long time Firebaser, I read the docs a lot, like a lot. And I do have a workaround for this since there aren’t any built in.
First, if you provide multiple sessions per user, you’d want to use the reauthenticate method.
Re-authenticate a user
Some security-sensitive actions—such as deleting an account, setting a
primary email address, and changing a password—require that the user
has recently signed in. If you perform one of these actions, and the
user signed in too long ago, the action fails with the
FIRAuthErrorCodeCredentialTooOld error. When this happens,
re-authenticate the user by getting new sign-in credentials from the
user and passing the credentials to reauthenticate. For example:
let user = Auth.auth().currentUser
var credential: AuthCredential
// Prompt the user to re-provide their sign-in credentials
user?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
Using this, you’d get the error 90% percent of the time because of old credentials.
Or:
(1) On the change process. Set a specific key for the user on your real-time database or Firestore,
(2) Check for that specific key on your app, if existed. That means the user needs to be re authenticated. Firebase will update the user credential on every new login.
I hope it helps.
Here’s the docs:
Manage users on Firebase
Following are the two approaches to validate the current action
Save user details on login in the user session. For every action after login, check whether user has roles and permissions every time. DB needs to be queried since an administrator can change the privilege of the user any time. Query the roles and permissions of the current user and verify the operation every time.
Save user details on login in the user session. For every action after login, check just the roles/permission associated with the session. DB doesn't needs to be queried. However if Administrator or any user changes the permission of the current user from different country invalidate the current user session which should be stored somewhere.
Which of the above would be the best approach on the above ? How to achieve it
Many applications hit the DB for every action.
Some application invalidate the session...
Any reference or code snippet for the above two would be helpful
Not sure whether any other approach is available other than the two
You could employ a hybrid approach: cache roles+permissions in user session for N minutes, then invalidate cache and reload them from the database. Thus, user permissions would be incorrect for no longer than N minutes.
Another approach is to store last update date+time per role in memory and in user session. When an administrator updates role permissions, you set 'update date+time' for this role to 'now'. Also, for each user action, you first check whether 'load date+time' for their role stored in their session is older than that global update date+time stored in memory. If it is older, then you need to reload role and its permissions from database.
This will only work well if
You only have one application instance (no distribution), or you can share that data a lot faster than access the database (some kind of a distributed cache?)
Permissions are changed by administrator via the same application
Your security model is not too complex
Imagine that you have an object roleUpdateTimes that contains a map from a role to its last update date+time (or null if the role was not updated since last restart).
When a role is updates by an administrator, the following is done:
roleUpdateTimes.updated(roleName);
where updated() method just puts the current date to the map:
public void updated(String roleName) {
map.put(roleName, new Date());
}
map may be a ConcurrentHashMap here, so that we have a correct behavior knowing that the map will be accessed by different threads.
For the user, you could have the following in a Filter:
Date roleDate = getRoleDateFromSession(session);
String roleName = getRoleNameFromSession(session);
if (roleUpdateTimes.roleIsStale(roleName, roleDate)) {
... reload role permissions from database and save them to session
saveRoleDateToSession(session, new Date());
}
And
public void roleIsStale(String roleName, Date snapshotDate) {
Date updateDate = map.get(roleName)) {
if (updateDate == null) {
// role was not changed by admin, it's not stale
return false;
}
return updateDate.after(snapshotDate);
}
Also you might need to load map on startup: just put new Date() for every known role.
I want to know the logic and how i can implement it in netbeans. I want to create a login option in simple java application in netbeans via connectivity of mysql.
What I used to do is Preferences for checking whether user status is logged in or not
if user is already logged in then I don't lunch the login interface
else lunch login interface
in log in interface simply take input from user along with primary key (may be email address). and query to database whether it is available in database or not.
If user is available the data of particular user is displayed and preference for user logged in status should changed to logged in.
If user is not available then show some error message.
you can add sign up features where you can simply do simple input validation such as age range, email etc and finally insert the data input by the user.
i have one java web application in jsp and servlet and db as oracle 10g EE. In login if one user has been logged in then how can i prevent same user from logging again unless sign out?
Note: I am not telling that if a logged in user will click on login page then immediately he would be forwarded to his home page.
I am asking is how can i prevent that logged in user to login again if he is already logged in. Suppose user A is already logged in into the db(sign out not done), then user B tries to login in to db with the user id and password of user A, then simply user B will be prevented from login. How do i implement that?
You need to manage a map of logged in users, Map<String, String> userToSessionIdMap when user logs in you check if there is any session exist for this user name if yes deny else allow login,
Now on logout you need to remove the entry from map, also you need to implement HttpSessionBindingListener so when session expires it removes the entry again
Also See
prevent multiple login using the same user name and password
Preventing multiple login
Let the database do it's own job!
(This solution assumes that you can get help from DBAs; or you have SYSDBA access to the database.)
First create a profile that allows only 1 session per user:
CREATE PROFILE single_session_profile
LIMIT SESSIONS_PER_USER 1;
Then create the user with the right profile or alter an existing user to use the profile:
CREATE USER user_a
IDENTIFIED BY user_id
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp
QUOTA UNLIMITED ON users
PROFILE single_session_profile;
or
ALTER USER user_a
PROFILE single_session_profile;
Finally, the database needs to be observe these limits:
ALTER SYSTEM SET RESOURCE_LIMIT = TRUE SCOPE = MEMORY;
(SCOPE = MEMORY enables limit enforcement for the currently running database instance; if you want to make it persistent, i.e. enabled after a database restart, you need to use SCOPE = BOTH where BOTH means both MEMORY and SPFILE, i.e. DB initialization file. If the database does not use the new SPFILE format, but the old PFILE (init.ora), then you need to add the RESOURCE_LIMIT setting to the init.ora and restart the database.)
That's it. If a user_a tries to log in twice, it will get:
ORA-02391: exceeded simultaneous SESSIONS_PER_USER limit