Search code examples
authenticationtypescriptangularangular2-routing

Angular redirect to login page


I come from the Asp.Net MVC world where users trying to access a page they are not authorized are automatically redirected to the login page.

I am trying to reproduce this behavior on Angular. I came accross the @CanActivate decorator, but it results in the component not rendering at all, no redirection.

My question is the following:

  • Does Angular provide a way to achieve this behaviour?
  • If so, how? Is it a good practice?
  • If not, what would be the best practice for handling user authorization in Angular?

Solution

  • Update: I've published a full skeleton Angular 2 project with OAuth2 integration on Github that shows the directive mentioned below in action.

    One way to do that would be through the use of a directive. Unlike Angular 2 components, which are basically new HTML tags (with associated code) that you insert into your page, an attributive directive is an attribute that you put in a tag that causes some behavior to occur. Docs here.

    The presence of your custom attribute causes things to happen to the component (or HTML element) that you placed the directive in. Consider this directive I use for my current Angular2/OAuth2 application:

    import {Directive, OnDestroy} from 'angular2/core';
    import {AuthService} from '../services/auth.service';
    import {ROUTER_DIRECTIVES, Router, Location} from "angular2/router";
    
    @Directive({
        selector: '[protected]'
    })
    export class ProtectedDirective implements OnDestroy {
        private sub:any = null;
    
        constructor(private authService:AuthService, private router:Router, private location:Location) {
            if (!authService.isAuthenticated()) {
                this.location.replaceState('/'); // clears browser history so they can't navigate with back button
                this.router.navigate(['PublicPage']);
            }
    
            this.sub = this.authService.subscribe((val) => {
                if (!val.authenticated) {
                    this.location.replaceState('/'); // clears browser history so they can't navigate with back button
                    this.router.navigate(['LoggedoutPage']); // tells them they've been logged out (somehow)
                }
            });
        }
    
        ngOnDestroy() {
            if (this.sub != null) {
                this.sub.unsubscribe();
            }
        }
    }
    

    This makes use of an Authentication service I wrote to determine whether or not the user is already logged in and also subscribes to the authentication event so that it can kick a user out if he or she logs out or times out.

    You could do the same thing. You'd create a directive like mine that checks for the presence of a necessary cookie or other state information that indicates that the user is authenticated. If they don't have those flags you are looking for, redirect the user to your main public page (like I do) or your OAuth2 server (or whatever). You would put that directive attribute on any component that needs to be protected. In this case, it might be called protected like in the directive I pasted above.

    <members-only-info [protected]></members-only-info>
    

    Then you would want to navigate/redirect the user to a login view within your app, and handle the authentication there. You'd have to change the current route to the one you wanted to do that. So in that case you'd use dependency injection to get a Router object in your directive's constructor() function and then use the navigate() method to send the user to your login page (as in my example above).

    This assumes that you have a series of routes somewhere controlling a <router-outlet> tag that looks something like this, perhaps:

    @RouteConfig([
        {path: '/loggedout', name: 'LoggedoutPage', component: LoggedoutPageComponent, useAsDefault: true},
        {path: '/public', name: 'PublicPage', component: PublicPageComponent},
        {path: '/protected', name: 'ProtectedPage', component: ProtectedPageComponent}
    ])
    

    If, instead, you needed to redirect the user to an external URL, such as your OAuth2 server, then you would have your directive do something like the following:

    window.location.href="https://myserver.com/oauth2/authorize?redirect_uri=http://myAppServer.com/myAngular2App/callback&response_type=code&client_id=clientId&scope=my_scope