Issue
I am in the process of updating from okta/okta-angular 3.x to 5.x. It seems to have introduced an odd bug.
When the app first starts up, we have been using APP_INITIALIZER to execute appInitializerFactory(configService: ConfigService), which makes an http call to load configuration data.
The call looks like this:
public async loadConfig(): Promise<any> {
return this.httpClient.get('assets/config.json').pipe(settings => settings)
.toPromise()
.then(settings => {
this.config = settings as IAppConfig;
})
.catch(exception => {
console.log("Exception encountered while retreiving configuration");
});
}
Before updating to okta 5.x, the APP_INITIALIZER has been awaiting the promise. Now, it appears that other providers are being resolved before the promise in APP_INITILIZER has finished.
The resulting issue happens downstream at the oktaInitializerFactory, where it runs the following code:
public oktaConfig() {
return Object.assign({
onAuthRequired: (oktaAuth: OktaAuth, injector: Injector) => {
const router = injector.get(Router);
router.navigate(['/login']);
}
}, this.config.oktaConfig);
}
On the last line, this.config.oktaConfig is returning as undefined because the APP_INITIALIZER has not finished awaiting.
Heres the complete app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { Routes, RouterModule, Router } from '@angular/router';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { MaterialModule } from './modules/material/material.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TablesComponent } from './components/tables/tables.component';
import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component';
import { DashboardComponent } from './components/dashboard/dashboard.component';
import { ToolbarComponent } from './components/toolbar/toolbar.component';
import { LoginComponent } from './components/login/login.component';
import { FormsModule } from '@angular/forms';
import { RunLogComponent } from './components/run-log/run-log.component';
import { RunDataComponent } from './components/run-data/run-data.component';
import { ActionComponent } from './components/action/action.component';
import { ErrorsComponent } from './components/errors/errors.component';
import { OktaAuth } from '@okta/okta-auth-js';
import { OKTA_CONFIG, OktaAuthGuard, OktaAuthModule, OktaCallbackComponent } from '@okta/okta-angular';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { TabulatorUserTableComponent } from './components/tabulator-user-table/tabulator-user-table.component';
import { TabulatorTableComponent } from './components/tabulator-table/tabulator-table.component';
import { DataLakeComponent } from './components/data-lake/data-lake.component';
import { IntegrationHistoryComponent } from './components/integration-history/integration-history.component';
import { IntegrationStatusComponent } from './components/integration-status/integration-status.component';
import { MatSpinnerButtonComponent } from './components/mat-spinner-button/mat-spinner-button.component';
import { ConfigService } from './services/config.service';
import { DataStudioComponent } from './components/data-studio/data-studio.component';
import { IntegrationDashboardComponent } from './components/integration-dashboard/integration-dashboard.component';
import { IntegrationSelectorToolbarComponent } from './integration-selector-toolbar/integration-selector-toolbar.component';
import { PrimaryButtonsComponent } from './components/primary-buttons/primary-buttons.component';
import { UserCardComponent } from './components/user-card/user-card.component';
import { HttpOktaInterceptorService } from './services/http-okta-interceptor.service';
import { DebugInfoComponent } from './components/debug-info/debug-info.component';
import { ModalModule } from './modal';
import { DragNDrop } from './components/dropbox/drag-n-drop';
import { ProgressComponent } from './components/dropbox/progress/progress.component';
const appRoutes: Routes = [
{
path: '',
component: DashboardComponent,
canActivate: [OktaAuthGuard]
},
{
path: 'login/callback',
component: OktaCallbackComponent
},
{
path: 'login',
component: LoginComponent
}
];
@NgModule({
declarations: [
AppComponent,
TablesComponent,
PageNotFoundComponent,
DashboardComponent,
ToolbarComponent,
LoginComponent,
RunLogComponent,
RunDataComponent,
ActionComponent,
ErrorsComponent,
TabulatorUserTableComponent,
TabulatorTableComponent,
DataLakeComponent,
IntegrationHistoryComponent,
IntegrationStatusComponent,
MatSpinnerButtonComponent,
DataStudioComponent,
IntegrationDashboardComponent,
IntegrationSelectorToolbarComponent,
PrimaryButtonsComponent,
UserCardComponent,
DebugInfoComponent,
ProgressComponent,
DragNDrop
],
imports: [
BrowserModule,
OktaAuthModule,
RouterModule.forRoot(appRoutes, { relativeLinkResolution: 'legacy' }),
BrowserAnimationsModule,
MaterialModule,
FormsModule,
HttpClientModule,
ModalModule
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: appInitializerFactory,
deps:[ConfigService],
multi: true
},
{
provide: OKTA_CONFIG,
useFactory: oktaInitializerFactory,
deps:[ConfigService],
},
{
provide: HTTP_INTERCEPTORS,
useClass: HttpOktaInterceptorService,
multi: true,
deps: [OktaAuth]
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
export function appInitializerFactory(configService: ConfigService) {
return () => configService.loadConfig();
}
export function oktaInitializerFactory(configService: ConfigService) {
return configService.oktaConfig();
}
Is there a particular reason why my APP_INITIALIZER isn't finishing before other code executes? When I downgrade back to okta 3.x, this issue goes away.
Solution
As it turns out, upgrading from Okta 3 to 4+ or 5+ does introduce a change that makes loading auth server configuration in APP_INITIALIZER a non-viable option.
In a nutshell, okta-angular will attempt to connect to the auth server before the APP_INITIALIZER finishes. The workaround for this is to load the configuration data into the injector in main.ts, which fires off before the APP_INITIALIZER. This technically goes against the documentation in angular.io, but Okta support has verified this behavior [Source]
Another user does a similar implementation for Auth0, which encountered the same problem, linked here: stackoverflow.com/a/66957293/3202440
Answered By - Chris Phillips
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.