Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 6 AngularFireAuth check if user is logged in before page rendered

I have set up a new angular 6 project with Firebase auth and Cloud Fire Store. There is a login page where you can login via google and the user data is saved in Firestore (code below). My only issue is how I can check if the user is already logged in, is there any best practice?

At the moment im fetching the user data async, but then the navigation is flickering. For one second there is the login button, then it switches to the logout button. Is there good way to check the login state before it renders the page? enter image description here one second later

User.ts

export interface User {
  uid: string;
  email: string;
}

auth.service.ts

import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import * as firebase from 'firebase/app';
import { AngularFireAuth } from 'angularfire2/auth';
import { AngularFirestore, AngularFirestoreDocument } from 'angularfire2/firestore';
import { Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { User } from './user';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  user: Observable<User>;

  constructor(private afAuth: AngularFireAuth, private afs: AngularFirestore, private router: Router) {
    this.user = this.afAuth.authState.pipe(
        switchMap(user => {
          if (user) {
            return this.afs.doc<User>(`users/${user.uid}`).valueChanges();
          } else {
            return of(null);
          }
        }))
  }

  public googleLogin() {
    const provider = new firebase.auth.GoogleAuthProvider()
    return this.oAuthLogin(provider);
  }

  public signOut() {
    this.afAuth.auth.signOut();
  }

  private oAuthLogin(provider) {
    return this.afAuth.auth.signInWithPopup(provider)
      .then((credential) => {
        this.updateUserData(credential.user)
      })
  }

  private updateUserData(user) {
    const userRef: AngularFirestoreDocument<any> = this.afs.doc(`users/${user.uid}`);
    const data: User = {
      uid: user.uid,
      email: user.email
    }
    return userRef.set(data, { merge: true })
  }

}

navigation.component.ts

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../core/auth.service';
import { AngularFireAuth } from 'angularfire2/auth';

@Component({
  selector: 'app-navigation',
  templateUrl: './navigation.component.html',
  styleUrls: ['./navigation.component.scss']
})
export class NavigationComponent implements OnInit {

  constructor(public auth: AuthService) { }

  ngOnInit() {}

}

auth.service.ts

<div *ngIf="auth.user | async; then loggedIn else loggedOut"></div>
                    
<ng-template #loggedOut>
    <li  class="nav-item d-none d-md-inline-block pl-2 pr-0">
        <a class="btn btn-sm btn-primary u-btn-primary u-btn-pill transition-3d-hover" href="/login">
            Login
        </a>
    </li>
</ng-template>

<ng-template #loggedIn>
    <li  class="nav-item d-none d-md-inline-block pl-2 pr-0">
        <a class="btn btn-sm btn-primary u-btn-primary u-btn-pill transition-3d-hover" (click)="auth.signOut()">
            Logout
        </a>
    </li>
</ng-template>
like image 467
Yves Wehrli Avatar asked May 28 '18 19:05

Yves Wehrli


1 Answers

You probably figured this out a long time ago but someone else might wonder. I chose to do a check against the authState of angularFireAuth. If it's null, then you're logged out. Then You can use route guards as explained in Ryan Chenkie's Medium article

@Injectable()
export class FirebaseAuthService {
private authState: Observable<firebase.User>
private currentUser: firebase.User = null;

constructor(
  public afAuth: AngularFireAuth,
  private http: HttpClient,
  private localStorage: LocalStorageService,
  private router: Router,
  private snackBar: MatSnackBar) {
  this.authState = this.afAuth.authState;

this.authState.subscribe(user => {
  if (user) {
    this.currentUser = user;
    this.localStorage.storeSimple('userData', user)
    this.openSnackBar('Successfully authenticated');
    console.log('AUTHSTATE USER', user)
    this.router.navigate(['home']);
  } else {
    console.log('AUTHSTATE USER EMPTY', user)
    this.currentUser = null;
  }
},
  err => {
    this.openSnackBar(`${err.status} ${err.statusText} (${err.error.message})`, 'Please try again')
  });
 }

 isAuthenticated(): boolean {
   return this.authState !== null;
 }

 loginEmail(email, password, route) {
   this.afAuth.auth.signInWithEmailAndPassword(email, password).catch(error => {
   let errorCode = error.code;
   let errorMessage = error.message;
   this.openSnackBar(error, 'OK')
 });
}

logout() {
  this.afAuth.auth.signOut()
  .then(response => this.openSnackBar('Signed out'))
  .catch(error => this.openSnackBar('Error signing out: ' + error));
}

...
}
like image 88
jsaddwater Avatar answered Sep 28 '22 07:09

jsaddwater