Firebase authentication limit login by the same user

后端 未结 3 2135
感情败类
感情败类 2020-12-06 22:11

is there a way in Firebase to limit the number of logins by the same user at the same time, says I want to put a limit of 3 devices at a time for one single account, how can

相关标签:
3条回答
  • I came around similar situation and then I implemented bojeil approach for limiting simultaneous login using one user credentials.

    I have maintained following array field and number field in user document.

    {
        allowed_auth_times : [
    
        ],
        max_granted_login : 3
    }
    

    Add entry into this array on

    1. user signup.
    2. If another person logs into the system using same credentials (either on same or different device) if size(allowed_auth_times) < max_granted_login

    If size(allowed_auth_times) == max_granted_login then remove oldest auth time, (allowed_auth_times[0]) and then add entry of new auth time.

    This way you will make sure that, at any given point of time, limited users will be able to use the system. I have implemented this using Angular + AngularFire2 I will be more than happy if anyone can provide suggesting for improving this. Thanks in advance.

    Authentication Component :

    trySignInWithEmailAndPassword() {
    
        // Actual Signin process starts here.
        this._firebaseAuthenticationService.signInUserWithEmailAndPassword(this.userCredentials.value)
          .then((user) => {
            this._firebaseAuthenticationService.OTCheckAndUpdateDBAuthTime().then((result) => {
              bootbox.hideAll();
              if (result === true) {
                this._router.navigate(['/home']);
              }
            }, (err) => {
              bootbox.hideAll();
              bootbox.alert("Error Occured. Please contact support team with screenshot. " + err);
            });
          }).catch((err) => {
            console.error("Login failed. Redirecting user to authentication page.");
            bootbox.alert("Some Error Occured While Authenticating " + this.userCredentials.get("email").value);
            this._router.navigate(['/authentication']);
          });
      }
    

    FirebaseAuthService

    import { Injectable, OnInit } from '@angular/core';
    import { AngularFireAuth } from '@angular/fire/auth';
    import * as firebase from 'firebase/app';
    import { Router } from '@angular/router';
    import { Observable } from 'rxjs';
    import { AngularFirestore } from '@angular/fire/firestore';
    import { environment } from 'src/environments/environment';
    import { first, take } from 'rxjs/operators';
    
    @Injectable({
      providedIn: 'root'
    })
    export class FirebaseAuthenticationService implements OnInit {
      ngOnInit(): void {
      }
    
      uid: string = "";
      constructor(private _angularFireAuth: AngularFireAuth,
        private db: AngularFirestore,
        private _router: Router) { }
    
      trySignOut() {
        return new Promise<any>((resolve, reject) => {
          this._angularFireAuth.auth.signOut().then(() => {
            resolve();
          }, (error) => {
            reject(error);
          }
          );
        });
      }
    
      getCurrentUserUID(): string {
        if (this._angularFireAuth.auth.currentUser != undefined || this._angularFireAuth.auth.currentUser != null) {
          let uid = this._angularFireAuth.auth.currentUser.uid;
          if (uid == undefined || uid == null) {
            return "";
          } else {
            return uid;
          }
        }
        return this._angularFireAuth.auth.currentUser.uid;
      }
    
      getCurrentUserUID2(): Observable<firebase.User> {
        return this._angularFireAuth.authState.pipe(first());
      }
    
      createUserWithEmailAndPassword(userCredentials: any) {
        return new Promise<any>((resolve, reject) => {
          this._angularFireAuth.auth.createUserWithEmailAndPassword(userCredentials.email, userCredentials.password)
            .then((userData) => {
              userData.user.getIdTokenResult().then((a) => {
                console.log("Auth Time : " + a.authTime);
                resolve(userData.user);
              });
            });
        });
      }
    
      signInUserWithEmailAndPassword(userCredentials: any) {
        return new Promise<any>((resolve, reject) => {
          this._angularFireAuth.auth.signInWithEmailAndPassword(userCredentials.email, userCredentials.password)
            .then((userData) => {
              console.log(userData);
              // If email is not verified then send verification email every time.
              if (this._angularFireAuth.auth.currentUser.emailVerified == false) {
                this.sendEmailVerification();
              }
              userData.user.getIdTokenResult().then((a) => {
                console.log("Auth Time : " + a.authTime);
                resolve(userData.user);
              });
            }, err => {
              console.error("Error Occured During Signin user with email and password in auth service.");
              console.error(err);
              reject(err);
            });
        });
      }
    
      // This function checks entry in allowed_auth_times [] depending on max_logins : in user profile.
      OTCheckAndUpdateDBAuthTime() {
        return new Promise<any>((resolve, reject) => {
          this._angularFireAuth.authState.subscribe((userAuthState) => {
            if (userAuthState.uid != null || userAuthState.uid != undefined || userAuthState.uid != "") {
              const user_doc_id = userAuthState.uid;
              this.db.collection("users").doc(user_doc_id).snapshotChanges().subscribe((docData) => {
                let doc: any = docData.payload.data();
                let allowed_auth_times_arr: string[] = doc.allowed_auth_times;
                let max_granted_login: number = parseInt(doc.max_granted_login);
                this.getAuthTime().then((currentUserAuthTime) => {
                  if (allowed_auth_times_arr && allowed_auth_times_arr.includes(currentUserAuthTime)) {
                    resolve(true);
                  } else {
                    if (allowed_auth_times_arr) {
                      if (allowed_auth_times_arr.length == max_granted_login) {
                        allowed_auth_times_arr.splice(0, 1);      // Delete Oldest Entry
                      }
                      allowed_auth_times_arr.push(currentUserAuthTime);
                      this.updateDBAuthTimesArr(userAuthState.uid, allowed_auth_times_arr).then(() => {
                        resolve(true);
                      }, (error) => {
                        bootbox.alert("Error Occured While Updating Auth Times. Please take screenshot of this and contact June Support team. " + error);
                        reject(false);
                      });
                    }
                  }
                });
              });
            }else{
              console.error("Authentication Service > OTCheckAndUpdateDBAuthTime > userAuthState is blank");
            }
          });
        });
      }
    
    // Update 
    
      updateDBAuthTimesArr(uid: string, allowed_auth_times_arr: string[]) {
        return this.db.collection(environment.collctn_users).doc(uid).update({
          allowed_auth_times: allowed_auth_times_arr
        });
      }
    
    // Get Auth Time of Currently Signed in User
    
     getAuthTime() {
        return new Promise<any>((resolve, reject) => {
          try {
            this._angularFireAuth.authState.pipe(take(1)).subscribe((userAuthState) => {
              if (userAuthState) {
                userAuthState.getIdTokenResult().then((tokenResult) => {
                  console.log("Token result obtained. ");
                  console.log("Auth time obtained : " + tokenResult.authTime);
                  resolve(tokenResult.authTime);
                });
              } else {
                console.error("Blank UserAuthState Captured.");
                reject(null);
              }
            });
          } catch (err) {
            console.error("Error Occured while obtaining the auth time of the user.");
            reject(null);
          }
        });
      }
    
    **// You will be using this in other components. If there is entry of authTime in allowed_auth_times array then keep this user signed in, otherwise signout forcefully.**
      validateAuthTime(uid: string): Observable<boolean> {
        return new Observable<any>((observer) => {
          this.db.collection(environment.collctn_vendor_list).doc(uid).snapshotChanges().subscribe((docData) => {
            let doc: any = docData.payload.data();
            this.getAuthTime().then((currentUserAuthTime) => {
              if (doc.allowed_auth_times.includes(currentUserAuthTime)) {
                observer.next(true);
              } else {
                console.log("ValidateAuthTime > else : Observer returning false.");
                observer.next(false);
                //this.trySignOut().then(() => {
                  //this._router.navigate(['/authentication']);
                //});
              }
            });
          });
    
        });
      }
    
    
    }
    
    0 讨论(0)
  • 2020-12-06 22:56

    You should use the Database (Realtime Database or Firestore) to save user’s devices and check from there if he can login.

    0 讨论(0)
  • 2020-12-06 22:59

    That is not supported by Firebase. The best you can do is keep track of the token auth_time. This is the time of sign-in. You would keep a queue of 3 entries for that. Each time you sign-in a user, send their ID token for verification, add the auth_time to the queue (if it is not there already) and dequeue the oldest auth_time in the queue if it exceeds it maximum size (3) . You would only allow access to data for ID tokens with auth_times within that queue.

    0 讨论(0)
提交回复
热议问题