DEV Community

Cover image for Authentication preload strategy
Marko Berger
Marko Berger

Posted on

Authentication preload strategy

I have decided to write my own custom authentication preload strategy. I got an idea from John Papa and his preload strategy series of articles. I truly recommend you read this series of articles on this subject. So what are custom preload strategies?

It’s a way you can tell your Angular application how to bundle your javascript code in a predefined way of your choosing.

For this let’s say we have the authentication service that will reload what modules are going to be preloaded based on the user role. I’m not going to deeper on how or where you will store your strategy. Because it is a philosophical question :-).
I'm going to set up my preload strategies in the environment variable. (This is just one way of doing this).

strategies: new Map([
    ['default', ['module1']],
    ['admin', ['module1', 'module2', 'module3']],
    ['user', ['module1', 'module2']]
  ]
  )

Our router setup.

const routes: Routes = [
  {path: '', redirectTo: '/home', pathMatch: 'full'}, //not important
  {path: 'home', component: HomeComponent}, //not important
  {
   path: 'module1', 
   loadChildren: '../app/module1/module1.module#Module1Module', 
   data: {preload: true}
  },
  {
   path: 'module2', 
   loadChildren: '../app/module2/module2.module#Module2Module', 
   data: {preload: true}
  },
  {
   path: 'module3', 
   loadChildren: '../app/module3/module3.module#Module3Module', 
   data: {preload: true}
  },
];
@NgModule({
  imports: [RouterModule.forRoot(routes, {preloadingStrategy: RolePreloadService})],
  exports: [RouterModule]
})

Let's create what we need in our auth service

export class OnDemandRolePreloadOptions {
  constructor(public routePath: string, public preload = true) {}
}
@Injectable({
  providedIn: 'root'
})
export class AuthServiceService {

  constructor() { }

  getUserRolePreloadOption(){
    const rolePaths: OnDemandRolePreloadOptions[] = this.generatePaths()
    return from(rolePaths);
  }
  generatePaths(){
    return environment.strategies.get('default') //default preload
    .map( mod => new OnDemandRolePreloadOptions(mod, true));
  }
}

This is just to serve this example
In a nutshell. We need to return our OnDemandRolePreloadOptions class of a default strategy as observable.

Now that we got our strategy. Let's do some magic in role-preload.service.ts

export class RolePreloadService implements PreloadingStrategy {
  userRole$: Observable<OnDemandRolePreloadOptions>;

  constructor(private authService: AuthServiceService) { 
    this.userRole$ = this.authService.getUserRolePreloadOption();
  }

  preload(route: Route, load: () => Observable<any>): Observable<any> {

    return this.userRole$.pipe(
      mergeMap(role => {
        const shouldPreload = this.preloadCheck(route, role);
        return shouldPreload ? load() : EMPTY;
      })
    );
  }

  preloadCheck(route: Route, preloadRoleOptions: OnDemandRolePreloadOptions){
    return (
      route.data &&
      route.data['preload'] && 
      [route.path, '*'].includes(preloadRoleOptions.routePath) &&
      preloadRoleOptions.preload
      )
  }

}

So preloadCheck() method is what is doing the heavy lifting here. It checks if the lazy load route is predefined for preload, does it exists in router and preloadRoleOptions. If so it will load our preload strategy based on authentication. If you log in with admin account it will load preload admin javascript bundle.

Enjoy

Latest comments (0)