From 61bd2a0adb638c1c2469459d78556a99cec697c7 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Wed, 18 Oct 2023 13:02:44 +0200 Subject: [PATCH] feat(types): add optional Generics for JWT verify and decrypt BREAKING CHANGE: jwtVerify and jwtDecrypt type argument for the resolved KeyLike type is now a second optional type argument following a type for the JWT Claims Set (aka payload) Resolves #568 --- src/jwt/decrypt.ts | 9 ++++---- src/jwt/unsecured.ts | 15 ++++++++---- src/jwt/verify.ts | 9 ++++---- src/types.d.ts | 8 +++---- test/types/index.test-d.ts | 47 ++++++++++++++++++++++++++++++++++---- 5 files changed, 68 insertions(+), 20 deletions(-) diff --git a/src/jwt/decrypt.ts b/src/jwt/decrypt.ts index 76339f7329..f63f1842af 100644 --- a/src/jwt/decrypt.ts +++ b/src/jwt/decrypt.ts @@ -1,5 +1,6 @@ import { compactDecrypt } from '../jwe/compact/decrypt.js' import type { + JWTPayload, KeyLike, DecryptOptions, JWTClaimVerificationOptions, @@ -47,22 +48,22 @@ export interface JWTDecryptGetKey * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWT Decryption and JWT Claims Set validation options. */ -export async function jwtDecrypt( +export async function jwtDecrypt( jwt: string | Uint8Array, key: KeyLike | Uint8Array, options?: JWTDecryptOptions, -): Promise +): Promise> /** * @param jwt JSON Web Token value (encoded as JWE). * @param getKey Function resolving Private Key or Secret to decrypt and verify the JWT with. See * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWT Decryption and JWT Claims Set validation options. */ -export async function jwtDecrypt( +export async function jwtDecrypt( jwt: string | Uint8Array, getKey: JWTDecryptGetKey, options?: JWTDecryptOptions, -): Promise> +): Promise & ResolvedKey> export async function jwtDecrypt( jwt: string | Uint8Array, key: KeyLike | Uint8Array | JWTDecryptGetKey, diff --git a/src/jwt/unsecured.ts b/src/jwt/unsecured.ts index 1381b49093..9cdd9cd0ba 100644 --- a/src/jwt/unsecured.ts +++ b/src/jwt/unsecured.ts @@ -6,8 +6,8 @@ import { JWTInvalid } from '../util/errors.js' import jwtPayload from '../lib/jwt_claims_set.js' import { ProduceJWT } from './produce.js' -export interface UnsecuredResult { - payload: JWTPayload +export interface UnsecuredResult { + payload: PayloadType & JWTPayload header: JWSHeaderParameters } @@ -53,7 +53,10 @@ export class UnsecuredJWT extends ProduceJWT { * @param jwt Unsecured JWT to decode the payload of. * @param options JWT Claims Set validation options. */ - static decode(jwt: string, options?: JWTClaimVerificationOptions): UnsecuredResult { + static decode( + jwt: string, + options?: JWTClaimVerificationOptions, + ): UnsecuredResult { if (typeof jwt !== 'string') { throw new JWTInvalid('Unsecured JWT must be a string') } @@ -71,7 +74,11 @@ export class UnsecuredJWT extends ProduceJWT { throw new JWTInvalid('Invalid Unsecured JWT') } - const payload = jwtPayload(header, base64url.decode(encodedPayload), options) + const payload = jwtPayload( + header, + base64url.decode(encodedPayload), + options, + ) as UnsecuredResult['payload'] return { payload, header } } diff --git a/src/jwt/verify.ts b/src/jwt/verify.ts index 7c0f6da57c..2b5529fbf3 100644 --- a/src/jwt/verify.ts +++ b/src/jwt/verify.ts @@ -1,5 +1,6 @@ import { compactVerify } from '../jws/compact/verify.js' import type { + JWTPayload, KeyLike, VerifyOptions, JWTClaimVerificationOptions, @@ -98,11 +99,11 @@ export interface JWTVerifyGetKey extends GetKeyFunction( jwt: string | Uint8Array, key: KeyLike | Uint8Array, options?: JWTVerifyOptions, -): Promise +): Promise> /** * @example Usage with a public JSON Web Key Set hosted on a remote URL @@ -123,11 +124,11 @@ export async function jwtVerify( * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWT Decryption and JWT Claims Set validation options. */ -export async function jwtVerify( +export async function jwtVerify( jwt: string | Uint8Array, getKey: JWTVerifyGetKey, options?: JWTVerifyOptions, -): Promise> +): Promise & ResolvedKey> export async function jwtVerify( jwt: string | Uint8Array, diff --git a/src/types.d.ts b/src/types.d.ts index 28ae8197d2..ce9c4ae1de 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -590,17 +590,17 @@ export interface CompactVerifyResult { protectedHeader: CompactJWSHeaderParameters } -export interface JWTVerifyResult { +export interface JWTVerifyResult { /** JWT Claims Set. */ - payload: JWTPayload + payload: PayloadType & JWTPayload /** JWS Protected Header. */ protectedHeader: JWTHeaderParameters } -export interface JWTDecryptResult { +export interface JWTDecryptResult { /** JWT Claims Set. */ - payload: JWTPayload + payload: PayloadType & JWTPayload /** JWE Protected Header. */ protectedHeader: CompactJWEHeaderParameters diff --git a/test/types/index.test-d.ts b/test/types/index.test-d.ts index a2c8741780..92dba9ae6d 100644 --- a/test/types/index.test-d.ts +++ b/test/types/index.test-d.ts @@ -50,12 +50,12 @@ expectType(await lib.importJWK({})) } { - const result = await lib.jwtVerify('', lib.createLocalJWKSet({ keys: [] })) + const result = await lib.jwtVerify<{}, CryptoKey>('', lib.createLocalJWKSet({ keys: [] })) expectType(result.key) } { - const result = await lib.jwtVerify('', lib.createLocalJWKSet({ keys: [] })) + const result = await lib.jwtVerify<{}, KeyObject>('', lib.createLocalJWKSet({ keys: [] })) expectType(result.key) } @@ -128,12 +128,12 @@ expectType(await lib.importJWK({})) } { - const result = await lib.jwtDecrypt('', () => {}) + const result = await lib.jwtDecrypt<{}, CryptoKey>('', () => {}) expectType(result.key) } { - const result = await lib.jwtDecrypt('', () => {}) + const result = await lib.jwtDecrypt<{}, KeyObject>('', () => {}) expectType(result.key) } @@ -208,3 +208,42 @@ expectType(await lib.createRemoteJWKSet(new URL(''))()) expectType(await lib.EmbeddedJWK()) expectType(await lib.EmbeddedJWK()) expectType(await lib.EmbeddedJWK()) + +{ + const result = await lib.jwtVerify<{ foo: 'string' }>('', new Uint8Array()) + expectType(result.payload.iss) + expectType(result.payload.unknown) + expectType<'string'>(result.payload.foo) +} + +{ + const result = await lib.jwtDecrypt<{ foo: 'string' }>('', new Uint8Array()) + expectType(result.payload.iss) + expectType(result.payload.unknown) + expectType<'string'>(result.payload.foo) +} + +{ + const result = lib.UnsecuredJWT.decode<{ foo: 'string' }>('') + expectType(result.payload.iss) + expectType(result.payload.unknown) + expectType<'string'>(result.payload.foo) +} + +{ + const result = await lib.jwtVerify('', new Uint8Array()) + expectType(result.payload.iss) + expectType(result.payload.unknown) +} + +{ + const result = await lib.jwtDecrypt('', new Uint8Array()) + expectType(result.payload.iss) + expectType(result.payload.unknown) +} + +{ + const result = lib.UnsecuredJWT.decode('') + expectType(result.payload.iss) + expectType(result.payload.unknown) +}