22

I'm using Angular 6 with an HTTP Interceptor configured to apply bearer token to outgoing requests.

  • In the dev build (ng serve), the token is applied and everything works fine. :-)

  • In the production build (ng serve --prod) the request is sent out without bearer token. :-(

In the prod build, I have verified the header is being applied, by dumping the headers to the console after applying them.

I have no idea why they are excluded from the http request.

There are NO differences in my environment files.

What else should I be looking at?

missing bearer token

What can I do to fix this?

At first I thought this was an issue between my local environment and my staging environment, but then I tried running ng serve --prod locally and saw the same results.

All that to say, everything is identical except one being a production build and one being a dev build.

jwt-interceptor:

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        // add authorization header with jwt token if available
        let currentUser = JSON.parse(localStorage.getItem('currentUser'));

        if (currentUser && currentUser.token) {
            request = request.clone({
                setHeaders: {
                    Authorization: `Bearer ${currentUser.token}`
                }
            });
            console.log('headers:', request.headers); // <---- I can see headers in console output
        }

        return next.handle(request);
    }
}

Here's what I see in the console: screenshot of console output

app.module.ts

import { HttpClientModule, HttpClient, HttpInterceptor } from '@angular/common/http';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { PortalModule } from '@angular/cdk/portal';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

import { JwtInterceptor } from './jwt-interceptor';
import { ENV } from '../environments/environment';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
... 
import { myApiService } from './services/my-api.service';
import { myModalComponent } from './_components/my-modal/my-modal.component';
import { myModalService } from './services/my-modal.service';

import { AngularLaravelEchoModule, PusherEchoConfig, EchoInterceptor } from 'angular-laravel-echo/angular-laravel-echo';

export const echoConfig: PusherEchoConfig = {
    userModel: 'App.User',
    notificationNamespace: 'App\\Notifications',
    options: {
        broadcaster: 'pusher',
        key: ENV.pusherConfig.key,
        cluster: ENV.pusherConfig.cluster,
        host: ENV.apiRoot,
        authEndpoint: ENV.apiRoot + '/broadcasting/auth',
    }
};

@NgModule({
    declarations: [
        AppComponent,
        ...
    ],
    imports: [
        BrowserModule,
        HttpClientModule,
        BrowserModule,
        AppRoutingModule,
        FormsModule,
        ReactiveFormsModule,
        PortalModule,
        AngularLaravelEchoModule.forRoot(echoConfig)
    ],
    providers: [
        myApiService,
        myModalService,
        {
            provide: HTTP_INTERCEPTORS,
            useClass: JwtInterceptor,
            multi: true,
        },
        {
            provide: HTTP_INTERCEPTORS,
            useClass: EchoInterceptor,
            multi: true
        }
    ],
    bootstrap: [AppComponent],
    entryComponents: [ 
        myModalComponent
    ]
})

export class AppModule {
}
Robin Dijkhof
  • 18,665
  • 11
  • 65
  • 116
BizzyBob
  • 12,309
  • 4
  • 27
  • 51
  • 1
    Have you checked that `currentUser` has any value? – Julius Dzidzevičius Oct 29 '18 at 06:29
  • 1
    Yes, the value is there, and further, the header with the defined token value is applied (I can see this in the console output from the interceptor) – BizzyBob Oct 29 '18 at 06:33
  • 1
    Hm.. Might be some problem with `AOT` – Julius Dzidzevičius Oct 29 '18 at 06:42
  • 1
    Show me the difference between dev/prod in terms of files. What do you have in dev that you dont' in prod? – Mike Tung Oct 31 '18 at 14:42
  • @Mike can you please be more specific? To my knowledge, there is no intentional differences. But, I’m not familiar with what all happens when angular generates a production build. – BizzyBob Oct 31 '18 at 17:06
  • 2
    During production, angular looks at `environment.prod.ts` is there anything different in there? Also in your `AppModule` do you have anything that is production only? – Mike Tung Oct 31 '18 at 19:17
  • post your module code and service.ts – Sajeetharan Nov 01 '18 at 01:03
  • @MikeTung the contents of `environment.ts` and `environment.prod.ts` files are identical. I don't believe there is any code in the app.module.ts file that is prod only. (code added to question) – BizzyBob Nov 01 '18 at 09:23
  • What does your `EchoInterceptor` do ? And when you say you see teh console log headers, you do see the correct value for the token right? – David Nov 01 '18 at 09:24
  • @BizzyBob For the first time when the currentUser is not set how are you storing the token in the localstorage? – Niladri Nov 01 '18 at 09:32
  • EchoInterceptor comes from a package called [laravel-angular-echo](https://github.com/chancezeus/angular-laravel-echo/blob/master/src/lib/src/services/interceptor.service.ts) that adds an `x-socket-id` header to the request. – BizzyBob Nov 01 '18 at 09:33
  • @Niladri my apiService has a login method that sets the token in local storage upon successful login. `localStorage.setItem('currentUser', JSON.stringify(response));` – BizzyBob Nov 01 '18 at 09:38
  • @David As a troubleshooting step, I removed EchoInterceptor reference completely and still see same result, so I don't think that is messing things up – BizzyBob Nov 01 '18 at 09:53
  • Just to clarify, in your `console.log` you see the correct Bearer token? or can you just see that there is an `Authorization` header? – David Nov 01 '18 at 09:54
  • @David Yes, the correct bearer token is included in the output. I added a screenshot above of the console output. – BizzyBob Nov 01 '18 at 10:00
  • @BizzyBob can you try and mock the currentUser.token ? My guess is that the problem is with how local storage is managed in production builds. Also, are you using angular universal? – Lucian Moldovan Nov 01 '18 at 10:08
  • @LucianMoldovan you mean just hardcode a static value as a test? – BizzyBob Nov 01 '18 at 10:09
  • @LucianMoldovan good thought about local storage in prod builds.. but I'm seeing same result after using hardcoded string value in place of retrieving from local storage. I am not using Angular Universal. – BizzyBob Nov 01 '18 at 10:16
  • @BizzyBob That's odd. Can you log the entire request just before it's being returned to the handler? And check if the Authorization Token is correct. – Lucian Moldovan Nov 01 '18 at 10:21
  • @LucianMoldovan do you mean from the http interceptor code, or from somewhere else? – BizzyBob Nov 01 '18 at 11:30
  • @BizzyBob Yes, from the interceptor code. – Lucian Moldovan Nov 02 '18 at 07:50
  • @BizzyBob have you tried to `setHeaders` for the original object instead of creating a clone and passing it further ? – Yuriy Kravets Nov 02 '18 at 07:54
  • @BizzyBob do you have any lazyloaded Modules in your app? Any chance you provide a minimal working version of the relevant parts on blitzstack using a hardcoded token? – A.Winnen Nov 06 '18 at 00:58

6 Answers6

3

I wrote this app in StackBlitz, and it's working fine when I run it locally with ng serve --prod.

https://stackblitz.com/edit/angular-yzckos

Download it and run it to see if you're still getting undefined in your network tab. If you can see the header properly being sent, then there's definitely something funny in your code.

Try bellow :

1- try running `ng serve --port=aDifferentPort // like 2098

Maybe there's something running on that port and sending auth header

2- Try with AOT false, can't think of why that would cause any issue

3- Make sure your browser doesn't have any extension that overrides the Auth header or try other browsers

4- Turn off your other HTTP interceptors, maybe one of them does something unexpected

5- Change the header name from Authorizaion to MyAuthorization, see if you're still getting undefined, if you don't, then it's being overridden by a something, check your package.json and make sure you're not running anything else on the production serve.

6- Turn off the JwtInterceptor altogether and try attaching the authorization header to your HTTP request, see if you're still getting undefined.

7- If none helped, you really need to send more code to us :)

Milad
  • 27,506
  • 11
  • 76
  • 85
  • 1
    Thank you so much for the list of things to check! In my case, doing aot=false prevented the issue in the prod build. Then, your suggestion #5 I saw what was really going wrong. For this http call, I could see that the interceptor was NOT applying the header at all. The reason I didn't notice this, is because I am using a package that has a configuration option to set the Authorization header for calls that it makes, so it was setting the header. For some reason with AOT, the code I put in the config for that package, that grabs the token value from local storage returns undefined. – BizzyBob Nov 11 '18 at 16:40
  • So, I’m not going to investigate why that is. but rather figure out why my interceptor isn’t applying the header to this request. Thanks again for your advice! I now have a direction to go in. – BizzyBob Nov 11 '18 at 16:41
  • `AOT` is really buggy and untrustable, it swallows the errors and has lots of unexpected behaviours. If you want AOT, make sure to run your `ng serve --aot=true`. But that's broken ( as of v5) and for every significant file change, you have to restart it. – Milad Nov 11 '18 at 22:34
  • Well, I don't know much about it. I didn't specifically "want" it, but it seemed to offer a few benefits, mainly smaller file size. I am noticing a couple bugs and error swallowing as you have mentioned. – BizzyBob Nov 11 '18 at 22:37
  • 1
    Like in my case, my code that is setting the headers, the code after AOT completely removes lines that include function calls like `JSON.parse` or 'localStorage.getItem`. That's what was breaking it for me with the prod build. But there was no warning from the CLI that there was a problem. – BizzyBob Nov 11 '18 at 22:38
  • I had the same issue. The 'Try with AOT false' in angular.json solved it for me. I also had to set the "buildOptimizer" to false. – Rutger van Dijk Mar 13 '19 at 10:23
  • Thank you for your suggestion,after changing header name from Authorizaion to MyAuthorization in http-intercepter it working perfectly for me. – dipti Apr 03 '19 at 12:12
0

I have had almost the similar issue in the production environment where server completely ignores the Authorization header. Angular 6 sends the Authorization header properly but server strips out completely (Due to most of production server, shared hosting security settings). I know this might not be the answer you looking for. But, I just wanted give you a clue.

So, finally for me to get this working, I had to use a different header parameter such as Php-Auth-Digest, like this.

request = request.clone({
    setHeaders: {
      "Php-Auth-Digest": `Bearer ${currentUser.token}`,
    }
  });

As a workaround try changing your header parameter name.

Cheers!

Anjana Silva
  • 8,353
  • 4
  • 51
  • 54
  • Thanks for the response @Anjana. I will check this out. However, I'm using the same environment in both cases (my local machine) and I'm still seeing the header missing only when using --prod flag. It makes sense that a difference in the server could cause this, but since my server is the same, it seems like the generated code is different on the client side. Do you think this still applies in this case? – BizzyBob Nov 01 '18 at 09:40
  • @BizzyBob - I would just give it a try by changing the `header` name. When you host, one day you might hit with this issue. So, it's worth giving a try anyway. https://forums.cpanel.net/threads/apache-strips-out-custom-header.624799/ – Anjana Silva Nov 01 '18 at 09:48
0

Can you try setting the header in the actual api call? Like, example:

put(path: string, body: Object = {}): Observable<any> {
return this.http.put(`${environment.api_url}${path}`, body, { headers: 
     this.setHeaders() })
     .map((res: Response) => {
        return res;
     });
}

private setHeaders(): HttpHeaders {
    const headersConfig = {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Authorization': 'Bearer ' + this.oauthService.getAccessToken()
    };
    return new HttpHeaders(headersConfig);
}

And interceptor will have just the

request.clone() 
Minu
  • 232
  • 4
  • 13
0

You can try cloning the headers manually in your request.clone() method. This is what works for me:

export class HttpHeaderInterceptor implements HttpInterceptor {
  // ...
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // ...
    const clonedRequest = req.clone({ 
      headers: req.headers.set('Authorization', 'Bearer ' + currentUser.token) 
    });
    return next.handle(clonedRequest).pipe(
      catchError(err => { /* Error handling here */ })
    );
  }
}

Hope this helps a little :-)

Heehaaw
  • 2,677
  • 17
  • 27
0

I have an idea about this - but I'm not sure it might work or not please check

HttpHeaders are mutable, if you add any header it updates the existing one and appends the value - so this cause me a problem in appending a header so followed the below method:

private getHeaders(): HttpHeaders {
    let headers = new HttpHeaders();
    headers = headers.append("Content-Type", "application/json");
    return headers;
  }

Since, I append the new headers and assigned the object to the original object and returned the object - This worked for me fine in both prod and dev build

But in your case you can use the same method above in your HttpInterceptor or try to change the setheaders with headers as below mentioned sample

if (currentUser && currentUser.token) {
            request = request.clone({
                headers: new HttpHeaders({
                    Authorization: `Bearer ${currentUser.token}`
                })
            });
            console.log('headers:', request.headers); 
        }

I'm sure this will solve your problem in both the builds - try and let me know if it doesn't work - Hope it works thanks - happy coding !!

Rahul
  • 2,040
  • 2
  • 11
  • 29
-2

Try this

if (currentUser && currentUser.token) {
        request = request.clone({
            setHeaders: {
                Authorization: `Bearer ${currentUser.token}`
            }
        });
        console.log('headers:', request.headers); // <---- I can see headers in console output
    }
if (typeof $ != 'undefined') {
    $.ajaxSetup({
      beforeSend: function (xhr: any) {
        xhr.setRequestHeader('Authorization', 'Bearer ' + currentUser.token);
      }
    });
  }
    return next.handle(request);
Max
  • 794
  • 3
  • 7