Closed mendhak closed 4 years ago
Hi @mendhak ,
I managed to reproduce it to some degree, however I am seeing an error being logged. Do you as well? I want to ensure we are talking about the same issue and that the fix I provided will also solve your use-case.
This is the error I am seeing:
core.js:4352 ERROR TypeError: Cannot read property 'httpInterceptor' of undefined
at AuthHttpInterceptor.intercept (auth.interceptor.ts:34)
at HttpInterceptorHandler.handle (http.js:1258)
at HttpXsrfInterceptor.intercept (http.js:1886)
at HttpInterceptorHandler.handle (http.js:1258)
at HttpInterceptingHandler.handle (http.js:1936)
at MergeMapSubscriber.project (http.js:1082)
at MergeMapSubscriber._tryNext (mergeMap.js:46)
at MergeMapSubscriber._next (mergeMap.js:36)
at MergeMapSubscriber.next (Subscriber.js:49)
at Observable._subscribe (subscribeToArray.js:3)
Thanks,
Hi @frederikprijck thanks for looking.
I get a different error, mine appears when I do an http.get
, then in the browser console I see an error appear.
ERROR TypeError: this.config is undefined Angular 6 intercept handle intercept handle handle request/events$< RxJS 12 getWeatherForecast protected.component.ts:25 ProtectedComponent protected.component.ts:17 ProtectedComponent_Factory main.js:211 Angular 11 RxJS 95 core.js:4352
I don't know enough to say what the problem could be.
No error appears elsewhere, are you handling a catch somewhere else? Is there something I could try myself and see if I can match your error?
It sounds related to the error I am seeing, maybe a different error message because of an older Angular? Would u be able to verify if the patch in the PR works in your case?
No error appears elsewhere, are you handling a catch somewhere else? I could try myself and see if I can match your error.
No catch, this line is failing because this,config
is undefined (which is also what your error mentions)
OK I will try your branch. Correct me if I'm wrong (since I am new to this): I need to clone your branch, then ng build it, then reference dist
directory in my own package.json and try my http.get again.
It might be a bit more complicated than that to be honest. We should have a release out soon, which should solve it I think. Might be worth trying once we have a release.
One easier way could be: you should be able to create your own interceptor and copy our interceptor"s code and not use ours directly. Be sure to use the patched version: https://github.com/auth0/auth0-angular/blob/fix/interceptor/projects/auth0-angular/src/lib/auth.interceptor.ts
Hey I tried it slightly differently before I saw your comment. After the npx ng build
against your branch, I copied what was in the dist/ into my local project's node_modules/@auth0/ and replaced the auth0-angular directory. That seems simple enough.
I restarted my local Angular application, and your changes worked! 😁
I can see an Authorization Bearer token in the requests.
Great, thanks for verifying!
Hi @mendhak ,
This should be fixed in the latest release (1.2.0)!
Tested 1.2.0 and working, thanks a lot for your efforts with the past few issues @frederikprijck and @stevehobbsdev
I'm really glad we're getting to use this library in our new project; it's easy to use, the instructions are easy to follow, with very good examples. This has made securing our application really easy with minimal effort, with so little cognitive load on our developers, it's an absolute joy to set up for them 😄
My favorite part is the API syntax, minimal and clean.
{
uri: '/api/accounts/*',
tokenOptions: {
audience: 'http://my-api/',
scope: 'read:accounts',
},
},
Thanks for that feedback @mendhak , we are happy you are liking the library and it is enabling you to implement security with minimal effort! 🎉
Hey guys,
I've tried to setup dynamic loading of configuration + HTTP_INTERCEPTORS.
Unlike @mendhak , I use httpClient to load my configuration
This is the function that is called in the APP_INITIALIZER
method
public loadEnv(): Observable<any> {
const http = this.injector.get(HttpClient);
const config = this.injector.get(AuthClientConfig);
return http.get('/assets/config/environment.json').pipe(
tap((data: Environnement) => {
environment.settings = Object.assign({}, data.settings);
config.set({
domain: environment.settings.auth0.domain,
clientId: environment.settings.auth0.clientId,
httpInterceptor: {
allowedList: [
{
uri: environment.settings.api + '/*',
tokenOptions: {
scope: 'my-scope',
audience: environment.settings.auth0.audience,
},
},
],
},
});
}),
catchError((err: HttpErrorResponse) => {
console.warn('No environment.json file found, use default environment.');
console.error(err);
config.set({
domain: environment.settings.auth0.domain,
clientId: environment.settings.auth0.clientId,
httpInterceptor: {
allowedList: [
{
uri: environment.settings.api + '/*',
tokenOptions: {
scope: 'my-scope',
audience: environment.settings.auth0.audience,
},
},
],
},
});
return new Observable((subscriber) => {
subscriber.next();
subscriber.complete();
});
})
);
}
My module look like this (I'm loading Auht0Module through my service module, but, I've also tried to load it directly in the appModule, and got the same error)
@NgModule({
declarations: [],
imports: [
CommonModule,
HttpClientModule,
AuthModule.forRoot(),
],
})
export class ServicesModule {
public static forRoot(): ModuleWithProviders<ServicesModule> {
return {
ngModule: ServicesModule,
providers: [
...SERVICE_PROVIDERS,
{
provide: APP_INITIALIZER,
useFactory: init_app,
deps: [EnvironmentService],
multi: true,
},
{ provide: HTTP_INTERCEPTORS, useClass: AuthHttpInterceptor, multi: true },
],
} as ModuleWithProviders<ServicesModule>;
}
}
At the initialization of my app , I got the following Error
environment.service.ts:37 Error: Configuration must be specified either through AuthModule.forRoot or through AuthClientConfig.set
at Object.createClient [as useFactory] (auth0-auth0-angular.js:18)
at Object.factory (core.js:11378)
at R3Injector.hydrate (core.js:11289)
at R3Injector.get (core.js:11111)
at injectInjectorOnly (core.js:899)
at Module.ɵɵinject (core.js:903)
at Object.AuthHttpInterceptor_Factory [as factory] (auth0-auth0-angular.js:491)
at R3Injector.hydrate (core.js:11289)
at R3Injector.get (core.js:11111)
at injectInjectorOnly (core.js:899)
If I remove the HTTP_INTERCEPTORS
, the application load, but, obviously, Access_token is not attached to the requets
It seems that Angular is trying to init the Interceptors for the HttpClient, but, since I've not supplied a configuration yet, the Auth0Interceptor fails to init.
Am I missing something in order to make this works ?
Sorry for commenting in a closed issue, but it seems really related to this issue !
Hi @gagaXD I am using a web request to fetch my configuration, I just didn't show it in my example above to simplify things. Let me share what I have, and you can try and compare and see what's different or get a clue.
In app.module.ts, under providers I have:
providers: [
AppConfigService,
{ provide: HTTP_INTERCEPTORS, useClass: AuthHttpInterceptor, multi: true },
{ provide: APP_INITIALIZER,useFactory: initializeApp, deps: [AppConfigService], multi: true}
],
The initializeApp method is:
import { AppConfigService } from './app-config.service';
export function initializeApp(appConfigService: AppConfigService) {
return (): Promise<any> => {
return appConfigService.load();
}
}
You can see it's actually calling another service.
Now in my app-config.service.ts
note that I'm using HttpBackend
with the HttpClient
so that the interceptor doesn't intercept :laughing: The reason for the separate class is because it's going to hold all my other UI settings, I want to pass it around my application.
import { Injectable } from '@angular/core';
import { HttpClient, HttpBackend } from '@angular/common/http';
import { AuthClientConfig, AuthConfig, AuthConfigService } from '@auth0/auth0-angular';
@Injectable()
export class AppConfigService {
static settings: IAppConfig;
httpClient: HttpClient;
handler: HttpBackend;
authClientConfig: AuthClientConfig;
constructor(private http: HttpClient, handler: HttpBackend, authClientConfig: AuthClientConfig) {
this.httpClient = http;
this.handler = handler;
this.authClientConfig = authClientConfig;
}
load() {
const jsonFile = `https://api.npoint.io/86e813b730424ff5fcd0`;
return new Promise<void>((resolve, reject) => {
this.httpClient = new HttpClient(this.handler);
this.httpClient.get(jsonFile).toPromise().then((response : IAppConfig) => {
AppConfigService.settings = <IAppConfig>response;
this.authClientConfig.set({
clientId: AppConfigService.settings.auth0ClientId, domain: AppConfigService.settings.auth0Domain,
httpInterceptor: { allowedList: [
{
uri: "/api/*",
tokenOptions: {
audience: "my-api"
}
}
] }
});
console.log('Config Loaded');
console.log( AppConfigService.settings);
resolve();
/*}).catch((response: any) => {
reject(`Could not load the config file`);*/
});
});
}
}
export interface IAppConfig {
auth0ClientId: string
auth0Domain: string
}
Thx @mendhak
The important line I was missing was in my loading service
this.httpClient = new HttpClient(this.handler);
With this line, you create a new HttpClient, that "ignore" HTTP_INTERCEPTOR
. Seems more like a workaround to me, but, it works great, so I'll go with this solution !
Thank you for the help !
Thanks for the example @mendhak - glad you were able to help @gagaXD with it 👍
No worries, I was doing my own writeup on Angular + auth0-angular library + dynamic configuration. Hope it helps future searchers since there are a lot of moving parts to getting it set up properly.
I'm also facing this issue:
main.ts:34 Error: Configuration must be specified either through AuthModule.forRoot or through AuthClientConfig.set
at Object.createClient [as useFactory] (auth0-auth0-angular.js:18)
at Object.factory (core.js:11247)
at R3Injector.hydrate (core.js:11158)
at R3Injector.get (core.js:10979)
at injectInjectorOnly (core.js:4907)
Unfortunately I have other app initializers that ultimately need to access the HttpClient. I'm using the this.httpClient = new HttpClient(handler);
trick in my config service, but can't use it in the other initializers because those need to have the access token attached.
I've tried ordering the app initializers using this https://github.com/fvilers/ngx-ordered-initializer but it didn't help. I still end up with the same error.
I think that's a hard thing to solve @dsebastien . If u have App Initializers that need the access token but only get the details for initializing the Auth0 SDK in another App Initializer, it can get tricky.
U could fetch the config outside of Angular and only once that call is done bootstrap the Angular App and use the config directly, that should make it available in AppInitializers.
Ok ok, I see what you mean. Indeed, that should do it. I just have to jump through some hoops to pass the config loaded outside of angular to the config service; I guess that I can put it in a global or in local storage for a second ;-)
I would've hoped to be able to set a temporary config with AuthModule.forRoot, and update it a jiffy afterwards. But indeed, it's probably complicated on your end.
Thanks @frederikprijck
Ye I think this is partly because of how Angular handles AppInitializers and their order.
I would've hoped to be able to set a temporary config with AuthModule.forRoot, and update it a jiffy afterwards. But indeed, it's probably complicated on your end.
U can do that... But it is not going to solve your problem as your AppInitializers will use the static config passed to forRoot while they might need the dynamic config that is being loaded in the other App Initializer.
What I mean is that the Auth0 interceptor could probably be notified when the AuthConfig config changes, and use the new one instead of having it statically defined once and for all. Anyways, I won't bother you anymore, I think I can move on with the project ;-)
Thanks again
Describe the problem
I am using the new dynamic configuration to set the various Auth0 Config properties like domain, clientId, and httpInterceptor.
Problem - The httpInterceptor uri/audience options aren't being picked up. As a result, my
http.get
API calls do not have an Authorization header on them.What was the expected behavior?
Authorization header should appear on API requests when specified in httpInterceptor
Reproduction
Following the Dynamic Configuration instructions, I didn't pass anything in the
.forRoot()
in app.module.ts imports section.I set up the App Initializer and HTTP Interceptor:
I set the values directly in the configInitializer
The
httpInterceptor
does not seem to get picked up. The library does not pass any audience when requesting a token, and no Authorization header is present on calls to/api/...
.If I just copy paste that configuration back into
.forRoot()
, all behavior returns to normal, and Authorization header is passed in API requests.CC @stevehobbsdev please can you try this out, does
httpInterceptor
work for you?Environment
auth0-angular
used:: 1.1.0