Search code examples
angulartypescriptauthorizationrolesguard

Role guard sometimes allows entry into secured components on localhost


My RoleGuard looks like this:

import { CanLoad, Route } from "@angular/router";
import { AuthenticationService } from "../_services";
import { Injectable } from "@angular/core";

@Injectable({ providedIn: 'root' })
export class RoleGuard implements CanLoad {

    constructor(private authService: AuthenticationService) { }

    canLoad(route: Route) {
        let authorities = route.data.roles;
        if (this.authService.hasAnyRole(authorities)) {
            return true;
        }
        return false;
    }

}

and my methods in authService:

 hasAnyRole(roles: string[]): boolean {
        for (let i = 0; i <= roles.length; i++) {
            if (this.hasRole(roles[i])) {
                return true;
            }
        }
        return false;
    }

    hasRole(role: string): boolean {
        let authorities = this.getAuthority();
        return authorities.findIndex(a => a === role) > -1;
    }

app.routing.ts :

const appRoutes: Routes = [
    {
        path: 'login',
        component: LoginComponent,
        canActivate: [NoAuthGuard]
    },
    {
        path: 'password',
        component: PasswordComponent,
        canActivate: [NoAuthGuard]
    },
    {
        path: 'change-password',
        component: ChangePasswordComponent,
        canActivate: [ChangePasswordGuard]
    },
    {
        path: 'reset-password',
        component: ResetPasswordComponent,
        canActivate: [ResetPasswordGuard],
        resolve: {
            recoverPassword: ResetPasswordGuard
        }
    },
    {
        path: '',
        component: HomeComponent,
        canActivate: [AuthGuard],
        children: [
            {
                path: 'users',
                loadChildren: '../app/users/users.module#UsersModule',
                canLoad: [RoleGuard],
                data: { roles: ['AK.W.1'] }
            },
            {
                path: 'products',
                loadChildren: '../app/products/products.module#ProductsModule',
                canLoad: [RoleGuard],
                data: { roles: ['AK.W.1', 'AK.W.2'] }
            },
            {
                path: 'codes',
                loadChildren: '../app/codes/codes.module#CodesModule',
                canLoad: [RoleGuard],
                data: { roles: ['AK.W.1', 'AK.W.2'] }
            },
            {
                path: 'reports',
                loadChildren: '../app/reports/reports.module#ReportsModule',
                canLoad: [RoleGuard],
                data: { roles: ['AK.W.1','AK.W.2','AK.W.3'] }
            }
        ]
    },
    { path: '**', redirectTo: '' }
];

User authorized roles for components are provided in path's data and checked in AuthorizationService. Methods get user's roles from token and nextable compare them with roles provided in path's data. The problem is guard doesn't work properly. Sometimes it allows unauthorized users to let in secured components on localhost mostly after first login when app is served. Could you indicate me what's wrong with my guard?


Solution

  • The issue could be with CanLoad. CanLoad Gaurd protects a module to be loaded but once module is loaded then CanLoad guard does nothing.

    For example, lets say A user logged in application and he navigates to some Module. After that he click on logout. Now if user wants he will be able to navigate to same module since the it was loaded already.

    So if you want to protect your application, the best would be to use CanActivate.

    Add CanActivate into your RoleGaurd

    import { CanLoad, CanActivate, Route,Router,
     ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
    import { AuthenticationService } from "../_services";
    import { Injectable } from "@angular/core";
    
    @Injectable({ providedIn: 'root' })
    export class RoleGuard implements CanLoad, CanActivate {
    
        constructor(private authService: AuthenticationService,private router: Router) { }
    
        canLoad(route: Route) {
            let authorities = route.data.roles;
            if (this.authService.hasAnyRole(authorities)) {
                return true;
            }
            return false;
        }
    
     canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
            let authorities = route.data.roles;
            if (this.authService.hasAnyRole(authorities)) {
                return true;
            }
            return false;
         }
    
       }