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.
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 { }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With