Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 31 additions & 19 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,12 @@ declare module 'fastify' {
}

interface FastifyReply {
jwtSign(payload: fastifyJwt.SignPayloadType, options?: fastifyJwt.FastifyJwtSignOptions): Promise<string>
jwtSign(payload: fastifyJwt.SignPayloadType, callback: SignerCallback): void
jwtSign(payload: fastifyJwt.SignPayloadType, options: fastifyJwt.FastifyJwtSignOptions, callback: SignerCallback): void
jwtSign(payload: fastifyJwt.SignPayloadType, options?: Partial<fastifyJwt.SignOptions>): Promise<string>
jwtSign(payload: fastifyJwt.SignPayloadType, options: Partial<fastifyJwt.SignOptions>, callback: SignerCallback): void
jwtSign: fastifyJwt.JwtSignFunction
}

interface FastifyRequest {
jwtVerify<Decoded extends fastifyJwt.VerifyPayloadType>(options?: fastifyJwt.FastifyJwtVerifyOptions): Promise<Decoded>
// eslint-disable-next-line @typescript-eslint/no-unused-vars
jwtVerify<Decoded extends fastifyJwt.VerifyPayloadType>(callback: VerifierCallback): void
// eslint-disable-next-line @typescript-eslint/no-unused-vars
jwtVerify<Decoded extends fastifyJwt.VerifyPayloadType>(options: fastifyJwt.FastifyJwtVerifyOptions, callback: VerifierCallback): void
jwtVerify<Decoded extends fastifyJwt.VerifyPayloadType>(options?: Partial<fastifyJwt.VerifyOptions>): Promise<Decoded>
// eslint-disable-next-line @typescript-eslint/no-unused-vars
jwtVerify<Decoded extends fastifyJwt.VerifyPayloadType>(options: Partial<fastifyJwt.VerifyOptions>, callback: VerifierCallback): void
jwtDecode<Decoded extends fastifyJwt.DecodePayloadType>(options?: fastifyJwt.FastifyJwtDecodeOptions): Promise<Decoded>
jwtDecode<Decoded extends fastifyJwt.DecodePayloadType>(callback: fastifyJwt.DecodeCallback<Decoded>): void
jwtDecode<Decoded extends fastifyJwt.DecodePayloadType>(options: fastifyJwt.FastifyJwtDecodeOptions, callback: fastifyJwt.DecodeCallback<Decoded>): void
jwtVerify: fastifyJwt.JwtVerifyFunction
jwtDecode: fastifyJwt.JwtDecodeFunction
user: fastifyJwt.UserType
}
}
Expand All @@ -45,6 +32,31 @@ type FastifyJwt = FastifyPluginCallback<fastifyJwt.FastifyJWTOptions>

declare namespace fastifyJwt {

export interface JwtSignFunction {
(payload: SignPayloadType, options?: FastifyJwtSignOptions): Promise<string>
(payload: SignPayloadType, callback: SignerCallback): void
(payload: SignPayloadType, options: FastifyJwtSignOptions, callback: SignerCallback): void
(payload: SignPayloadType, options?: Partial<SignOptions>): Promise<string>
(payload: SignPayloadType, options: Partial<SignOptions>, callback: SignerCallback): void
}

export interface JwtVerifyFunction {
<Decoded extends VerifyPayloadType>(options?: FastifyJwtVerifyOptions): Promise<Decoded>
// eslint-disable-next-line @typescript-eslint/no-unused-vars
<Decoded extends VerifyPayloadType>(callback: VerifierCallback): void
// eslint-disable-next-line @typescript-eslint/no-unused-vars
<Decoded extends VerifyPayloadType>(options: FastifyJwtVerifyOptions, callback: VerifierCallback): void
<Decoded extends VerifyPayloadType>(options?: Partial<VerifyOptions>): Promise<Decoded>
// eslint-disable-next-line @typescript-eslint/no-unused-vars
<Decoded extends VerifyPayloadType>(options: Partial<VerifyOptions>, callback: VerifierCallback): void
}

export interface JwtDecodeFunction {
<Decoded extends DecodePayloadType>(options?: FastifyJwtDecodeOptions): Promise<Decoded>
<Decoded extends DecodePayloadType>(callback: DecodeCallback<Decoded>): void
<Decoded extends DecodePayloadType>(options: FastifyJwtDecodeOptions, callback: DecodeCallback<Decoded>): void
}

export type FastifyJwtNamespace<C extends {
namespace?: string;
jwtDecode?: string;
Expand All @@ -56,21 +68,21 @@ declare namespace fastifyJwt {
: C extends { namespace: string }
? `${C['namespace']}JwtDecode`
: never,
JWT['decode']>
JwtDecodeFunction>
&
Record<C extends { jwtSign: string }
? C['jwtSign']
: C extends { namespace: string }
? `${C['namespace']}JwtSign`
: never,
JWT['sign']>
JwtSignFunction>
&
Record<C extends { jwtVerify: string }
? C['jwtVerify']
: C extends { namespace: string }
? `${C['namespace']}JwtVerify`
: never,
JWT['verify']>
JwtVerifyFunction>

/**
* for declaration merging
Expand Down
83 changes: 67 additions & 16 deletions types/index.tst.ts
Original file line number Diff line number Diff line change
@@ -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'

Expand Down Expand Up @@ -148,9 +157,9 @@ app.post('/signup', {
// }
// }

expect<FastifyJwtNamespace<{ namespace: 'security' }>['securityJwtDecode']>().type.toBe<JWT['decode']>()
expect<FastifyJwtNamespace<{ namespace: 'security' }>['securityJwtSign']>().type.toBe<JWT['sign']>()
expect<FastifyJwtNamespace<{ namespace: 'security' }>['securityJwtVerify']>().type.toBe<JWT['verify']>()
expect<FastifyJwtNamespace<{ namespace: 'security' }>['securityJwtDecode']>().type.toBe<JwtDecodeFunction>()
expect<FastifyJwtNamespace<{ namespace: 'security' }>['securityJwtSign']>().type.toBe<JwtSignFunction>()
expect<FastifyJwtNamespace<{ namespace: 'security' }>['securityJwtVerify']>().type.toBe<JwtVerifyFunction>()

declare module 'fastify' {
interface FastifyInstance extends FastifyJwtNamespace<{ namespace: 'tsdTest' }> {
Expand All @@ -159,43 +168,85 @@ declare module 'fastify' {

expect<
FastifyJwtNamespace<{ namespace: 'security', jwtDecode: 'decode' }>['decode']
>().type.toBe<JWT['decode']>()
>().type.toBe<JwtDecodeFunction>()
expect<
FastifyJwtNamespace<{ namespace: 'security', jwtDecode: 'decode' }>['securityJwtSign']
>().type.toBe<JWT['sign']>()
>().type.toBe<JwtSignFunction>()
expect<
FastifyJwtNamespace<{ namespace: 'security', jwtDecode: 'decode' }>['securityJwtVerify']
>().type.toBe<JWT['verify']>()
>().type.toBe<JwtVerifyFunction>()

expect<
FastifyJwtNamespace<{ namespace: 'security', jwtSign: 'decode' }>['securityJwtDecode']
>().type.toBe<JWT['decode']>()
>().type.toBe<JwtDecodeFunction>()
expect<
FastifyJwtNamespace<{ namespace: 'security', jwtSign: 'sign' }>['sign']
>().type.toBe<JWT['sign']>()
>().type.toBe<JwtSignFunction>()
expect<
FastifyJwtNamespace<{ namespace: 'security', jwtSign: 'decode' }>['securityJwtVerify']
>().type.toBe<JWT['verify']>()
>().type.toBe<JwtVerifyFunction>()

expect<
FastifyJwtNamespace<{ namespace: 'security', jwtVerify: 'verify' }>['securityJwtDecode']
>().type.toBe<JWT['decode']>()
>().type.toBe<JwtDecodeFunction>()
expect<
FastifyJwtNamespace<{ namespace: 'security', jwtVerify: 'verify' }>['securityJwtSign']
>().type.toBe<JWT['sign']>()
>().type.toBe<JwtSignFunction>()
expect<
FastifyJwtNamespace<{ namespace: 'security', jwtVerify: 'verify' }>['verify']
>().type.toBe<JWT['verify']>()
>().type.toBe<JwtVerifyFunction>()

expect<
FastifyJwtNamespace<{ jwtDecode: 'decode' }>['decode']
>().type.toBe<JWT['decode']>()
>().type.toBe<JwtDecodeFunction>()
expect<
FastifyJwtNamespace<{ jwtSign: 'sign' }>['sign']
>().type.toBe<JWT['sign']>()
>().type.toBe<JwtSignFunction>()
expect<
FastifyJwtNamespace<{ jwtVerify: 'verify' }>['verify']
>().type.toBe<JWT['verify']>()
>().type.toBe<JwtVerifyFunction>()

// 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<JwtSignFunction>().type.not.toBe<JWT['sign']>()
expect<JwtVerifyFunction>().type.not.toBe<JWT['verify']>()
expect<JwtDecodeFunction>().type.not.toBe<JWT['decode']>()

// 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<string>()
expect(await reply.accessTokenSign({ user: 'userName' }, { expiresIn: '1h' })).type.toBe<string>()

// Namespaced verify/decode on request should resolve to the decoded payload
// without requiring a token argument.
expect(await request.accessTokenVerify()).type.toBe<object | string>()
expect(await request.accessTokenVerify<{ user: string }>()).type.toBe<{ user: string }>()
expect(await request.accessTokenDecode()).type.toBe<object | string>()
expect(await request.accessTokenDecode<{ user: string }>()).type.toBe<{ user: string }>()
})

let signOptions: SignOptions = {
key: 'supersecret',
Expand Down
Loading