1

I am using ngx-admin for my new app. I have utilised the Nebular Auth framework to use JWT tokens to enable access to the back-end REST server.

I can successfully authenticate and access the REST server when using Postman to test the API by formatting the Authorisation HTTP header with the token in the format JWT <token>. The issue with accessing the API from my ngx-admin based app is that the NbAuthJWTInterceptor class is presenting the Authorisation HTTP header in the format Bearer JWT <token> and thus my back-end API cannot extract the token.

How do I configure or override the NbAuthJWTInterceptor class to set the Authorisation HTTP header in the format JWT <token>?

On the client end I am using:

  • ngx-admin 3.2.1
  • angular 7.2.1
  • nebular/auth 3.4.2

On the server end I am using the following with MongoDB:

  • express 4.6.13
  • passport 0.4.0
  • passport-jwt 4.0.0
  • jsonwebtoken 8.5.1
  • mongoose 5.1.7

I have tested various calls (GET, POST, PUT, DELETE) to my API with a token I have successfully signed in with using Postman and formatted the Authorization token as JWT <token> and the request was authorised and the correct data was returned.

When the same requests were presented by my app, the NbAuthJWTInterceptor class formats the Authorisation token as Bearer JWT <token> and so the request is rejected as "Unauthorised"

Accessing and decoding the Authorisation token at the REST server end:

module.exports = function (passport) {
    var opts = {};
    opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('JWT');
    opts.secretOrKey = config.secret;

    passport.use(new JwtStrategy(opts, function (jwt_payload, done) {
        User.findOne({
            id: jwt_payload._id        
        }, function (err, user) {
            if (err) {
                return done(err, false);
            }
            if (user) {
                done(null, user);
            } else {
                done(null, false);
            }
        });
    }));
};

Configuring the API endpoints and HTTP Interceptor to inject the Authorisation token on the client end:

@NgModule({
    declarations: [AppComponent],
    imports: [
        BrowserModule,
        BrowserAnimationsModule,
        HttpClientModule,
        AppRoutingModule,

        // NbEvaIconsModule,

        NgbModule.forRoot(),
        ThemeModule.forRoot(),
        CoreModule.forRoot(),

        NbAuthModule.forRoot({
            strategies: [
                NbPasswordAuthStrategy.setup({
                    name: 'email',
                    token: {
                        class: NbAuthJWTToken,
                        key: 'token',
                    },

                    baseEndpoint: '/api',
                    login: {
                        endpoint: '/auth/signin',
                        method: 'post',
                    },
                    register: {
                        endpoint: '/auth/signup',
                        method: 'post',
                    },
                    logout: {
                        endpoint: '/auth/sign-out',
                        method: 'post',
                    },
                    requestPass: {
                        endpoint: '/auth/request-pass',
                        method: 'post',
                    },
                    resetPass: {
                        endpoint: '/auth/reset-pass',
                        method: 'post',
                    },
                }),
            ],
            forms: {
                login: formDelaySetting,
                register: formDelaySetting,
                requestPassword: formSetting,
                resetPassword: formSetting,
                logout: {
                    redirectDelay: 0,
                },
            },
        }),

        NbThemeModule.forRoot({ name: 'corporate' }),

        NbToastrModule.forRoot(),

        NbLayoutModule,
    ],
    bootstrap: [AppComponent],
    providers: [
        { provide: APP_BASE_HREF, useValue: '/' }, 
        { provide: HTTP_INTERCEPTORS, useClass: NbAuthJWTInterceptor, multi: true },
        { provide: NB_AUTH_TOKEN_INTERCEPTOR_FILTER, useValue: (req) => { return false; } },
    ],
})
Dave Boulden
  • 23
  • 1
  • 6

2 Answers2

1

I ended up digging through all the @Nebular/auth module code and eventually found the culprit. The NbAuthJWTInterceptor has the 'Bearer ' part of the web token hard-coded. So I had to clone the class and make and then use my own HTTP Interceptor:

import { Inject, Injectable, Injector } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { NbAuthToken } from '@nebular/auth';
import { NbAuthService } from '@nebular/auth';
import { NB_AUTH_TOKEN_INTERCEPTOR_FILTER } from '@nebular/auth';

@Injectable()
export class NgxAuthJWTInterceptor implements HttpInterceptor {

  constructor(private injector: Injector,
              @Inject(NB_AUTH_TOKEN_INTERCEPTOR_FILTER) protected filter) {
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // do not intercept request whose urls are filtered by the injected filter
      if (!this.filter(req)) {
        return this.authService.isAuthenticatedOrRefresh()
          .pipe(
            switchMap(authenticated => {
              if (authenticated) {
                  return this.authService.getToken().pipe(
                    switchMap((token: NbAuthToken) => {
                      //const JWT = `Bearer ${token.getValue()}`;  <--- replace this line with the next
                      const JWT = `${token.getValue()}`;
                      req = req.clone({
                        setHeaders: {
                          Authorization: JWT,
                        },
                      });
                      return next.handle(req);
                    }),
                  )
              } else {
                 // Request is sent to server without authentication so that the client code
                 // receives the 401/403 error and can act as desired ('session expired', redirect to login, aso)
                return next.handle(req);
              }
            }),
          )
      } else {
      return next.handle(req);
    }
  }

  protected get authService(): NbAuthService {
    return this.injector.get(NbAuthService);
  }

}
Dave Boulden
  • 23
  • 1
  • 6
0

I'm using nebular/auth 4.2.1 and still has the same problem, checking the code appear some folders: esm2015 and esm5 with a few invocations but is not clear, in services only this:

export declare class NbAuthJWTInterceptor implements HttpInterceptor {
    private injector;
    protected filter: any;
    constructor(injector: Injector, filter: any);
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>;
    protected readonly authService: NbAuthService;
}
temp2010
  • 49
  • 5