diff --git a/README.md b/README.md index 497fd439..37e1a12a 100644 --- a/README.md +++ b/README.md @@ -152,19 +152,19 @@ It works the same as `useClass` with one critical difference - `JwtModule` will The `JwtService` uses [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) underneath. -#### jwtService.sign(payload: string | Object | Buffer, options?: SignOptions): string +#### jwtService.sign(payload: string | Object | Buffer, options?: JwtSignOptions): string -The sign method is an implementation of jsonwebtoken `.sign()`. +The sign method is an implementation of jsonwebtoken `.sign()`. Differing from jsonwebtoken it also allows an additional `secret` property on `options` to override the secret passed in from the module. It only overrides the `secret`, `publicKey` or `privateKey` though not a `secretOrKeyProvider`. -#### jwtService.signAsync(payload: string | Object | Buffer, options?: SignOptions): Promise\ +#### jwtService.signAsync(payload: string | Object | Buffer, options?: JwtSignOptions): Promise\ The asynchronous `.sign()` method. -#### jwtService.verify\(token: string, options?: VerifyOptions): T +#### jwtService.verify\(token: string, options?: JwtVerifyOptions): T -The verify method is an implementation of jsonwebtoken `.verify()`. +The verify method is an implementation of jsonwebtoken `.verify()`. Differing from jsonwebtoken it also allows an additional `secret` property on `options` to override the secret passed in from the module. It only overrides the `secret`, `publicKey` or `privateKey` though not a `secretOrKeyProvider`. -#### jwtService.verifyAsync\(token: string, options?: VerifyOptions): Promise\ +#### jwtService.verifyAsync\(token: string, options?: JwtVerifyOptions): Promise\ The asynchronous `.verify()` method. diff --git a/lib/interfaces/jwt-module-options.interface.ts b/lib/interfaces/jwt-module-options.interface.ts index 6e22fe1a..901e5070 100644 --- a/lib/interfaces/jwt-module-options.interface.ts +++ b/lib/interfaces/jwt-module-options.interface.ts @@ -33,3 +33,11 @@ export interface JwtModuleAsyncOptions extends Pick { useFactory?: (...args: any[]) => Promise | JwtModuleOptions; inject?: any[]; } + +export interface JwtSignOptions extends jwt.SignOptions { + secret?: string | Buffer; +} + +export interface JwtVerifyOptions extends jwt.VerifyOptions { + secret?: string | Buffer; +} diff --git a/lib/jwt.service.spec.ts b/lib/jwt.service.spec.ts index cf9ac070..42ee767c 100644 --- a/lib/jwt.service.spec.ts +++ b/lib/jwt.service.spec.ts @@ -191,4 +191,35 @@ describe('JWT Service', () => { .mockImplementation((token, secret, options) => secret); }); }); + + describe('should use secret key from options', () => { + let jwtService: JwtService; + + beforeAll(async () => { + jwtService = await setup({ + ...config, + secretOrKeyProvider: undefined + }); + }); + + let secret = 'custom'; + + it('signing should use secret key from options', async () => { + expect(await jwtService.sign('random', { secret })).toBe(secret); + }); + + it('signing (async) should use secret key from options', async () => { + expect(jwtService.signAsync('random', { secret })).resolves.toBe(secret); + }); + + it('verifying should use secret key from options', async () => { + expect(await jwtService.verify('random', { secret })).toBe(secret); + }); + + it('verifying (async) should use secret key from options', async () => { + expect(jwtService.verifyAsync('random', { secret })).resolves.toBe( + secret + ); + }); + }); }); diff --git a/lib/jwt.service.ts b/lib/jwt.service.ts index b0a11fe2..9f7bdd67 100644 --- a/lib/jwt.service.ts +++ b/lib/jwt.service.ts @@ -2,7 +2,9 @@ import { Inject, Injectable, Logger } from '@nestjs/common'; import * as jwt from 'jsonwebtoken'; import { JwtModuleOptions, - JwtSecretRequestType + JwtSecretRequestType, + JwtVerifyOptions, + JwtSignOptions } from './interfaces/jwt-module-options.interface'; import { JWT_MODULE_OPTIONS } from './jwt.constants'; @@ -14,9 +16,9 @@ export class JwtService { @Inject(JWT_MODULE_OPTIONS) private readonly options: JwtModuleOptions ) {} - sign(payload: string | Buffer | object, options?: jwt.SignOptions): string { + sign(payload: string | Buffer | object, options?: JwtSignOptions): string { const signOptions = this.mergeJwtOptions( - options, + { ...options }, 'signOptions' ) as jwt.SignOptions; const secret = this.getSecretKey( @@ -31,10 +33,10 @@ export class JwtService { signAsync( payload: string | Buffer | object, - options?: jwt.SignOptions + options?: JwtSignOptions ): Promise { const signOptions = this.mergeJwtOptions( - options, + { ...options }, 'signOptions' ) as jwt.SignOptions; const secret = this.getSecretKey( @@ -51,11 +53,8 @@ export class JwtService { ); } - verify( - token: string, - options?: jwt.VerifyOptions - ): T { - const verifyOptions = this.mergeJwtOptions(options, 'verifyOptions'); + verify(token: string, options?: JwtVerifyOptions): T { + const verifyOptions = this.mergeJwtOptions({ ...options }, 'verifyOptions'); const secret = this.getSecretKey( token, options, @@ -68,9 +67,9 @@ export class JwtService { verifyAsync( token: string, - options?: jwt.VerifyOptions + options?: JwtVerifyOptions ): Promise { - const verifyOptions = this.mergeJwtOptions(options, 'verifyOptions'); + const verifyOptions = this.mergeJwtOptions({ ...options }, 'verifyOptions'); const secret = this.getSecretKey( token, options, @@ -93,9 +92,10 @@ export class JwtService { } private mergeJwtOptions( - options: jwt.VerifyOptions | jwt.SignOptions, + options: JwtVerifyOptions | JwtSignOptions, key: 'verifyOptions' | 'signOptions' ): jwt.VerifyOptions | jwt.SignOptions { + delete options.secret; return options ? { ...(this.options[key] || {}), @@ -106,13 +106,13 @@ export class JwtService { private getSecretKey( token: string | object | Buffer, - options: jwt.VerifyOptions | jwt.SignOptions, + options: JwtVerifyOptions | JwtSignOptions, key: 'publicKey' | 'privateKey', secretRequestType: JwtSecretRequestType ): string | Buffer | jwt.Secret { let secret = this.options.secretOrKeyProvider ? this.options.secretOrKeyProvider(secretRequestType, token, options) - : this.options.secret || this.options[key]; + : options?.secret || this.options.secret || this.options[key]; if (this.options.secretOrPrivateKey) { this.logger.warn(