diff --git a/types/index.d.ts b/types/index.d.ts index 82bc6d1..6817a24 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -18,25 +18,12 @@ declare module 'fastify' { } interface FastifyReply { - jwtSign(payload: fastifyJwt.SignPayloadType, options?: fastifyJwt.FastifyJwtSignOptions): Promise - jwtSign(payload: fastifyJwt.SignPayloadType, callback: SignerCallback): void - jwtSign(payload: fastifyJwt.SignPayloadType, options: fastifyJwt.FastifyJwtSignOptions, callback: SignerCallback): void - jwtSign(payload: fastifyJwt.SignPayloadType, options?: Partial): Promise - jwtSign(payload: fastifyJwt.SignPayloadType, options: Partial, callback: SignerCallback): void + jwtSign: fastifyJwt.JwtSignFunction } interface FastifyRequest { - jwtVerify(options?: fastifyJwt.FastifyJwtVerifyOptions): Promise - // eslint-disable-next-line @typescript-eslint/no-unused-vars - jwtVerify(callback: VerifierCallback): void - // eslint-disable-next-line @typescript-eslint/no-unused-vars - jwtVerify(options: fastifyJwt.FastifyJwtVerifyOptions, callback: VerifierCallback): void - jwtVerify(options?: Partial): Promise - // eslint-disable-next-line @typescript-eslint/no-unused-vars - jwtVerify(options: Partial, callback: VerifierCallback): void - jwtDecode(options?: fastifyJwt.FastifyJwtDecodeOptions): Promise - jwtDecode(callback: fastifyJwt.DecodeCallback): void - jwtDecode(options: fastifyJwt.FastifyJwtDecodeOptions, callback: fastifyJwt.DecodeCallback): void + jwtVerify: fastifyJwt.JwtVerifyFunction + jwtDecode: fastifyJwt.JwtDecodeFunction user: fastifyJwt.UserType } } @@ -45,6 +32,31 @@ type FastifyJwt = FastifyPluginCallback declare namespace fastifyJwt { + export interface JwtSignFunction { + (payload: SignPayloadType, options?: FastifyJwtSignOptions): Promise + (payload: SignPayloadType, callback: SignerCallback): void + (payload: SignPayloadType, options: FastifyJwtSignOptions, callback: SignerCallback): void + (payload: SignPayloadType, options?: Partial): Promise + (payload: SignPayloadType, options: Partial, callback: SignerCallback): void + } + + export interface JwtVerifyFunction { + (options?: FastifyJwtVerifyOptions): Promise + // eslint-disable-next-line @typescript-eslint/no-unused-vars + (callback: VerifierCallback): void + // eslint-disable-next-line @typescript-eslint/no-unused-vars + (options: FastifyJwtVerifyOptions, callback: VerifierCallback): void + (options?: Partial): Promise + // eslint-disable-next-line @typescript-eslint/no-unused-vars + (options: Partial, callback: VerifierCallback): void + } + + export interface JwtDecodeFunction { + (options?: FastifyJwtDecodeOptions): Promise + (callback: DecodeCallback): void + (options: FastifyJwtDecodeOptions, callback: DecodeCallback): void + } + export type FastifyJwtNamespace + JwtDecodeFunction> & Record + JwtSignFunction> & Record + JwtVerifyFunction> /** * for declaration merging diff --git a/types/index.tst.ts b/types/index.tst.ts index 292edfc..3cb032a 100644 --- a/types/index.tst.ts +++ b/types/index.tst.ts @@ -1,5 +1,14 @@ import fastify from 'fastify' -import fastifyJwt, { FastifyJWTOptions, FastifyJwtNamespace, JWT, SignOptions, VerifyOptions } from '..' +import fastifyJwt, { + FastifyJWTOptions, + FastifyJwtNamespace, + JwtDecodeFunction, + JwtSignFunction, + JwtVerifyFunction, + JWT, + SignOptions, + VerifyOptions +} from '..' import { expect } from 'tstyche' import fastifyRateLimit from '@fastify/rate-limit' @@ -148,9 +157,9 @@ app.post('/signup', { // } // } -expect['securityJwtDecode']>().type.toBe() -expect['securityJwtSign']>().type.toBe() -expect['securityJwtVerify']>().type.toBe() +expect['securityJwtDecode']>().type.toBe() +expect['securityJwtSign']>().type.toBe() +expect['securityJwtVerify']>().type.toBe() declare module 'fastify' { interface FastifyInstance extends FastifyJwtNamespace<{ namespace: 'tsdTest' }> { @@ -159,43 +168,85 @@ declare module 'fastify' { expect< FastifyJwtNamespace<{ namespace: 'security', jwtDecode: 'decode' }>['decode'] ->().type.toBe() +>().type.toBe() expect< FastifyJwtNamespace<{ namespace: 'security', jwtDecode: 'decode' }>['securityJwtSign'] ->().type.toBe() +>().type.toBe() expect< FastifyJwtNamespace<{ namespace: 'security', jwtDecode: 'decode' }>['securityJwtVerify'] ->().type.toBe() +>().type.toBe() expect< FastifyJwtNamespace<{ namespace: 'security', jwtSign: 'decode' }>['securityJwtDecode'] ->().type.toBe() +>().type.toBe() expect< FastifyJwtNamespace<{ namespace: 'security', jwtSign: 'sign' }>['sign'] ->().type.toBe() +>().type.toBe() expect< FastifyJwtNamespace<{ namespace: 'security', jwtSign: 'decode' }>['securityJwtVerify'] ->().type.toBe() +>().type.toBe() expect< FastifyJwtNamespace<{ namespace: 'security', jwtVerify: 'verify' }>['securityJwtDecode'] ->().type.toBe() +>().type.toBe() expect< FastifyJwtNamespace<{ namespace: 'security', jwtVerify: 'verify' }>['securityJwtSign'] ->().type.toBe() +>().type.toBe() expect< FastifyJwtNamespace<{ namespace: 'security', jwtVerify: 'verify' }>['verify'] ->().type.toBe() +>().type.toBe() expect< FastifyJwtNamespace<{ jwtDecode: 'decode' }>['decode'] ->().type.toBe() +>().type.toBe() expect< FastifyJwtNamespace<{ jwtSign: 'sign' }>['sign'] ->().type.toBe() +>().type.toBe() expect< FastifyJwtNamespace<{ jwtVerify: 'verify' }>['verify'] ->().type.toBe() +>().type.toBe() + +// Verify that JWT instance methods are still distinct from request/reply +// decorator methods — the JWT instance methods take a token argument and +// are synchronous, while the request/reply decorators infer the token from +// the request and are asynchronous. +expect().type.not.toBe() +expect().type.not.toBe() +expect().type.not.toBe() + +// Issue #348 (https://github.com/fastify/fastify-jwt/issues/348): namespaced sign/verify/decode decorators on the request and +// reply objects should be callable just like the default `jwtSign`, +// `jwtVerify` and `jwtDecode` decorators. +declare module 'fastify' { + interface FastifyInstance extends FastifyJwtNamespace<{ + namespace: 'accessToken', + jwtDecode: 'accessTokenDecode', + jwtSign: 'accessTokenSign', + jwtVerify: 'accessTokenVerify' + }> {} + + interface FastifyReply { + accessTokenSign: JwtSignFunction + } + + interface FastifyRequest { + accessTokenVerify: JwtVerifyFunction + accessTokenDecode: JwtDecodeFunction + } +} + +app.addHook('preHandler', async (request, reply) => { + // Namespaced sign on reply should accept the same overloads as jwtSign. + expect(await reply.accessTokenSign({ user: 'userName' })).type.toBe() + expect(await reply.accessTokenSign({ user: 'userName' }, { expiresIn: '1h' })).type.toBe() + + // Namespaced verify/decode on request should resolve to the decoded payload + // without requiring a token argument. + expect(await request.accessTokenVerify()).type.toBe() + expect(await request.accessTokenVerify<{ user: string }>()).type.toBe<{ user: string }>() + expect(await request.accessTokenDecode()).type.toBe() + expect(await request.accessTokenDecode<{ user: string }>()).type.toBe<{ user: string }>() +}) let signOptions: SignOptions = { key: 'supersecret',