Angular Guards | Angular Auth Guard | Angular Canactivate
Angular Guards | Angular Auth Guard | Angular Canactivate

In this post you will learn how to protect your pages inside Angular by using Angular Guards.

Client side protection

Just from the beginning I must tell you the truth. You can't really protect your application in client side. Which actually means it doesn't really matter what we write inside guards, if somebody really wants to break our code and access the page it is totally possible.

You must do all needed backend checks and allow people to access your API only when they have correct credentials.

Which actually means that your application must be first safe on the backend and guards in Angular just help users to avoid accident page access. It is not to make your application bulletproof.

Creating a guard

So let's try to protect our pages now.

Starting project

As you can see here I already prepared a small Angular application. We have 2 routes: Home and Private.

So inside Angular to protect our routes we are using guards.

Guard is a class with a method which will be called when we try to access our page.

If this method returns true then we can access the page. In other case we can't.

Let's create our guard now.

// src/app/auth-guard.service.ts
@Injectable()
export class AuthGuardService implements CanActivate {}

This is just a service which implements CanActivate.

Can activate error

Now we directly get an error that our service didn't implement a method correctly.

@Injectable()
export class AuthGuardService implements CanActivate {
  canActivate(): boolean {
    return true;
  }
}

This method simply returns a boolean and in order to check our guard I returned here true.

@NgModule({
  providers: [AuthGuardService],
})
export class AppModule {}

Now we registered a guard as a service inside our module.

And the last thing is to add a guard to the page that we want to protect.

// src/app/private/private.module.ts
const routes: Routes = [
  {
    path: 'private',
    component: PrivateComponent,
    canActivate: [AuthGuardService],
  },
];

If we check in browser we still can access our private route because our canActivate always returns true. If we change it to false we won't be able to access it.

Adding local storage check

If you have some simple case with data on the client and you don't need to make an API call then you can simply write synchronous canActivate. I want to add check of localStorage property to our method to allow access to the page.

canActivate(): boolean {
  if (localStorage.getItem('token')) {
    return true;
  }
  return false;
}

Here we check for token in our localStorage. If we have such property then we allow access to the page. In other case we don't.

If we try to access the page in browser it won't work. But if we add a localStorage key token with any value then we are allowed to access the page.

Async guards in Angular

But the problem is that this case is rarely used. In the real application we fetch a current user to make sure that we can access the page. Which means that our canActivate method must to asynchronous.

In our application I already prepared an additional CurrentUserService which has a stream of user data.

// app/currentUser.service.ts
@Injectable()
export class CurrentUserService {
  currentUser$ = new BehaviorSubject<
    { id: string; name: string } | null | undefined
  >(undefined);

  setCurrentUser() {
    if (localStorage.getItem('token')) {
      this.currentUser$.next({ id: '1', name: 'Foo' });
    } else {
      this.currentUser$.next(null);
    }
  }
}

We have here a currentUser$ stream of data and a setCurrentUser function which will emulate real API call like in production application. We need to call setCurrentUser on initialize of our application in order to know if we are logged in user or not. Here we just return some mock data if we set a token inside localStorage.

// app/app.component.ts
export class AppComponent implements OnInit {
  constructor(private currentUserService: CurrentUserService) {}

  ngOnInit(): void {
    setTimeout(() => {
      this.currentUserService.setCurrentUser();
    }, 2000);
  }
}

Here we called setCurrentUser on initialize with 2 seconds delay to simulate API call which takes some time.

Now we must update our canActivate function and use currentUser$ stream inside.


export class AuthGuardService implements CanActivate {
  constructor(
    private currentUserService: CurrentUserService,
    private router: Router
  ) {}

  canActivate(): Observable<boolean> {
    return this.currentUserService.currentUser$.pipe(
      filter((currentUser) => currentUser !== undefined),
      map((currentUser) => {
        if (!currentUser) {
          this.router.navigateByUrl('/');
          return false;
        }
        return true;
      })
    );
  }
}

First of all here we returned Observable<boolean> and not just boolean. It means that our canActivate is async now. Inside we must return a stream which returns either true or false.

As our initial value is undefined inside the stream we must filter it our. So we are getting either null or current user object.

Additionally we redirect to the homepage if the user is not logged in.

As you can see in browser we can access the route but only after 2 seconds. It happens because we want for the returns from asynchronous canActivate. If we are jumping directly to /private page we won't see any content until we get currentUser. If we don't get it we are redirected to the homepage.

And actually if you want to learn Angular with NgRx from empty folder to a fully functional production application make sure to check my Angular and NgRx - Building Real Project From Scratch course.

📚 Source code of what we've done