Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 5 AngularFire Firebase Login Flicker

I'm using AngularFire2 to create a login system with Firebase and Angular 5. When I click login and logout, everything works well. The one issue is that I get a flicker if I refresh the page as shown below. Obviously this happens because the javascript takes time to load. What's the best way to deal with this problem? Usually I create login systems in PHP or some other backend language, so I never have this problem because I just check SESSION variables for login, and the pages don't finish loading until that happens. Here the page loads immediately, and then Javascript checks if you're logged in.

I know I could using a loading spinner, but I don't like websites that use that trick. Another possibility is using Javascript cookies or JWT. I just want to find out what's best practice.

Also isn't having a login system using Firebase and Angular not secure, because users could just change the Javascript variables? For example users could just set afAuth.user. The private user dashboard checks if this variable is set, and lets the user view this page if it is. So couldn't someone just get into the private dashboard by setting the afAuth.user variable. I understand that I can protect Firebase data using rules, and can ensure that users are logged in before accessing the database. But it seems like HTML code can't be hidden from users if you use Firebase for authentication with frontend Javascript.

enter image description here

Here's app.component.html

    <div *ngIf="afAuth.user | async as user; else showLogin">
      <h1>Hello {{ user.displayName }}!</h1>
      <button (click)="logout()">Logout</button>
    </div>
    <ng-template #showLogin>
      <p>Please login.</p>
      <button (click)="login()">Login</button>
    </ng-template>
    
    <router-outlet></router-outlet>

My app.component.ts file is

    import { Component } from '@angular/core';
    import { AngularFirestore } from 'angularfire2/firestore';
    import { AngularFireAuth } from 'angularfire2/auth';
    import { auth } from 'firebase/app';
    import { Observable } from 'rxjs';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      title = 'app';
    
      constructor(public afAuth: AngularFireAuth) {
      }
      login() {
        var email = "[email protected]";
        var password = "testpass";
        return this.afAuth.auth.signInWithEmailAndPassword(email, password)
          .then((user) => {
            console.log(user);
          })
          .catch((error) => {
            console.log(error)
          });
      }
      logout() {
        this.afAuth.auth.signOut();
      }
    }

app.module.ts file

    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    
    import { AppComponent } from './app.component';
    import { AppRoutingModule } from './app-routing.module';
    import { LoginComponent } from './login/login.component';
    
    import { AlertModule } from 'ngx-bootstrap';
    
    import { AngularFireModule } from 'angularfire2';
    import { AngularFirestoreModule } from 'angularfire2/firestore';
    import { AngularFireStorageModule } from 'angularfire2/storage';
    import { AngularFireAuthModule } from 'angularfire2/auth';
    import { environment } from '../environments/environment';
    
    @NgModule({
      declarations: [
        AppComponent,
        LoginComponent
      ],
      imports: [
        BrowserModule,
        AppRoutingModule,
        AlertModule.forRoot(),
        AngularFireModule.initializeApp(environment.firebase),
        AngularFirestoreModule,
        AngularFireAuthModule,
        AngularFireStorageModule
      ],
      providers: [],
      bootstrap: [ AppComponent ]
    })
    export class AppModule { }

like image 221
David Marcus Avatar asked May 28 '18 13:05

David Marcus


Video Answer


1 Answers

First of all, AngularFire auth isn't that different from what you are used to. The only big difference is that you get notified when the auth state changes (This happens with the observable/promise). you can still get it the way you are used to: this.af.auth.currentUser. However, the value could be outdated already (user has signed out somewhere, or the session has expired)

When you get notified about the auth state, you'll always have some kind of flashy effect. Either it is your entire page or just that message.

I put my entire site (except for the login page) behind a guard. The guard is checked before a route loads and looks something like this:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
  return this.af.authState.pipe(
    map(auth => {
      if (isNullOrUndefined(auth)) {
        this.router.navigate(['/login']); //Redirect if not authenticated
        return false;
      } else {
        return true;
      }
    })
  );
}

In your login component, you can subscribe to your authState and redirect on a successful login.

ngOnInit() {
  this.subsciption = this.af.authState.pipe(defaultIfEmpty()).subscribe(auth => {
    this.logginIn = false;
    if (!isNullOrUndefined(auth)) {
      this.router.navigate(['/overview']); //Redirect succesfull login
    }
  }, console.log, () => {
    this.logginIn = false;
  });
}

logginIn is used to display a spinner.

About that security issue. Yes anyone could possibly see your HTML, but this shouldn't be an issue. It is your data you want (and should) protect. That HTML is just a fancy leather jacket.

like image 164
Robin Dijkhof Avatar answered Sep 27 '22 19:09

Robin Dijkhof