Angular2-jwt: Authorisation not sent (possibly whitelist issue)

Created on 1 Feb 2018  ·  29Comments  ·  Source: auth0/angular2-jwt

Exactly the same issue as here https://github.com/auth0/angular2-jwt/issues/464

It was closed because he ditched auth0 however, that is not an option for us.

Locally it works locally with http using whitelist: ['localhost:3000'], how when deployed on https, with or without the port in the whitelist it fails whitelist: ['api.server.com:443']

stale

Most helpful comment

I got this to work by changing from the default setup...

JwtModule.forRoot({
      config: {
        tokenGetter: () => {
          return localStorage.getItem('access_token');
        },
        whitelistedDomains: [environment.whitelist]
      }
    }),

to the custom setup...

export function jwtOptionsFactory() {
  return {
    tokenGetter: () => {
      return localStorage.getItem('access_token');
    },
    whitelistedDomains: environment.whitelist
  }
}

JwtModule.forRoot({
          jwtOptionsProvider: {
            provide: JWT_OPTIONS,
            useFactory: jwtOptionsFactory
          }
        }),

Where environment is...

export const environment = {
  production: true,
  api: 'https://example.io',
  whitelist: ['example.io', 'https://example.io', 'example:443'],
  version: 'x.x.x',
};

Shortly, I will investigate further to understand more, but hopefully that helps someone.

All 29 comments

I got this to work by changing from the default setup...

JwtModule.forRoot({
      config: {
        tokenGetter: () => {
          return localStorage.getItem('access_token');
        },
        whitelistedDomains: [environment.whitelist]
      }
    }),

to the custom setup...

export function jwtOptionsFactory() {
  return {
    tokenGetter: () => {
      return localStorage.getItem('access_token');
    },
    whitelistedDomains: environment.whitelist
  }
}

JwtModule.forRoot({
          jwtOptionsProvider: {
            provide: JWT_OPTIONS,
            useFactory: jwtOptionsFactory
          }
        }),

Where environment is...

export const environment = {
  production: true,
  api: 'https://example.io',
  whitelist: ['example.io', 'https://example.io', 'example:443'],
  version: 'x.x.x',
};

Shortly, I will investigate further to understand more, but hopefully that helps someone.

I'm also getting this problem and can confirm using the factory fixes it.

Angular CLI: 1.6.4
Node: 8.9.4
OS: win32 x64
Angular: 5.2.0
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

@angular/cli: 1.6.4
@angular-devkit/build-optimizer: 0.0.37
@angular-devkit/core: 0.0.24
@angular-devkit/schematics: 0.0.45
@ngtools/json-schema: 1.1.0
@ngtools/webpack: 1.9.4
@schematics/angular: 0.1.12
typescript: 2.5.3
webpack: 3.10.0

Even that my solution above didn't work correctly, not sure what changed.

I reverted back to the older version with using two http clients...

export function authHttpServiceFactory(http: Http, options: RequestOptions) {
  return new AuthHttp(new AuthConfig({
    tokenGetter: (() => localStorage.getItem('access_token')),
    globalHeaders: [{'Content-Type':'application/json'}]
  }), http, options);
}
{
     provide: AuthHttp,
     useFactory: authHttpServiceFactory,
     deps: [Http, RequestOptions]
}

Maybe it is connected to this issue:
from the documentation, if whitelistedDomains is not specified, it should work if the request is made to the same serving domain.
From this code, taken fromjwt.interceptor.js :

JwtInterceptor.prototype.isWhitelistedDomain = function (request) {
        var requestUrl;
        try {
            requestUrl = new URL(request.url);
            return (this.whitelistedDomains.findIndex(function (domain) {
                return typeof domain === 'string'
                    ? domain === requestUrl.host
                    : domain instanceof RegExp ? domain.test(requestUrl.host) : false;
            }) > -1);
        }
        catch (err) {
            // if we're here, the request is made
            // to the same domain as the Angular app
            // so it's safe to proceed
            return true;
        }
    };

looks like if whitelistedDomains is empty, it will always return -1 thus the request will be forbidden (the condition > -1 will never be true).

EDIT: This should fix the issue?

return (this.whitelistedDomains.length > 0? this.whitelistedDomains.findIndex(function (domain) {
                return typeof domain === 'string'
                    ? domain === requestUrl.host
                    : domain instanceof RegExp ? domain.test(requestUrl.host) : false;
            }) > -1 : window.location.hostname === requestUrl.hostname);

I have the same issue than @eddiejaoude. I need to parametrize the whitelistedDomains with the current location, but I can't figure out the way.

If you leave the whitelist empty the isWhitelistedDomainmethod will _always_ return false. It will never match anything, and hence never send the authorization headers. (This is contrary to the documentation, which implies that having an empty whitelist _will_ match local domain requests.)

Workaround - If you use domain-less routes, the workaround is to add the null domain to the whitelist, which you can do through a RegExp. In other words, do this:

whitelistedDomains: [  /^null$/  ]

@PartyArk Your workaround does not work for me. This is the error that I am getting because of the regex:

ERROR in app\app.module.ts(97,11): Error during template compile of 'AppModule'
  Expression form not supported.

@PartyArk thank you! This worked for us.

whitelistedDomains makes direct comparation between the domain and the requestUrl.host, so is comparing for example 'localhost' === 'localhost:80' --> always false

i changed ( domain === requestUrl.host) to (domain === requestUrl.host.split(':')[0]) , dont need to put obvious things like the 80 port.

I think it puts de port in the string when you are for example in port 4200 and the server is in 80.

source: @auth/angular-jwt/src/jwt.interceptor.js

Sorry if what i did is dumb, i am still learning.
Sorry too for my english.

Here is the original code:

JwtInterceptor.prototype.isWhitelistedDomain = function(request) { var requestUrl = URL.parse(request.url, false, true); return ( this.whitelistedDomains.findIndex(function(domain) { return typeof domain === 'string' ? domain === requestUrl.host <-- Here is the "problem" (at least in my context) : domain instanceof RegExp ? domain.test(requestUrl.host) : false; }) > -1 ); };

Over 2 months and still no official solution 😞. Is this project dead?

Here is a work around to monkey patch the interceptor and send authorization headers to relative URLs where the host is null. I import it from my main.ts.

import { JwtInterceptor } from '@auth0/angular-jwt';
import { HttpRequest } from '@angular/common/http';
import * as URL from 'url';

// https://github.com/auth0/angular2-jwt/issues/481
if (JwtInterceptor.prototype['_isWhitelistedDomain'] == null) {
    JwtInterceptor.prototype['_isWhitelistedDomain'] = JwtInterceptor.prototype.isWhitelistedDomain;

    // send tokens to null host (the server that originated the app)
    JwtInterceptor.prototype.isWhitelistedDomain = function isWhitelistedDomain(request: HttpRequest<any>): boolean {
        const requestUrl = URL.parse(request.url, false, true);
        if (requestUrl.host === null) {
            return true;
        }
        return this['_isWhitelistedDomain'](request);
    };
}

There is an issue about whitelisting urls without host: #518

Updated 5/2/18 - Modified jwtConf.whitelistedDomains

I had this issue today as well. I used a combination of the solutions above. I'm teetering on being redundant here, but hopefully my thorough solution below is worth it :)

// app.module.ts

// Put the tokenGetter function at the top of app.module.ts rather than inside of .forRoot
export function tokenGetter() {
  return localStorage.getItem('access_token');
};

// Declare a variable that utilized tokenGetter. Use /^null$/ for whitelistedDomains as an alternative to an empty array
const jwtConf = {
  config: {
    tokenGetter: tokenGetter,
    whitelistedDomains: new Array(new RegExp('^null$')),
    throwNoTokenError: true
  }
};

// use the variable inside of .forRoot
...
  imports: [
    HttpClientModule,
    JwtModule.forRoot(jwtConf),
...

When I raised this issue, I think I was using a beta or a RC, is this still an issue now a v1.1 (master branch) has been released?

Same issue for me. the whitelistedDomains: [ /^null$/ ] didn't work for me. I a using a urlRewrite can this have anything to do this with?

@eddiejaoude: Verified that I'm n 1.1.0. Still an issue.

@tbadlov: I couldn't get it to solely by whitelisting [ /^null$/ ]. I had to declare the tokenGetter() function outside of the .forRoot() method and declare the jwtConf variable.

So my code references the jwtConf varable from the .forRoot() method, then the jwtConf variable has the .config.tokenGetter property that references the tokenGetter() function.

In _dev_ mode the /^null$/ regex, works, but not when running in --aot mode.

Expression form not supported 

From another issue, here is a solution to the above comment that works with --aot.

whitelist: new Array(new RegExp('^null$'))

It doesn't work, using IIS to serve angular with URL Rewrite to call Express (using the same IIS server with IISNode). I had to use the solution provided by this comment. But essentially, it is not using the library which sucks.

@eddiejaoude I am also using 1.1.0 and still get this error.

We could whitelist all urls without a host by default as @ahafsi suggested here: #521

@eddiejaoude: Thanks. I updated my comment and whitelisted with the new syntax

@PartyArk I've been trying to solve this issue for over two months, and because of you suggestion I fixed it. Thank you very much! If by any chance you come to Canada someday I'll pay you a (bunch of) beer(s).

For anyone googling this who ended up here. The current config requires whitelistedDomains:[..] instead of whitelist:[..] and you only need to specify ports if you are using a non-standard port (i.e. port 80 and port 443 are standard http and https so don't need to be specified).

@kalahari thank you for your code, but it dosn't work, so I replaced
return this['_isWhitelistedDomain'](request);
by
return this['whitelistedDomains'].indexOf(requestUrl.host);
And this works fine.

Thanks @eddiejaoude , this code https://github.com/auth0/angular2-jwt/issues/481#issuecomment-362505569 resolved my issue.

I have debugged this routine using a common token factory and whitelist, but the whitelist are nothind injected in production enviroment. With your code, work's very fine.

Importing the whitelist directly from the environment file worked for me:

JwtModule.forRoot({
      config: {
        tokenGetter: tokenGetterFn,
        whitelistedDomains: environment.whitelist,
        blacklistedRoutes: [/.+\/api\/oauth\//],
      },
    }),

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! 🙇‍♂️

I was having this problem too, trying to whitelist localhost without the port. I solve it in this clean way, so you just need to change the host and port consts to what you want and all will continue working:

environment.ts

const host = 'localhost';
const port = '8089';

export const environment = {
  production: false,
  apiUrl: `http://${host}:8089`,
  whiteList: [`${host}:${port}`]
};

app.module.ts

//...
import { environment } from 'src/environments/environment';
//...
@NgModule({
  //...
  imports: [
    //...
    JwtModule.forRoot({
      config: {
        tokenGetter,
        whitelistedDomains: environment.whiteList,
        blacklistedRoutes: []
        }
      }),
    //...
  ],
  //...
})
//...
Was this page helpful?
0 / 5 - 0 ratings

Related issues

sfabriece picture sfabriece  ·  4Comments

huineng picture huineng  ·  4Comments

JaxonWright picture JaxonWright  ·  4Comments

getglad picture getglad  ·  5Comments

jaumard picture jaumard  ·  5Comments