From e046947fbcbe65008227779cd1f2a95d5ae2e061 Mon Sep 17 00:00:00 2001 From: Alexandre Caron Date: Thu, 4 Apr 2024 11:46:15 -0400 Subject: [PATCH] feat(auth): configure auth with provider BREAKING CHANGE: AuthModule is deprecated and use the provideAuthentification for the configuration --- packages/auth/form/src/auth-form.module.ts | 30 +------- .../microsoft/src/auth-microsoft.provider.ts | 73 +++++++++++-------- packages/auth/src/lib/auth.module.ts | 38 ++++++++++ packages/auth/src/lib/auth.provider.ts | 33 +++++++++ .../auth/src/lib/shared/auth.interface.ts | 11 +++ packages/auth/src/public_api.ts | 2 + projects/demo/src/main.ts | 54 +++----------- 7 files changed, 142 insertions(+), 99 deletions(-) create mode 100644 packages/auth/src/lib/auth.module.ts create mode 100644 packages/auth/src/lib/auth.provider.ts diff --git a/packages/auth/form/src/auth-form.module.ts b/packages/auth/form/src/auth-form.module.ts index 63de71d84..f0aed9d59 100644 --- a/packages/auth/form/src/auth-form.module.ts +++ b/packages/auth/form/src/auth-form.module.ts @@ -1,6 +1,5 @@ import { CommonModule } from '@angular/common'; -import { HTTP_INTERCEPTORS } from '@angular/common/http'; -import { ModuleWithProviders, NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; @@ -8,22 +7,18 @@ import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { AuthInterceptor } from '@igo2/auth'; import { AuthFacebookComponent } from '@igo2/auth/facebook'; import { AuthGoogleComponent } from '@igo2/auth/google'; import { AuthInternComponent } from '@igo2/auth/internal'; import { AuthMicrosoftComponent, - AuthMicrosoftb2cComponent, - provideAuthMicrosoft + AuthMicrosoftb2cComponent } from '@igo2/auth/microsoft'; import { IgoLanguageModule } from '@igo2/core/language'; -import { StorageService } from '@igo2/core/storage'; import { MsalModule } from '@azure/msal-angular'; import { AuthFormComponent } from './auth-form/auth-form.component'; -import { AuthStorageService } from './shared/auth-storage.service'; @NgModule({ imports: [ @@ -45,23 +40,4 @@ import { AuthStorageService } from './shared/auth-storage.service'; ], exports: [AuthFormComponent] }) -export class IgoAuthFormModule { - static forRoot(): ModuleWithProviders { - return { - ngModule: IgoAuthFormModule, - providers: [ - { - provide: HTTP_INTERCEPTORS, - useClass: AuthInterceptor, - multi: true - }, - { - provide: StorageService, - useClass: AuthStorageService - }, - ...provideAuthMicrosoft('add'), - ...provideAuthMicrosoft('b2c') - ] - }; - } -} +export class IgoAuthFormModule {} diff --git a/packages/auth/microsoft/src/auth-microsoft.provider.ts b/packages/auth/microsoft/src/auth-microsoft.provider.ts index f5e0b0da8..c56782cf7 100644 --- a/packages/auth/microsoft/src/auth-microsoft.provider.ts +++ b/packages/auth/microsoft/src/auth-microsoft.provider.ts @@ -1,3 +1,4 @@ +import { AuthFeature, AuthFeatureKind } from '@igo2/auth'; import { ConfigService } from '@igo2/core/config'; import { @@ -25,6 +26,9 @@ export function MSALConfigFactory( config: ConfigService ): PublicClientApplication { const msConf = config.getConfig('auth.microsoft') as AuthMicrosoftOptions; + if (!msConf) { + return; + } msConf.redirectUri = msConf?.redirectUri || window.location.href; msConf.authority = @@ -46,6 +50,9 @@ export function MSALConfigFactoryb2c( const msConf = config.getConfig( 'auth.microsoftb2c.browserAuthOptions' ) as BrowserAuthOptions; + if (!msConf) { + return; + } msConf.redirectUri = msConf?.redirectUri || window.location.href; msConf.authority = msConf?.authority || 'https://login.microsoftonline.com/organizations'; @@ -91,36 +98,44 @@ export function MSALAngularConfigFactoryb2c( }; } -export function provideAuthMicrosoft(type?: string) { +export function withMicrosoftSupport( + type?: string +): AuthFeature { if (type === 'b2c') { - return [ - { - provide: MSAL_INSTANCE, - useFactory: MSALConfigFactoryb2c, - deps: [ConfigService] - }, - { - provide: MSAL_GUARD_CONFIG, - useFactory: MSALAngularConfigFactoryb2c, - deps: [ConfigService], - multi: true - }, - MsalServiceb2c - ]; + return { + kind: AuthFeatureKind.Microsoft, + providers: [ + { + provide: MSAL_INSTANCE, + useFactory: MSALConfigFactoryb2c, + deps: [ConfigService] + }, + { + provide: MSAL_GUARD_CONFIG, + useFactory: MSALAngularConfigFactoryb2c, + deps: [ConfigService], + multi: true + }, + MsalServiceb2c + ] + }; } else { - return [ - { - provide: MSAL_INSTANCE, - useFactory: MSALConfigFactory, - deps: [ConfigService] - }, - { - provide: MSAL_GUARD_CONFIG, - useFactory: MSALAngularConfigFactory, - deps: [ConfigService], - multi: true - }, - MsalService - ]; + return { + kind: AuthFeatureKind.Microsoft, + providers: [ + { + provide: MSAL_INSTANCE, + useFactory: MSALConfigFactory, + deps: [ConfigService] + }, + { + provide: MSAL_GUARD_CONFIG, + useFactory: MSALAngularConfigFactory, + deps: [ConfigService], + multi: true + }, + MsalService + ] + }; } } diff --git a/packages/auth/src/lib/auth.module.ts b/packages/auth/src/lib/auth.module.ts new file mode 100644 index 000000000..de16425aa --- /dev/null +++ b/packages/auth/src/lib/auth.module.ts @@ -0,0 +1,38 @@ +import { CommonModule } from '@angular/common'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { ModuleWithProviders, NgModule } from '@angular/core'; + +import { AuthFormComponent, AuthStorageService } from '@igo2/auth/form'; +import { withMicrosoftSupport } from '@igo2/auth/microsoft'; +import { IgoLanguageModule } from '@igo2/core/language'; +import { StorageService } from '@igo2/core/storage'; + +import { AuthInterceptor } from '../public_api'; + +/** + * @deprecated import the AuthFormComponent directly and configure the auth with provideAuthentification + */ +@NgModule({ + imports: [CommonModule, IgoLanguageModule, AuthFormComponent], + exports: [AuthFormComponent] +}) +export class IgoAuthFormModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: IgoAuthFormModule, + providers: [ + { + provide: HTTP_INTERCEPTORS, + useClass: AuthInterceptor, + multi: true + }, + { + provide: StorageService, + useClass: AuthStorageService + }, + ...withMicrosoftSupport('add').providers, + ...withMicrosoftSupport('b2c').providers + ] + }; + } +} diff --git a/packages/auth/src/lib/auth.provider.ts b/packages/auth/src/lib/auth.provider.ts new file mode 100644 index 000000000..14c62faa1 --- /dev/null +++ b/packages/auth/src/lib/auth.provider.ts @@ -0,0 +1,33 @@ +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { + EnvironmentProviders, + Provider, + makeEnvironmentProviders +} from '@angular/core'; + +import { AuthStorageService } from '@igo2/auth/form'; +import { StorageService } from '@igo2/core/storage'; + +import { AuthFeature, AuthFeatureKind, AuthInterceptor } from './shared'; + +export function provideAuthentification( + ...features: AuthFeature[] +): EnvironmentProviders { + const providers: Provider[] = [ + { + provide: HTTP_INTERCEPTORS, + useClass: AuthInterceptor, + multi: true + }, + { + provide: StorageService, + useClass: AuthStorageService + } + ]; + + for (const feature of features) { + providers.push(...feature.providers); + } + + return makeEnvironmentProviders(providers); +} diff --git a/packages/auth/src/lib/shared/auth.interface.ts b/packages/auth/src/lib/shared/auth.interface.ts index edcf85391..e6c1ebc1a 100644 --- a/packages/auth/src/lib/shared/auth.interface.ts +++ b/packages/auth/src/lib/shared/auth.interface.ts @@ -1,3 +1,5 @@ +import { Provider } from '@angular/core'; + import { BaseUser } from '@igo2/core/user'; export interface User extends BaseUser { @@ -40,3 +42,12 @@ export interface WithCredentialsOptions { export interface AuthInternOptions { enabled?: boolean; } + +export interface AuthFeature { + kind: KindT; + providers: Provider[]; +} + +export declare enum AuthFeatureKind { + Microsoft = 0 +} diff --git a/packages/auth/src/public_api.ts b/packages/auth/src/public_api.ts index fd8ac9670..6953c4bf5 100644 --- a/packages/auth/src/public_api.ts +++ b/packages/auth/src/public_api.ts @@ -1,6 +1,8 @@ /* * Public API Surface of auth */ +export * from './lib/auth.module'; +export * from './lib/auth.provider'; export * from './lib/shared/auth.service'; export * from './lib/shared/logged.guard'; export * from './lib/shared/auth.guard'; diff --git a/projects/demo/src/main.ts b/projects/demo/src/main.ts index f9109bdf1..50609b1b1 100644 --- a/projects/demo/src/main.ts +++ b/projects/demo/src/main.ts @@ -1,10 +1,5 @@ import { provideHttpClient, withJsonpSupport } from '@angular/common/http'; -import { - APP_INITIALIZER, - ApplicationRef, - enableProdMode, - importProvidersFrom -} from '@angular/core'; +import { enableProdMode, importProvidersFrom } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; @@ -27,9 +22,10 @@ import { withPreloading } from '@angular/router'; +import { provideAuthentification } from '@igo2/auth'; import { IgoCoreModule } from '@igo2/core'; import { provideConfigOptions } from '@igo2/core/config'; -import { LanguageService } from '@igo2/core/language'; +import { provideRootTranslation } from '@igo2/core/language'; import { IgoDirectionsModule, IgoGeoWorkspaceModule, @@ -38,8 +34,6 @@ import { provideWorkspaceSearchSource } from '@igo2/geo'; -import { concatMap, first } from 'rxjs'; - import { AppComponent } from './app/app.component'; import { routes } from './app/app.routing'; import { environment } from './environments/environment'; @@ -70,46 +64,20 @@ bootstrapApplication(AppComponent, { IgoDirectionsModule ), provideHttpClient(withJsonpSupport()), + provideRouter(routes, withPreloading(PreloadAllModules)), + provideAnimations(), provideConfigOptions({ default: environment.igo }), - provideRouter(routes, withPreloading(PreloadAllModules)), + provideRootTranslation(), + provideAuthentification(), + provideOsrmDirectionsSource(), + provideIChercheSearchSource(), + provideWorkspaceSearchSource(), { provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: { appearance: 'outline' } }, - { - provide: APP_INITIALIZER, - useFactory: appInitializerFactory, - deps: [ApplicationRef, LanguageService], - multi: true - }, - { provide: MAT_TOOLTIP_DEFAULT_OPTIONS, useValue: defaultTooltipOptions }, - provideAnimations(), - provideOsrmDirectionsSource(), - provideIChercheSearchSource(), - provideWorkspaceSearchSource() + { provide: MAT_TOOLTIP_DEFAULT_OPTIONS, useValue: defaultTooltipOptions } ] }).catch((err) => console.log(err)); - -export function appInitializerFactory( - applicationRef: ApplicationRef, - languageService: LanguageService -) { - return () => - new Promise((resolve: any) => { - applicationRef.isStable - .pipe( - first((isStable) => isStable === true), - concatMap(() => { - const lang = languageService.getLanguage(); - return languageService.translate.getTranslation(lang); - }) - ) - .subscribe((translations) => { - const lang = languageService.getLanguage(); - languageService.translate.setTranslation(lang, translations); - resolve(); - }); - }); -}