Angular2-jwt: whitelistedDomains as a promise

Created on 19 Dec 2018  ·  13Comments  ·  Source: auth0/angular2-jwt

Hi, is there any way to pass whitelistedDomains as a promise? The example bellow jwtOptionsFactory receives AppConfigService which returns the domain value. Just for the record, the service is provided as APP_INITIALIZER.

export function jwtOptionsFactory(appConfig: AppConfigService) {
  return {
    tokenGetter: TokenHelper.getToken,
    headerName: 'token',
    authScheme: '',
    whitelistedDomains: [appConfig.domain],
    blacklistedRoutes: []
  };
}
stale good-first-issue help wanted proposal

Most helpful comment

This feature is exactly what my app needs. My app is hosted on a node server that contains a relative endpoint, /configuration, which returns the location of our API, which lives on a different domain. This allows us to use a node environment variable on the node server to configure what API our app should make all its API calls to. As such, we don't know what domain to whitelist until our APP_INITIALIZER makes the call to /configuration. For this reason, we need this feature...we need whitelistedDomains to support Promises and/or Observables.

All 13 comments

I'd like to know whether this is possible as well.

This would be extremely helpful for use cases where the URL for the application's backend is dynamically configurable and needs to be retrieved from the application's assets, for example.

I found a workaround for dynamically setting the backend host.

It's a bit ugly and hacky, the application might take slightly longer to load and the config can only be changed by reloading the application.
However, it works, so I'll post it anyway in case anyone runs into the same problem as I did.

EDIT:
See my next comment for instructions on how to make this work when building the application in prod mode (with AOT enabled).


I created a simple JS file inside assets which contains a function providing the endpoint config:

function getEndpointConfig() {
  return {
    "host": "localhost:3000",
    "https": false
  }
}

This JS file has to be included in src/index.html, inside the <head> tag:

<head>
  ...
  <script type="text/javascript" src="assets/endpoint.js"></script>
</head>

Now you can fetch the backend host in your app.module.ts using your function defined in the script asset:

JwtModule.forRoot({
  config: {
    tokenGetter: tokenGetter,
    whitelistedDomains: [window['getEndpointConfig']()['host']]
  }
})

My workaround doesn't seem to work correctly when aot-compiling the application using ng build --prod.

So here's an even more hacky version of it:


Angular "precompiles" the output of all functions called inside module decorators - in this case, however, we need to actually get a value at runtime.

Angular also doesn't allow calling functions inside module decorators when building with AOT enabled.

However, it is possible to supply some sort of hardcoded value (for prod mode only) and replace it after the build has been completed:

JwtModule.forRoot({
  config: {
    tokenGetter: tokenGetter,
    whitelistedDomains: [(environment.production ? '####BACKEND_HOST####' : getBackendHost())]
  }
})

The function getBackendHost() should be defined in the same module file:

export function getBackendHost(): string {
  return window['getEndpointConfig']()['host'];
}

Now you need to include a step in your buildscript that replaces "####BACKEND_HOST####" (note the double quotes in the js output) with window['getEndpointConfig']()['host'] in your main.*.js file after ng build --prod has completed.

My gulp task for replacing the backend host:

task('replaceEndpointHost', function(){
  return src(['dist/my-app/main.*.js'])
    .pipe(replace("\"####BACKEND_HOST####\"", "window['___getApplicationConfig___']()['endpoint']['host']"))
    .pipe(dest('dist/my-app/'));
});

I am also interested in this feature

We'd welcome a PR to enable this feature, if it's something of interest.

Also interested in this feature

This feature is exactly what my app needs. My app is hosted on a node server that contains a relative endpoint, /configuration, which returns the location of our API, which lives on a different domain. This allows us to use a node environment variable on the node server to configure what API our app should make all its API calls to. As such, we don't know what domain to whitelist until our APP_INITIALIZER makes the call to /configuration. For this reason, we need this feature...we need whitelistedDomains to support Promises and/or Observables.

@shad1w what about using enviroment variable ?

+1, We need this feature as well.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you have not received a response for our team (apologies for the delay) and this is still a blocker, please reply with additional information or just a ping. Thank you for your contribution! 🙇‍♂️

ping

Any news on this feature?

I've found another workaround for this.

You can edit the JwtConfig provided by your factory in an APP_INITIALIZER before the JwtModule starts using it.

function jwtConfigFactory(jwtService: JwtService): JwtConfig {
  return {
    tokenGetter: () => jwtService.getToken(),
    allowedDomains: [],
    disallowedRoutes: [],
  }
}

function jwtInitialiserFactory(configService: ConfigService, jwtConfig: JwtConfig): () => Promise<void> {
  return async () => {
    jwtConfig.allowedDomains.push(...(await configService.getPrivateDomains()));
    jwtConfig.disallowedRoutes.push(...(await configService.getPublicUrls()));
  };
}

const jwtOptionsProvider: Provider = {
  provide: JWT_OPTIONS,
  useFactory: jwtConfigFactory,
  deps: [JwtService]
}

const jwtInitialiserProvider: Provider = {
  provide: APP_INITIALIZER,
  useFactory: jwtInitialiserFactory,
  deps: [ConfigService, JWT_OPTIONS],
  multi: true,
}

@NgModule({
  declarations: [
    AppComponent,
    // ...
  ],
  imports: [
    // ...
    JwtModule.forRoot({ jwtOptionsProvider }),
  ],
  providers: [
    // ...
    jwtInitialiserProvider,
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
Was this page helpful?
0 / 5 - 0 ratings

Related issues

getglad picture getglad  ·  5Comments

tekkudoc picture tekkudoc  ·  5Comments

ciesielskico picture ciesielskico  ·  5Comments

nickraphael picture nickraphael  ·  3Comments

wannabegeek picture wannabegeek  ·  3Comments