DEV Community

sforsandeep
sforsandeep

Posted on

Types of property 'providers' are incompatible.

I am trying to dynamically load MSAL configurations from JSON file. here is my Service

import { Injectable } from '@angular/core';
import { HttpClient, HttpBackend } from '@angular/common/http';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class ConfigService {
  private config:any = null;
  private env:any = null;
    /**
   * Use to get the data found in the second file (config file)
   */
  public getConfig(key: any) {
    return this.config[key];
  }

  /**
   * Use to get the data found in the first file (env file)
   */
  public getEnv(key: any) {
    return this.env[key];
  }

  private settings: any;
  private http: HttpClient;
  constructor(private readonly httpHandler: HttpBackend) {
    this.http = new HttpClient(httpHandler);
  }

  init(): Promise<boolean> {
    return new Promise<boolean>((resolve, reject) => {


      var envFile:string="environment/conf.json";
      this.http.get(envFile).pipe(map(res => res))
      .pipe(map(res => res))
    .subscribe(value => {

      this.config=value;

      this.http.get(envFile).pipe(map(res => res))
        .subscribe(value => {
          this.settings = value;
          resolve(true);
        },
        (error) => {
          reject(error);
        });
    });



    });
  }

  getSettings(key?: string | Array<string>): any {
    if (!key || (Array.isArray(key) && !key[0])) {
      return this.settings;
    }

    if (!Array.isArray(key)) {
      key = key.split('.');
    }

    let result = key.reduce((acc: any, current: string) => acc && acc[current], this.settings);

    return result;
  }
}
Enter fullscreen mode Exit fullscreen mode

Here is my module for feeding all the options

import { HTTP_INTERCEPTORS } from "@angular/common/http";
import { APP_INITIALIZER, NgModule } from "@angular/core";
import { InjectionToken } from "@angular/core";
import { MsalBroadcastService, MsalGuard, MsalGuardConfiguration, MsalInterceptor, MsalInterceptorConfiguration, MsalModule, MsalService, MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG } from "@azure/msal-angular";
import { BrowserCacheLocation, Configuration, InteractionType, IPublicClientApplication, LogLevel, PublicClientApplication } from "@azure/msal-browser";
import { ConfigService } from "./shared/services/config.service";




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

export function msalConfig(): Configuration {
    const configuration:Configuration={
    auth: {
        clientId: 'clientid', // This is the ONLY mandatory field that you need to supply.
        authority: 'https://login.microsoftonline.com/hidingtenentid', // Defaults to "https://login.microsoftonline.com/common"
        redirectUri: 'http://localhost:4200', // Points to window.location.origin. You must register this URI on Azure portal/App Registration.
        postLogoutRedirectUri: '/', // Indicates the page to navigate after logout.
        navigateToLoginRequestUrl: true, // If "true", will navigate back to the original request location before processing the auth code response.
    },
    cache: {
        cacheLocation: BrowserCacheLocation.LocalStorage, // Configures cache location. "sessionStorage" is more secure, but "localStorage" gives you SSO between tabs.
        storeAuthStateInCookie: isIE, // Set this to "true" if you are having issues on IE11 or Edge
    },
    system: {
        loggerOptions: {
            loggerCallback(logLevel: LogLevel, message: string) {
                console.log(message);
            },
            logLevel: LogLevel.Verbose,
            piiLoggingEnabled: false
        }
    }
   }
   return configuration;
}

/**
* 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 function protectedResources(config:ConfigService): Map<string, Array<string>>{
    return new Map([
       ['https://graph.microsoft.com/v1.0/me', ['user.read']],
       [
         'api/pepics',
         [config.getSettings("ApiClientId")+ '/user_impersonation'],
       ],
     ]);
}

/**
 * An optional silentRequest object can be used to achieve silent SSO
 * between applications by providing a "login_hint" property.
 */
export const silentRequest = {
    scopes: ["openid", "profile"],
    loginHint: "example@domain.net"
};

/**
* 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://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
*/
export const loginRequest = {
   scopes: []
 };


/**
 * 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());
  }

  /**
   * 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(conf:ConfigService): MsalInterceptorConfiguration {

    return {
      interactionType: InteractionType.Redirect,
      protectedResourceMap: protectedResources(conf)
    };
  }

  /**
   * 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
    };
  }



const AUTH_CONFIG_URL_TOKEN = new InjectionToken<string>('AUTH_CONFIG_URL');

export function initializerFactory(env: ConfigService): any {
    // APP_INITIALIZER, except a function return which will return a promise
    // APP_INITIALIZER, angular doesnt starts application untill it completes
    const promise = env.init().then((value) => {
        console.log(env.getSettings('clientID'));
    });
    return () => promise;
}


@NgModule({
    providers: [
    ],
    imports: [MsalModule]
})
export class MsalApplicationModule {

    static forRoot() {

        return {
            ngModule: MsalApplicationModule,
            providers: [
                ConfigService,
                { provide: AUTH_CONFIG_URL_TOKEN },
                { provide: APP_INITIALIZER, useFactory: initializerFactory, multi: true },                     
                     {
                       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                     

            ]
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

And this is my appmodule

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule,AppRoutingComonent } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'
import { DashboardComponent } from './pages/dashboard/dashboard.component';
import { SidebarMenuComponent } from './components/main-sidebar/sidebar-menu/sidebar-menu.component';
import { SidebarSearchComponent } from './components/main-sidebar/sidebar-search/sidebar-search.component';
import { MainSidebarComponent } from './components/main-sidebar/main-sidebar.component';
import { NavbarComponent } from './components/navbar/navbar.component';
import { SampleButtonComponent } from './samples/sample-button/sample-button.component';
import { GeneralElementsComponent } from './samples/general-elements/general-elements.component';
import { SimpleTablesComponent } from './samples/simple-tables/simple-tables.component';
import { DataTablesComponent } from './samples/data-tables/data-tables.component';
import { WidgetsComponent } from './samples/widgets/widgets.component';
import { SampleFormComponent } from './samples/sample-form/sample-form.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {DataTablesModule} from 'angular-datatables'
import { ToastrModule } from 'ngx-toastr';
import { AdvancedFormComponent } from './samples/advanced-form/advanced-form.component';

import { NgWizardModule, NgWizardConfig, THEME } from 'ng-wizard';
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';

import { MsalApplicationModule } from './msal-application.module';

const ngWizardConfig: NgWizardConfig = {
  theme: THEME.default
};



@NgModule({
  declarations: [
    AppComponent,
    AppRoutingComonent,
    DashboardComponent,
    MainSidebarComponent,
    SidebarMenuComponent,
    SidebarSearchComponent,
    NavbarComponent,
    SampleButtonComponent,
    GeneralElementsComponent,
    SimpleTablesComponent,
    DataTablesComponent,
    WidgetsComponent,
    SampleFormComponent,
    AdvancedFormComponent


  ],
  imports: [
    BrowserModule,
    DataTablesModule,
    HttpClientModule,
    AppRoutingModule,
    MatDialogModule,
    BrowserAnimationsModule, // required animations module
    ToastrModule.forRoot(), // ToastrModule added,
    NgWizardModule.forRoot(ngWizardConfig),
    MsalApplicationModule.forRoot()
  ],
  providers: [
    {
    provide: MatDialogRef,
    useValue: {}
    }
],
  bootstrap: [AppComponent]
})
export class AppModule { }



Enter fullscreen mode Exit fullscreen mode

But I am getting an error only at the import in appmodule

Error: src/app/app.module.ts:62:5 - error TS2322: Type '{ ngModule: typeof MsalApplicationModule; providers: (typeof MsalService | typeof MsalBroadcastService | typeof MsalGuard | typeof ConfigService | ... 5 more ... | { ...; })[]; }' is not assignable to type 'any[] | Type | ModuleWithProviders<{}>'.
Type '{ ngModule: typeof MsalApplicationModule; providers: (typeof MsalService | typeof MsalBroadcastService | typeof MsalGuard | typeof ConfigService | ... 5 more ... | { ...; })[]; }' is not assignable to type 'ModuleWithProviders<{}>'.
Types of property 'providers' are incompatible.
Type '(typeof MsalService | typeof MsalBroadcastService | typeof MsalGuard | typeof ConfigService | { provide: InjectionToken; useFactory?: undefined; multi?: undefined; useClass?: undefined; } | ... 4 more ... | { ...; })[]' is not assignable to type 'Provider[]'.
Type 'typeof MsalService | typeof MsalBroadcastService | typeof MsalGuard | typeof ConfigService | { provide: InjectionToken; useFactory?: undefined; multi?: undefined; useClass?: undefined; } | ... 4 more ... | { ...; }' is not assignable to type 'Provider'.
Type '{ provide: InjectionToken; useFactory?: undefined; multi?: undefined; useClass?: undefined; }' is not assignable to type 'Provider'.
Type '{ provide: InjectionToken; useFactory?: undefined; multi?: undefined; useClass?: undefined; }' is not assignable to type 'FactoryProvider'.
Types of property 'useFactory' are incompatible.
Type 'undefined' is not assignable to type 'Function'.

62 MsalApplicationModule.forRoot()

Please help me. I didnt understand what I did wrong.

Top comments (0)