How to Prevent Simultaneous Logins of the Same User with Firebase

How to prevent simultaneous logins of the same user with Firebase?

The general idea is that you want to create some meta data in Firebase which tells you how many locations a user is logged in from. Then you can restrict their access using this information.

To do this, you'll need to generate your own tokens (so that the information is available to your security rules).

1) Generate a token

Use custom login to generate your own tokens. Each token should contain a unique ID for the client (IP Address? UUID?)

var FirebaseTokenGenerator = require("firebase-token-generator");
var tokenGenerator = new FirebaseTokenGenerator(YOUR_FIREBASE_SECRET);
var token = tokenGenerator.createToken({ id: USER_ID, location_id: IP_ADDRESS });

2) Use presence to store the user's location_id

Check out the managing presence primer for details:

var fb = new Firebase(URL);

// after getting auth token back from your server
var parts = deconstructJWT(token);
var ref = fb.child('logged_in_users/'+token.id);

// store the user's location id
ref.set(token.location_id);

// remove location id when user logs out
ref.onDisconnect().remove();

// Helper function to extract claims from a JWT. Does *not* verify the
// validity of the token.
// credits: https://github.com/firebase/angularFire/blob/e8c1d33f34ee5461c0bcd01fc316bcf0649deec6/angularfire.js
function deconstructJWT(token) {
var segments = token.split(".");
if (!segments instanceof Array || segments.length !== 3) {
throw new Error("Invalid JWT");
}
var claims = segments[1];
if (window.atob) {
return JSON.parse(decodeURIComponent(escape(window.atob(claims))));
}
return token;
}

3) Add security rules

In security rules, enforce that only the current unique location may read data

{
"some_restricted_path": {
".read": "root.child('logged_in_users/'+auth.id).val() === auth.location_id"
}
}

4) Control write access to logged_in_users

You'll want to set up some system of controlling write access to logged_in_users. Obviously a user should only be able to write to their own record. If you want the first login attempt to always win, then prevent write if a value exists (until they log out) by using ".write": "!data.exists()"

However, you can greatly simplify by allowing the last login to win, in which case it overwrites the old location value and the previous logins will be invalidated and fail to read.

5) This is not a solution to control the number of concurrents

You can't use this to prevent multiple concurrents to your Firebase. See goOffline() and goOnline() for more data on accomplishing this (or get a paid plan so you have no hard cap on connections).

How to prevent multiple account for same user in Firebase?

I was able to solve this.

1. Login Social account (Account_2)
2. Try to link Mobile No
If(Success)
Account (Step1) updated with Mobile No
Else
Login with Mobile No (Account_1)
Link Social account (Account_2) created in step1 with Account_1

Sample java script code:

    <script>
var firebaseConfig = {
apiKey: "XXXX",
authDomain: "XXXXXX",
databaseURL: "XXXXXX",
projectId: "XXXXXX",
storageBucket: "XXXXXXXXX",
messagingSenderId: "XXXXXXX",
appId: "XXXXXXXX",
measurementId: "XXXXXXXXXXXX"
};

firebase.initializeApp(firebaseConfig);
firebase.analytics();

var provider = new firebase.auth.FacebookAuthProvider;

var phoneAuthProvider = new firebase.auth.PhoneAuthProvider;

firebase.auth().useDeviceLanguage();

provider.setCustomParameters({
'display': 'popup'
});

var globalCred ;

firebase.auth().signInWithPopup(provider).then(function(result) {

var token = result.credential.accessToken;

globalCred = result.credential;

console.log("GLOBAL :" + globalCred);

var user = result.user;

var prevUser = firebase.auth().currentUser; //Logged in via FB

console.log(prevUser);


if (!user.phoneNumber) {
// Ask user for phone number.
var phoneNumber = window.prompt('Provide your phone number');
// You also need to provide a button element signInButtonElement
// which the user would click to complete sign-in.
// Get recaptcha token. Let's use invisible recaptcha and hook to the button.
var appVerifier = new firebase.auth.RecaptchaVerifier(
signInButtonElement, {size: 'invisible'});
// This will wait for the button to be clicked the reCAPTCHA resolved.
return result.user.linkWithPhoneNumber(phoneNumber, appVerifier)
.then(function(confirmationResult) {
// Ask user to provide the SMS code.
var code = window.prompt('Provide your SMS code');
// Complete sign-in.
return confirmationResult.confirm(code);
})
}

}).catch(function(error) {

var errorCode = error.code;
var errorMessage = error.message;

var email = error.email;

var credential = error.credential;

if(errorCode == 'auth/credential-already-in-use'){
// Merge accounts
console.log("Trying Linking");

var prevUser = firebase.auth().currentUser;

prevUser.delete().then(function() {
console.log("FB user deleted");
}).catch(function(error) {
// An error happened.
console.log("Error in FB user deletion");
});

firebase.auth().signInWithCredential(credential).then(function(result) {
console.log("Sign In Success using Phone", result);
var currentUser = result.user;

firebase.auth().currentUser.linkWithCredential(globalCred);

});

}).catch(function(error) {
// If there are errors we want to undo the data merge/deletion
console.log("Sign In Error", error);
//repo.set(prevUser, prevUserData);
});

}

});

</script>

Firebase : Prevent same account on multiple devices

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.



Related Topics



Leave a reply



Submit