Skip to content

Commit

Permalink
Merge pull request #649 from auth0/feature/tokengetter-request
Browse files Browse the repository at this point in the history
Pass HttpRequest to the tokenGetter
  • Loading branch information
Sambego authored May 15, 2020
2 parents 07e880b + a04c2c2 commit 07289bd
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 20 deletions.
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class AppComponent {

## Configuration Options

### `tokenGetter: function`
### `tokenGetter: function(?HttpRequest)`

The `tokenGetter` is a function which returns the user's token. This function simply needs to make a retrieval call to wherever the token is stored. In many cases, the token will be stored in local storage or session storage.

Expand All @@ -104,6 +104,24 @@ JwtModule.forRoot({
});
```

If you have multiple tokens for multiple domains, you can use the `HttpRequest` passed to the `tokenGetter` function to get the correct token for each intercepted request.

```ts
// ...
JwtModule.forRoot({
config: {
// ...
tokenGetter: (request) => {
if (request.url.includes("foo")) {
return localStorage.getItem("access_token_foo");
}

return localStorage.getItem("access_token");
},
},
});
```

### `whitelistedDomains: array`

Authenticated requests should only be sent to domains you know and trust. Many applications make requests to APIs from multiple domains, some of which are not controlled by the developer. Since there is no way to know what the API being called will do with the information contained in the request, it is best to not send the user's token to all APIs in a blind fashion.
Expand Down
37 changes: 22 additions & 15 deletions projects/angular-jwt/src/lib/angular-jwt.module.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { NgModule, ModuleWithProviders, Optional, SkipSelf, Provider } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import {JwtInterceptor} from './jwt.interceptor';
import {JWT_OPTIONS} from './jwtoptions.token';
import {JwtHelperService} from './jwthelper.service';

import {
NgModule,
ModuleWithProviders,
Optional,
SkipSelf,
Provider,
} from "@angular/core";
import { HttpRequest, HTTP_INTERCEPTORS } from "@angular/common/http";
import { JwtInterceptor } from "./jwt.interceptor";
import { JWT_OPTIONS } from "./jwtoptions.token";
import { JwtHelperService } from "./jwthelper.service";

export interface JwtModuleOptions {
jwtOptionsProvider?: Provider;
config?: {
tokenGetter?: () => string | null | Promise<string | null>;
tokenGetter?: (
request?: HttpRequest<any>
) => string | null | Promise<string | null>;
headerName?: string;
authScheme?: string;
whitelistedDomains?: Array<string | RegExp>;
Expand All @@ -20,10 +27,11 @@ export interface JwtModuleOptions {

@NgModule()
export class JwtModule {

constructor(@Optional() @SkipSelf() parentModule: JwtModule) {
if (parentModule) {
throw new Error('JwtModule is already loaded. It should only be imported in your application\'s main module.');
throw new Error(
"JwtModule is already loaded. It should only be imported in your application's main module."
);
}
}
static forRoot(options: JwtModuleOptions): ModuleWithProviders<JwtModule> {
Expand All @@ -33,15 +41,14 @@ export class JwtModule {
{
provide: HTTP_INTERCEPTORS,
useClass: JwtInterceptor,
multi: true
multi: true,
},
options.jwtOptionsProvider ||
{
options.jwtOptionsProvider || {
provide: JWT_OPTIONS,
useValue: options.config
useValue: options.config,
},
JwtHelperService
]
JwtHelperService,
],
};
}
}
6 changes: 4 additions & 2 deletions projects/angular-jwt/src/lib/jwt.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { from, Observable } from "rxjs";

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
tokenGetter: () => string | null | Promise<string | null>;
tokenGetter: (
request?: HttpRequest<any>
) => string | null | Promise<string | null>;
headerName: string;
authScheme: string;
whitelistedDomains: Array<string | RegExp>;
Expand Down Expand Up @@ -112,7 +114,7 @@ export class JwtInterceptor implements HttpInterceptor {
) {
return next.handle(request);
}
const token = this.tokenGetter();
const token = this.tokenGetter(request);

if (token instanceof Promise) {
return from(token).pipe(
Expand Down
65 changes: 63 additions & 2 deletions src/app/services/example-http.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,22 @@ import {
import { JwtModule } from "angular-jwt";

export function tokenGetter() {
return "SOME_TEST_TOKEN";
return "TEST_TOKEN";
}

describe("ExampleHttpService", () => {
export function tokenGetterWithRequest(request) {
if (request.url.includes("1")) {
return "TEST_TOKEN_1";
}

if (request.url.includes("2")) {
return "TEST_TOKEN_2";
}

return "TEST_TOKEN";
}

describe("Example HttpService: with simple tokken getter", () => {
let service: ExampleHttpService;
let httpMock: HttpTestingController;

Expand Down Expand Up @@ -82,3 +94,52 @@ describe("ExampleHttpService", () => {
})
);
});

describe("Example HttpService: with request based tokken getter", () => {
let service: ExampleHttpService;
let httpMock: HttpTestingController;

const routes = [
`http://example-1.com/api/`,
`http://example-2.com/api/`,
`http://example-3.com/api/`,
];

beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
JwtModule.forRoot({
config: {
tokenGetter: tokenGetterWithRequest,
whitelistedDomains: [
"example-1.com",
"example-2.com",
"example-3.com",
],
},
}),
],
});
service = TestBed.get(ExampleHttpService);
httpMock = TestBed.get(HttpTestingController);
});

it("should add Authorisation header", () => {
expect(service).toBeTruthy();
});

routes.forEach((route) =>
it(`should set the correct auth token for a domain: ${route}`, () => {
service.testRequest(route).subscribe((response) => {
expect(response).toBeTruthy();
});

const httpRequest = httpMock.expectOne(route);
expect(httpRequest.request.headers.has("Authorization")).toEqual(true);
expect(httpRequest.request.headers.get("Authorization")).toEqual(
`Bearer ${tokenGetterWithRequest({ url: route })}`
);
})
);
});

0 comments on commit 07289bd

Please sign in to comment.