Firebase : Prevent same account on multiple devices - firebase

I'm working on an angular app and I use Firebase to authenticate my users.
I would like to know how I could prevent my users to give their account to other people.
Also I would like to prevent people to use the same account to login from different devices at the same time.
I found some very good tutorials to build a presence system, but these system doesn't prevent the same account to be used by many different people on several devices.
I have been able to check if a user is trying tu use an account that is already in use (online) but I can't manage to log out one of those users (using an alreaydy online account..).
I tried to call auth.signout() inside the signInwithemailAndPassword() method but it doesn't work, I don't succeed in logout the users.
Thank you for your help. What I would need is a snippet because theorically, everything is very simple.

Since you didn't state what language you're using I'm just going to use Swift, but the principles behind what I laid out here are the same for any language.
Take a look at this question. It appears that Firebase does not directly support what you are looking for. You can however, do something like this:
Create a tree in your database that stores a boolean value for user signins.
SignedIn: {
uid1: {
"signedIn": true
}
uid2: {
"signedIn": false
}
.....
}
I'm assuming some where after authentication you change the screen. You'll now want to perform an additional query before doing that. If the user is already signed in you can display an alert, otherwise you can just continue as you always did.
func alreadySignedIn() {
if let uid = Auth.auth().currentUser?.uid {
Database.database().reference().child("SignedIn").child(uid).observeSingleEvent(of: .value, with: { snap in
if let dict = snap.value as? [String: Any] {
if let signedIn = dict["signedIn"] as? Bool {
if signedIn {
// display an alert telling the user only one device can use
// there account at a time
}
else {
// change the screen like normal
}
}
}
})
}
}
Of course this just prevents the account from being "shared" at the same time. You can make a stricter guideline if you only allow sign in based on a device id. For example you could get the device id and only allow sign in on that device. You'd have to allow users to update this when they get a new device, but if you really want to lock your accounts down this might be a better option.

Actually, you can't prevent your user to share their account with other people.
But, you can make sure your user can only sign in on only one device at the same time.
Normally, you can't sign out an user who already login, unless you can notify your client about the message.
But Just as #DoesData said, you can keep an sign in status data, and when the client visit the server, it can discover that it already be signed out, or others already singed in.

Related

Flutter + Firebase - Check User Presence

I am trying to remove users from database (Firebase Realtime Database) who are away or have disconnected. I did my search and the only resource I could find related to this was:
https://firebase.google.com/docs/firestore/solutions/presence
The link is a web solution, I have tried to adopt the concept to try to make it work:
#override
void initState() {
super.initState();
checkConnection();
}
checkConnection() {
databaseReference.child('.info/connected').onValue.listen((data) {
if (data.snapshot.value == false) {
return;
}
databaseReference
.child('games')
.child(inviteCode)
.child("players")
.child(playerID)
.onDisconnect()
.remove();
});
}
The above code doesn't seem to work. I have tried testing it on iOS simulator.
I am not using Firebase Authenticator. I am simply adding users directly to the Real Time Database and the structure is as follows:
games { inviteCodehere: { players: {-M_AUmwDhQBzFdPL1lsE: {name: saad, score: 0 } } } }
Would appreciate if someone could guide me. And is there a way I can define the trigger for how long the user is away? If not, what is the default value. Thank you
How do you test if it is working or not?
It can take some time for it to get triggered. Because you also search for something to indicate for how long the user is gone maybe this would help you.
You can make a combination of the onDisconnect event and a periodic is alive timestamp you save to the database each time a user does something in the app.
The RealtimeDatabase SDK for flutter is still in beta so I would recommend the is alive solution and if you need to delete some data if someone is gone for a specific time. Create a cloud function and query the timestamps older than a specific time and delete the data related to them.

Firestore rules for a survey web app and a shared link

I'm trying to build a survey web app with firebase and have a certain user flow in my mind but I don't know if it can be done:
In this app you would signup/login and build your survey, which will be stored like this:
collection("creator").doc("creatorID").collection("surveyData").
After storing the data, you would get a link (e.g. webapp.com/forUser/secret/surveyName) you then can share with your friends. This link opens the same web app (from another entry point) but with anonymous authentication, hydrating and displaying only the content of this one survey from this one user.
There are two main problems here:
Pointing the user's web app to the right data via the link and only being able to access this one survey.
Writing the user answers in the creator's files
For 1) Can I pass a secret via the link (could I use the creatorID for this or is this not safe/secure?), which points the user to this one survey data (without granting any other access)? If so, how to do this without hard coding the secret into the authentication rules?
For 2) I could use a two-step process:
The user answers the survey questions and the results are stored in something like:
collection("user").doc("secret").collection("surveyData").
When the creator opens the app later on (or done via cloud functions), the app fetches all the answers from the shared secret doc. Can this be done in one step?
I hope this makes sense somehow. Maybe my ideas are way too complicated and there is an easy way to do this or are there some best practices in this regard?
Thanks for your help!
A while ago I built a hashing package into Firebase Rules. It might be possible to use that?
Example rules:
rules_version="2"
function hashUserCreds(auth) {
let combinationOfCreds = string(auth.uid) /* + string(auth.other.stuff) */;
// hashing.sha256 will work on strings as well, but it's more readable to
// make this explicit.
return hashing.sha256(combinationOfCreds.toUtf8()).toBase64();
}
service cloud.firestore {
match /databases/{database}/documents {
match /{ownerHash}/{surveyId} {
function isOwner() {
return hashUserCreds(request.auth) == ownerHash;
}
allow read;
allow write: if isOwner();
match /{responseId} {
function isResponder() {
return hashUserCreds(request.auth) == responseId;
}
allow update: if isResponder();
allow read, create: if isOwner();
}
}
}
}
If you want open-responses then you change the final two permissions, but it sounds like you want the survey creator to choose who can respond which I've tried to express there.
Using hashes instead of names will provide better anonymity, obscure PII, and force the restriction that a user can only edit their data.

I can't get "Find personal time entry in progress" using clockify API

I will try to get those users whose time tracking start currently in clockify
and I'm trying to use the following API endpoint to get the user:
How to get user list using this endpoint?
I have made custom logic for check user is currently working or not
I have made a loop that checks the user is in progress or not.
e.g
public getClockifyBaseWorkerStatus(clokifyApiKey: string) {
let headers = new HttpHeaders().set('X-Api-Key', clokifyApiKey ? clokifyApiKey : environment.clokifyApiKey);
return this.http.get<any>(`https://api.clockify.me/api/workspaces/${environment.clockifyWorkSpace}/timeEntries/inProgress`, { headers })
.pipe(map((data) => { return data; }));
}
so, in this function, I have pass different clokifyApiKey and check the status of the user is currently working or not.
I hope this answer will help other people in the future.
The GET /workspaces/{workspaceId}/timeEntries/inProgress only returns your own currently running timer, not the entire workspaces, per the API docs (https://clockify.github.io/clockify_api_docs/#tag-Time-entry). So you can't get a list of active timers in the workspace other than for yourself. From what I can tell it is not possible to get a list of users with active timers.

Firebase security - Transactions

Using Transactions in Firebase is a great way to atomically modify the data, but how do I know that the user actually uses my code to insert data?
For example, what if the user gets a reference to the data location (using the browser console) and overwrites the previous data using set rather than clicking on the my pre-designed button which uses transaction in the background?
Update (an example):
var wilmaRef = new Firebase('https://docs-examples.firebaseio.com/samplechat/users/wilma');
wilmaRef.transaction(function(currentData) {
if (currentData === null) {
return { name: { first: 'Wilma', last: 'Flintstone' } };
} else {
console.log('User wilma already exists.');
return; // Abort the transaction.
}
});
Now, what if the user uses:
wilmaRef.set({name: { first: 'Wilma', last: 'Flintstone' }});
The Firebase Database has no way to ensure that it's a specific piece of code that makes a modification. See my answer to this question for more on why knowing the URL of a resource is not a security risk: How to restrict Firebase data modification?
Firebase security works based on knowing who the user is and allowing them specific read/write operations based on that knowledge. Once you take that mindset, it doesn't matter if someone uses a JavaScript console to make changes to the database that is behind your Android app. As long as the JavaScript follows the rules that you've set for the user that runs it, the changes to the database are permitted.

How do I get the number of children in a protected Firebase collection?

I have a protected firebase collection for users of my site, just an array of user objects. The permission rules for users allow an authenticated user to access only their user object in the list of users and no one else.
I'm trying to setup a simple way to get the count of all users in the collection with this permission scheme so that I can display a total user count on my site, however there doesn't seem to be a way to get a count of all users without getting a permission problem.
Any ideas about how to fix this?
I suppose I could store a count at a publicly readable firebase location that gets incremented and decremented whenever a user is added/removed, but I'd rather not store the data twice and worry about mismatches.
I suppose I could also have an authenticated watcher on my server that bypasses the permission requirement and sends to the client (either through firebase by writing to public location or exposed as an api) a user count.
Ideally I'd like to have everything client side at the moment, so please let me know if there's a simple permissions based solution to this.
Thanks!
Data duplication is pretty much the norm in NoSQL, so storing a counter is perfectly reasonable. Check out the Firebase article on denormalization
This pretty much sums up the approaches as I understand them.
Using a counter
It's fast and it's fairly simple, assuming you're using good DRY principles and centralizing all your manipulations of the records. Utilize a transaction to update the counter each time a record is added or removed:
function addUser(user) {
// do your add stuff...
updateCounter(1);
}
function removeUser(user) {
// do your remove stuff...
updateCounter(-1);
}
function updateCounter(amt) {
userCounter.transaction(function(currentValue) {
currentValue || (currentValue === 0); // can be null
return currentValue + amt;
});
}
Separate public and secured data
Store sensitive data (email addresses, things people can't see) in a private path, keep their public user data readable.
This prevents the need to synchronize a counter. It does mean, however, that clients must download the entire list of public users to create a count. So keep the public profiles small (a name, a timestamp, not much else) so it works into the tens of thousands without taking seconds.
"users": {
".read": true,
"$user": {
// don't try to put a ".read" here; it won't remove access
// after the parent path allows it
}
}
"users_secured": {
"$user": {
".read": "auth.id === $user"
}
}
Utilize a server process
Easy and painless; uber fast for clients, easily handles hundreds of thousands of profiles as long as they have a small footprint. Requires you to maintain something. Heroku and Nodejitsu will host this for free until you have users coming out of your ears.
var Firebase = require('firebase');
var fb = new Firebase(process.env.FBURL);
fb.auth( process.env.SECRET, function() {
fb.child('users').on('value', function(snap) {
fb.child('user_counter').set( snap.numChildren() );
});
}

Resources