Search Lessons, Code Snippets, and Videos
search by algolia
X
#native_cta# #native_desc# Sponsored by #native_company#

Router Guards to Redirect Unauthorized Firebase Users

Episode 4 written by Jeff Delaney
full courses and content on fireship.io

Health Check: This lesson was last reviewed on and tested with these packages:

  • Angular v4
  • AngularFire2 v4

Update Notes: Breaking changes have been introduced since this video's original release. Checkout the following updated lesson Episode 55 Firebase OAuth with Firestore.

Find an issue? Let's fix it

Guards are used in Angular to prevent unauthorized users from navigating anywhere they please in your app. The vast majority of apps need routing protection, which is why it’s included in the Firestarter demo app.

The special thing about Guards is that they implement the CanActivate interface. The most basic type of router guard is one that checks is a user is logged in or not. Our authorization process is built on observables, so we need to subscribe to the auth state, then return true or false.

Step 1: Create the Guard

The CLI in Angular 4 has a build in generator for guards.

ng g guard core/auth

It needs to be bootstrapped as a provider just like any other injectable service.

app.module.ts

import { AuthService } from './core/auth.service';
import { AuthGuard} from './core/auth.guard';
// ... omitted
providers: [
AuthService,
AuthGuard,
],

Check for Authentication from the Service

Here’s the service code that makes the guard work. First, we are subscribing the FirebaseAuthState, then using a getter to see if it returned null. Most of this code should be worked out in the service, so the guard code stays nice and dry.

This service was originally created in the Angular OAuth tutorial, so check that out if you’re lost.

core/auth.service.ts

// Returns true if user is logged in
get authenticated(): boolean {
return this.authState !== null;
}

core/auth.guard.ts

In the guard, we can use this getter to check see if the user is logged in or not. If not, we return false.

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { AuthService } from './auth.service';

@Injectable()
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) {}


canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | boolean {
if (this.auth.authenticated) { return true; }

console.log('access denied!')
this.router.navigate(['/login']);
return false


}
}

Using the Guard in the Router

Now that the guard is complete, you need to apply it to specific routes. You’re router should look something like this.

app-routing.module.ts

import { AuthGuard } from './core/auth.guard';
const routes: Routes = [
{ path: 'items', component: SomeComponent, canActivate: [AuthGuard]},
]

Dealing with Browser Refresh

Currently, our the router will redirect back to the root page on a full browser refresh - even if the user is authenticated. We can take a different approach to the router guard that takes the first emitted value from the Observable and maps it to a boolean. Then we use the RxJS do function to catch unauthenticated users and redirect them.

auth.service.ts

In the service, we just get the Firebase auth observable.

get currentUserObservable(): any {
return this.af.auth
}

auth.guard.ts

The new auth guard will wait for a value to be emitted before redirecting.

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { AuthService } from './auth.service';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';

@Injectable()
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) {}


canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | boolean {
if (this.auth.authenticated) { return true; }

return this.auth.currentUserObservable
.take(1)
.map(user => !!user)
.do(loggedIn => {
if (!loggedIn) {
console.log("access denied")
this.router.navigate(['/login']);
}
})

}
}