Home > Enterprise >  Best place to trigger angular authentication
Best place to trigger angular authentication

Time:11-17

I wasn't able to find an existing question that addresses my concrete question. I am working on an angular application that uses jwt and http-only cookies for authentication. As usual, to authenticate the requests on the backend, the jwt cookie is sent with every request. Everything is working, but I wonder what is the best way to verify if the user is authenticated - especially the WHERE.

From various tutorials I know that you usually use AuthGuards and a custom AuthService to verify Authentication and protect routes. In my case I can verify authentication by providing an endpoint /api/users/currentuser which only purpose is to check the jwt token and to answer with a user object if the user is authenticated. This also works fine, but where is the best place in an angular application to fire that request?

  • is it in the constructorof my AuthService, to ensure it is the first thing that is called and then set a BehaviorSubject<boolean> where all other components can subscribe?

     export class AuthService {
     isLoggedIn$ = new BehaviorSubject < boolean > (this.isLoggedIn);
     ...
     constructor() {
       // make call to /api/users/currentuser backend 
       // set isLoggedin$ to true/false according to result of currentuser response
       this.isLoggedIn$.next(value);
     }
    
  • should the AuthGuard that protects one or more routes fire its own request to the backend inside of canActivate?

     canActivate(
         route: ActivatedRouteSnapshot,
         state: RouterStateSnapshot
       ):
         | Observable<boolean | UrlTree>
         | Promise<boolean | UrlTree>
         | boolean
         | UrlTree {
         if (!this.authService.getCurrentUser()) {
           console.log('NOT autheticated');
           this.router.navigate(['']);
           return false;
         }
    
         console.log('AUTHENTICATED');
         return true;
       }
    
  • or should the AuthGuard subscribe to the AuthService BehaviorSubject like so:

     canActivate(
         route: ActivatedRouteSnapshot,
         state: RouterStateSnapshot
       ):
         | Observable<boolean | UrlTree>
         | Promise<boolean | UrlTree>
         | boolean
         | UrlTree {
         if (!this.authService.isLoggedIn$.getValue()) {
           console.log('auth guard NOT autheticated');
           this.router.navigate(['']);
           return false;
         }
    
         console.log('AUTHENTICATED');
         return true;
       }
    
  • and should the call to /api/users/currentuser be made with async ... await ... at the very beginning of the page load to ensure authentication is verfied before the page is rendered in the meantime?

  • especially after a page refresh - where should the authentication magic be happen ideally?

Is there ONE perfect place/point in the application where one call to ?/api/users/currentuser should happen? And all components that need to know that authentication information can share the result or should happen the call multiple times for (security?) reasons from various places (authservice, authguard, different components, etc)?

Current app.component.html:

<app-navbar></app-navbar>
<router-outlet></router-outlet>

CodePudding user response:

You (and presumably most people) ideally want authentication to happen before anything else in the app. That means the best place to do is as part of some "preload" service.

Create a service like:

@Injectable({
    providedIn: 'root',
})
export class PreloadService
    constructor(
        private authService: AuthService
        // add whatever other imports needed
    ) {}

    public async initializeApp() {
        if (!this.authService.isLoggedIn()) {
            await this.authService.login() // or whatever you need to do
        }     
    }
}

export function PreloadFactory(preloadService: PreloadService) {
    return () => preloadService.initializeApp();
}

Then in app.module:

    providers: [
        {
            provide: APP_INITIALIZER,
            useFactory: PreloadFactory,
            deps: [PreloadService],
            multi: true,
        },
    ]

Now your preload service will run and can ensure the user is authenticated when the App initializes and before any routes load.

  • Related