@origins-digital/nestjs-shared-key-http
v1.0.2
Published
NestJS HttpService that adds the internal shared-key auth headers to calls between Origins services
Readme
@origins-digital/nestjs-shared-key-http
HttpService for calls between Origins services: it adds the internal service
token (x-internal-auth-token) and x-service-name, so services no longer
build those headers by hand.
Rule (applyInternalAuth)
The internal headers are added only when the request has neither an
authorization nor an x-internal-auth-token header key (case-insensitive,
whatever the value, undefined, null and '' included). Key presence means "the
caller decided": a forwarded session token that is missing is not replaced by
the internal token, so the service still answers 401.
- A caller-provided
x-internal-auth-tokenorx-service-nameis never overwritten. - The JWT is fetched only when it will be sent.
- If the token cannot be resolved, the request rejects before it is sent.
Installation
yarn add @origins-digital/nestjs-shared-key-httpPeer dependencies: @nestjs/common, @nestjs/axios, axios, rxjs,
@origins-digital/nestjs-shared-key.
Usage
SharedKeyHttpService needs SharedKeyService. The module never registers
SharedKeyModule itself, so the app keeps its key backend (SSM or Secrets
Manager) and its options. Either register it globally once:
@Module({
imports: [
SharedKeyModule.forRoot({ isGlobal: true }),
SharedKeyHttpModule, // default options
],
})
export class AppModule {}or pass the module that provides it:
SharedKeyHttpModule.register({
serviceName: 'origins-backoffice-orchestrator', // x-service-name
imports: [SharedKeyModule],
axiosConfig: { timeout: 10_000 }, // defaults of the module's own axios instance
isGlobal: false,
});Then inject it (it extends HttpService):
@Injectable()
export class VideoClient {
constructor(private readonly http: SharedKeyHttpService) {}
async getVideo(id: string) {
const { data } = await this.http.getAsPromise<Video>(`${host}/videos/${id}`, {
requestedService: 'origins-video-service',
});
return data;
}
}getAsPromise,postAsPromise,putAsPromise,patchAsPromise,deleteAsPromise,headAsPromiseturn HTTP errors into the Nest exception built byraiseError(@origins-digital/error-utils), labelled withrequestedService. Other errors pass through unchanged.- The Observable methods of
HttpService(get,post, ...) apply the same rule. - The service uses its own axios instance: a plain
HttpModule(calls to external hosts) never gets the internal token.
x-service-name defaults to ${APP_ENV}-${npm_package_name} (the package name
alone without APP_ENV, unknown-service without a package name). Note that
npm_package_name is only set when the app is started through npm/yarn; pass
serviceName to make it explicit.
Other clients (hey-api)
The rule is a plain axios request interceptor, reusable on any axios instance:
client.instance.interceptors.request.use((config) =>
applyInternalAuth(config, () => sharedKeyHttp.internalHeaders()),
);
keepCallerAuthKeys(client);hey-api drops header keys whose value is undefined or null before axios
runs, so the rule would not see authorization: undefined (or null). keepCallerAuthKeys(client)
wraps the client's request methods and sends '' instead
(withCallerAuthKeys(options) is the pure version). Nest's HttpService keeps
undefined keys, so SharedKeyHttpService does not need it.
Build
yarn build
yarn test
yarn lint