0

I am on Angular 13, integrated with Azure AD using MSAL. My issue is as soon as the user successfully logs in , the logincomponent is not displayed. But when I do a page refresh then the login component displays. How do I get login component to display as soon as the user signs in.

On debugging realized, on signing in when AuthGaurd kicks in , this this.msalsvc.instance.getActiveAccount() returns a null even though sign in was successful

This is my app routing,

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './auth-guard.guard';
import { LoginComponent } from './components/login/login/login.component';
import { TestComponent } from './components/test/test.component';
import { RoleGuard } from './route-guard.guard';


const routes: Routes =[
  {path: '', component: TestComponent, canActivate: [AuthGuard]}
]

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

This is my app.component.html

<app-header></app-header>

  <div *ngIf="IsLoggedIn()">
    <h3>Welcome {{userName}}</h3>
    <h3>Your Assigned role: {{userRole}}</h3>
  </div>
  <div *ngIf="!IsLoggedIn()">
    <h3>You are NOT logged in</h3>
  </div>

<router-outlet></router-outlet>
<app-footer></app-footer>

This is the code for Auth Guard

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, RouterStateSnapshot, UrlTree } from '@angular/router';
import { MsalService } from '@azure/msal-angular';
import { map, Observable, Subscription } from 'rxjs';
import { CommonService } from './services/common-service.service';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate{
 
  constructor(private msalsvc : MsalService){
  }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): boolean {
      console.log('Hi there, ');
 
    if(this.msalsvc.instance.getActiveAccount() == null){
      return false;
    }
    return true;
  }

  
}

This is the common service where the method for login is added

import { Injectable } from '@angular/core';
import { MsalService } from '@azure/msal-angular';
import { AuthenticationResult } from '@azure/msal-browser';
import { observable, Observable, Subject,of} from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class CommonService {

  //private loggedIn : boolean = false;
   private subject = new Subject<any>();
  constructor(private authService: MsalService) {
    console.info('Common service is created');
   }
   
   login() {
    this.authService.loginPopup()
      .subscribe((response: AuthenticationResult) => {
        this.authService.instance.setActiveAccount(response.account);
        this.updateLoginNotification(true);
      });
  }

  isLoggedIn(): boolean {
    return this.authService.instance.getActiveAccount() != null
  }

  isAsyncLoggedIn():Observable<boolean>{
    return of(this.authService.instance.getActiveAccount() !=null);
  }
  
  onLoginCheck(): Observable<boolean>{
     return this.subject.asObservable();
   }
  
   getName():any{
    this.getRole();
    return this.authService.instance.getActiveAccount()?.name;
  }
   getRole():any{
     return (this.authService.instance.getActiveAccount()?.idTokenClaims as any).roles;
   }
   updateLoginNotification(item: any) { 
    this.subject.next(item);
}

}
user1005310
  • 737
  • 2
  • 12
  • 40
  • Please show how the user is logged in. The auth guard only fires once before page load, so if the user isn't signed in before page load, the component will not display. – Chris Hamilton Feb 01 '22 at 01:30
  • I just edited the post and added the common service, where contains the login method – user1005310 Feb 01 '22 at 14:07
  • Sorry, but that doesn't show me **when** the user is logged in. Where are you calling the service? Are you logging the user in before the page loads? – Chris Hamilton Feb 03 '22 at 07:17

1 Answers1

0

After calling login(), and ensuring it completes successfully, navigate to the correct path using the Router service. This will cause the auth guard to fire after logging in.

In order to ensure login completed, you should return the observable created in the login() function.

In common.service.ts:

  login(): Observable<AuthenticationResult> {
    const observable = this.authService.loginPopup();
    observable.subscribe((response: AuthenticationResult) => {
      this.authService.instance.setActiveAccount(response.account);
      this.updateLoginNotification(true);
    });
    return observable;
  }

Now, after calling login() you can route to the correct path after logging in.

In app.component.ts:

  constructor(private router: Router, private loginService: LoginService) {}

  login() {
    const obs = this.loginService.login();
    obs.subscribe((result) => {
      //Check that the login was successful here
      const success = true;
      if (success) this.router.navigate(['yourPathHere']);
    });
  }

Now you know the user is logged in before attempting to load the page. The auth guard is just there as a preventative measure, such as when the user manually navigates using a url.

Chris Hamilton
  • 9,252
  • 1
  • 9
  • 26