app roles not present in returned claims after authenticating from Azure AD B2C using Angular

2.2k Views Asked by At

i have an angular app with version 2.2 of msal-angular

i have registered 2 apps on azure. the UI app and the api app

in the ui app manifest i created 2 app roles. and form the UI i was able to assign one of the app role to one of the user.

when i clicK login the page is redirected to AD login and i am able to login sucessfully and on return i can idToken where claims have no information about the App Roles and access token is empy.

configuration is like this

      /* eslint-disable max-len */
  /* eslint-disable linebreak-style */
  import { BrowserCacheLocation, Configuration, LogLevel } from '@azure/msal-browser';

  const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;

  export const b2cPolicies = {
    names: {
      signUpSignIn: 'B2C_1_signupsxxx',
      passwordReset: 'B2C_1_passwoxxxset',
    },
    authorities: {
      signUpSignIn: {
        authority: 'https://xxx.b2clogin.com/bbbankAD.onmicrosoft.com/B2C_1_signupsignin',
      },
      passwordReset: {
        authority: 'https://bxxxkAD.b2clogin.com/bbxxxkAD.onmicrosoft.com/B2C_1_passwordreset',
      },
    },
    authorityDomain: 'xxx.b2clogin.com',
  };

  export const msalConfig: Configuration = {
    auth: {
      clientId: '1eaf5743-4xxx-489d-a3ad-11xx0e2a5f',
      authority: b2cPolicies.authorities.signUpSignIn.authority,
      knownAuthorities: [b2cPolicies.authorityDomain],
      redirectUri: 'http://localhost:4200',
      navigateToLoginRequestUrl: true,
      postLogoutRedirectUri: 'http://localhost:4200/login',
    },
    cache: {
      // eslint-disable-next-line max-len
      cacheLocation: BrowserCacheLocation.LocalStorage, // Configures cache location. "sessionStorage" is more secure, but "localStorage" gives you SSO between tabs.
      storeAuthStateInCookie: true,
      // isIE, // Set this to "true" if you are having issues on IE11 or Edge
    },
    system: {
      loggerOptions: {
        // eslint-disable-next-line @typescript-eslint/no-use-before-define
        loggerCallback,
        logLevel: LogLevel.Verbose,
        piiLoggingEnabled: false,
      },
    },
  };

  export function loggerCallback(logLevel: LogLevel, message: string) {
    console.log(message);
  }

  export const silentRequest = {
    scopes: ['openid', 'profile'],
    loginHint: '[email protected]',
  };

  /**
  * Add here the endpoints and scopes when obtaining an access token for protected web APIs. For more information, see:
  * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md
  */
  export const protectedResources = {
    getLast12MonthBalances: {
      endpoint: 'http://localhost:5070/api/Transaction/GetLast12xxxBalances',
      scopes: ['https://xxxx.onmicrosoft.com/bbbxxxpi/bank-xxx'],
    },
    graphApi: {
      endpoint: 'https://graph.microsoft.com/beta/',
      scopes: ['user.read'],
    },
  };

  /**
  * Scopes you add here will be prompted for user consent during sign-in.
  * By default, MSAL.js will add OIDC scopes (openid, profile, email) to any login request.
  * For more information about OIDC scopes, visit:
  * https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
  */
  export const loginRequest = {
    scopes: [] as any[],
  };

And on a login button click i am doing like this

        /* eslint-disable max-len */
    /* eslint-disable no-underscore-dangle */
    import { Component, Inject } from '@angular/core';
    import { Router } from '@angular/router';
    import {
      MsalGuardConfiguration, MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG,
    } from '@azure/msal-angular';
    import {
      AuthenticationResult, EventMessage, EventType, InteractionStatus, RedirectRequest, SilentRequest,
    } from '@azure/msal-browser';
    import { filter, Subject, takeUntil } from 'rxjs';
    import AppUser from '../shared/models/app-user';

    @Component({
      selector: 'app-login',
      templateUrl: './login.component.html',
      styleUrls: ['./login.component.css'],
    })
    export default class LoginComponent {
      // constructor(private authService: AuthService, private router: Router) { }
      title = 'Microsoft identity platform';

      isIframe = false;

      loginDisplay = false;

      private readonly _destroying$ = new Subject<void>();

      displayedColumns: string[] = ['claim', 'value'];

      dataSource: any = [];

      loggedInUser: AppUser;

      constructor(
        @Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration,
        private authService: MsalService,
        private msalBroadcastService: MsalBroadcastService,
        private router: Router,
      ) { }

      ngOnInit(): void {
        this.authService.handleRedirectObservable().subscribe({

          next: (result: AuthenticationResult) => {
            console.log(result);
          },
          error: (error) => console.log(error),
        });

        this.msalBroadcastService.msalSubject$
          .pipe(
            filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS),
            takeUntil(this._destroying$),
          )
          .subscribe((result: EventMessage) => {
            console.log(result);
            const payload = result.payload as AuthenticationResult;
            this.authService.instance.setActiveAccount(payload.account);

          });

        this.msalBroadcastService.inProgress$
          .pipe(
            filter((status: InteractionStatus) => status === InteractionStatus.None),
          )
          .subscribe(() => {
            this.setLoginDisplay();
            this.checkAndSetActiveAccount();
            this.getClaims(this.authService.instance.getActiveAccount()?.idTokenClaims);
          });
      }

      setLoginDisplay() {
        const xx = this.authService.instance.getAllAccounts()[0];
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
        console.log(this.loginDisplay);
      }

      login(userFlowRequest?: RedirectRequest) {
        if (this.msalGuardConfig.authRequest) {
          this.authService.loginRedirect({ ...this.msalGuardConfig.authRequest, ...userFlowRequest } as RedirectRequest);
        } else {
          this.authService.loginRedirect(userFlowRequest);
        }
      }

      checkAndSetActiveAccount() {
        /**
         * If no active account set but there are accounts signed in, sets first account to active account
         * To use active account set here, subscribe to inProgress$ first in your component
         * Note: Basic usage demonstrated. Your app may require more complicated account selection logic
         */
        const activeAccount = this.authService.instance.getActiveAccount();

        if (!activeAccount && this.authService.instance.getAllAccounts().length > 0) {
          const accounts = this.authService.instance.getAllAccounts();
          this.authService.instance.setActiveAccount(accounts[0]);
        }
      }

      getClaims(claims: any) {
        this.dataSource = [
          { id: 1, claim: 'Display Name', value: claims ? claims.name : null },
          { id: 2, claim: 'Object ID', value: claims ? claims.oid : null },
          { id: 3, claim: 'Job Title', value: claims ? claims.jobTitle : null },
          { id: 4, claim: 'City', value: claims ? claims.city : null },
        ];
      }

      ngOnDestroy(): void {
        this._destroying$.next(undefined);
        this._destroying$.complete();
      }
    }

app module is setup like this

      /* eslint-disable max-len */
  import { NgModule } from '@angular/core';
  import { BrowserModule } from '@angular/platform-browser';

  import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
  import { FormsModule } from '@angular/forms';
  import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
  import { IPublicClientApplication, PublicClientApplication, InteractionType } from '@azure/msal-browser';
  import {
    MsalGuard, MsalBroadcastService, MsalModule, MsalService,
    MSAL_GUARD_CONFIG, MSAL_INSTANCE,
    MsalGuardConfiguration, MsalRedirectComponent,
    MsalInterceptorConfiguration, MSAL_INTERCEPTOR_CONFIG, MsalInterceptor,
  } from '@azure/msal-angular';
  import AppRoutingModule from './app-routing.module';
  import AppComponent from './app.component';
  import TransactionService from './shared/services/transaction.service';
  import SharedModule from './shared/shared.module';

  import { loginRequest, msalConfig, protectedResources } from './auth-config';

  /**
   * Here we pass the configuration parameters to create an MSAL instance.
   * For more info, visit: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/docs/v2-docs/configuration.md
   */
  export function MSALInstanceFactory(): IPublicClientApplication {
    return new PublicClientApplication(msalConfig);
  }

  /**
   * Set your default interaction type for MSALGuard here. If you have any
   * additional scopes you want the user to consent upon login, add them here as well.
   */
  export function MSALGuardConfigFactory(): MsalGuardConfiguration {
    return {
      interactionType: InteractionType.Redirect,
      authRequest: loginRequest,
    };
  }

  /**
   * MSAL Angular will automatically retrieve tokens for resources
   * added to protectedResourceMap. For more info, visit:
   * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/docs/v2-docs/initialization.md#get-tokens-for-web-api-calls
   */
  export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
    const protectedResourceMap = new Map<string, Array<string>>();

    protectedResourceMap.set(protectedResources.getLast12MonthBalances.endpoint, protectedResources.getLast12MonthBalances.scopes);

    return {
      interactionType: InteractionType.Redirect,
      protectedResourceMap,
    };
  }

  @NgModule({
    declarations: [
      AppComponent,
    ],
    imports: [
      SharedModule,
      BrowserModule,
      AppRoutingModule,
      FormsModule,
      HttpClientModule,
      BrowserAnimationsModule, // CLI adds AppRoutingModule to the AppModule's imports array
      MsalModule,
    ],
    providers: [
      /*     {
        provide: HTTP_INTERCEPTORS,
        useClass: MsalInterceptor,
        multi: true,
      }, */
      {
        provide: MSAL_INSTANCE,
        useFactory: MSALInstanceFactory,
      },
      {
        provide: MSAL_GUARD_CONFIG,
        useFactory: MSALGuardConfigFactory,
      },
      /*     {
        provide: MSAL_INTERCEPTOR_CONFIG,
        useFactory: MSALInterceptorConfigFactory,
      }, */
      MsalService,
      MsalGuard,
      MsalBroadcastService],
    bootstrap: [AppComponent, MsalRedirectComponent],
  })
  export default class AppModule { }

update Now that I see there is no information of logged in user. No email no givenName lastName etc nothing of this sort but yet idToken is there with regular claims.

Tried getting silent token But no luck.

update2

i didnt checked the required attributes in signinsignup flow i checked them now. i can see username and surname etc but cant see access token.

enter image description here

Update3

i was able to get the access token after passing AppId in claims array. but access token looks like the same as idToken. it does not have information about App Roles.

0

There are 0 best solutions below