fastify-jwt
fastify-jwt copied to clipboard
Type augmentation for namespace methods?
Prerequisites
- [X] I have written a descriptive issue title
- [X] I have searched existing issues to ensure the issue has not already been raised
Issue
EDIT: Simplifying this question a little.
We've created the following namespaced jwt plugin based on the docs here... https://github.com/fastify/fastify-jwt?tab=readme-ov-file#namespace
import fp from 'fastify-plugin'
import { FastifyPluginAsync, FastifyInstance } from 'fastify'
import fastifyJwt, { FastifyJWTOptions, JWT } from '@fastify/jwt'
// https://fastify.dev/docs/latest/Reference/TypeScript/#creating-a-typescript-fastify-plugin
declare module 'fastify' {
interface FastifyInstance {
jwt: {
accessToken: JWT
refreshToken: JWT
}
}
interface FastifyReply {
accessTokenSign: JWT['sign']
refreshTokenSign: JWT['sign']
}
interface FastifyRequest {
accessTokenVerify: JWT['verify']
accessTokenDecode: JWT['decode']
refreshTokenVerify: JWT['verify']
refreshTokenDecode: JWT['decode']
}
}
const jwtPlugin: FastifyPluginAsync<FastifyJWTOptions> = async (app: FastifyInstance, options) => {
app.register<FastifyJWTOptions>(fastifyJwt, {
// secret: Buffer.from(app.config.jwt.secret as string, 'base64'),
secret: {
private: app.config.jwt.access.secret.private,
public: app.config.jwt.access.secret.public,
},
sign: {
algorithm: 'RS256',
iss: app.config.jwt.access.issuer,
aud: app.config.jwt.access.audience,
expiresIn: app.config.jwt.access.expiresIn,
},
verify: {
algorithms: ['RS256'],
allowedIss: app.config.jwt.access.issuer,
},
namespace: 'accessToken',
jwtDecode: 'accessTokenDecode',
jwtSign: 'accessTokenSign',
jwtVerify: 'accessTokenVerify',
})
app.register<FastifyJWTOptions>(fastifyJwt, {
// secret: Buffer.from(app.config.jwt.secret as string, 'base64'),
secret: {
private: app.config.jwt.refresh.secret.private,
public: app.config.jwt.refresh.secret.public,
},
sign: {
algorithm: 'RS256',
iss: app.config.jwt.refresh.issuer,
aud: app.config.jwt.refresh.audience,
expiresIn: app.config.jwt.refresh.expiresIn,
},
verify: {
algorithms: ['RS256'],
allowedIss: app.config.jwt.refresh.issuer,
},
namespace: 'refreshToken',
jwtDecode: 'refreshTokenDecode',
jwtSign: 'refreshTokenSign',
jwtVerify: 'refreshTokenVerify',
})
}
export default fp(jwtPlugin)
With the above, when calling either of the namespaced sign methods, our augmented type definition above, and the signature on the actual method don't exactly match - which means we suspect, that
accessTokenSign: JWT['sign'] is not the correct way to assign a type to the accessTokenSign and other methods. - since the reply and request augmented methods are asynchronous - i.e. require either a callback, or return a promise.
accessTokenVerify: JWT['verify'] is also not the correct type annotation, as the request.accessTokenVerify method does not need a token as a parameter (it looks up the token from the request header).
And so our question really is - what's the correct way to augment the FastifyReply and FastifyRequest types with correct sign, verify and decode methods when using namespaces?
You are requesting to expose the below types. https://github.com/fastify/fastify-jwt/blob/db719f020e79db9f262c639e34705afefcf1416b/types/jwt.d.ts#L20-L38
@climba03003 - yes I think you're right. I've also updated the above to include our augmented FastifyInstance .
Would the exported function types look like this? (courtesy ChatGPT). If so I'd be glad to submit a PR
EDIT: Okay we're actually working on this, and the types are more likely to be something like:
export interface JwtSignFunction {
(
payload: SignPayloadType,
options?: FastifyJwtSignOptions | Partial<SignOptions>
): Promise<string>
(payload: SignPayloadType, callback: SignerCallback): void
(
payload: SignPayloadType,
options: FastifyJwtSignOptions | Partial<SignOptions>,
callback: SignerCallback
): void
}
export interface JwtVerifyFunction {
<Decoded extends VerifyPayloadType>(options?: FastifyJwtVerifyOptions): Promise<Decoded>
<Decoded extends VerifyPayloadType>(callback: VerifierCallback): void
<Decoded extends VerifyPayloadType>(
options: FastifyJwtVerifyOptions,
callback: VerifierCallback
): void
<Decoded extends VerifyPayloadType>(options?: Partial<VerifyOptions>): Promise<Decoded>
<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
}
And then implemented in our augmented types as :
declare module 'fastify' {
interface FastifyInstance {
jwt: {
accessToken: JWT
refreshToken: JWT
}
}
interface FastifyReply {
accessTokenSign: JwtSignFunction
refreshTokenSign: JwtSignFunction
}
interface FastifyRequest {
accessTokenVerify: JwtVerifyFunction
accessTokenDecode: JwtDecodeFunction
refreshTokenVerify: JwtVerifyFunction
refreshTokenDecode: JwtDecodeFunction
}
}
Again - if this is close, and I can help by submitting a PR then I'd be glad to.