Skip to content

Commit 05f5229

Browse files
committed
Bump Jose to ^6
1 parent 5607d36 commit 05f5229

File tree

7 files changed

+104
-90
lines changed

7 files changed

+104
-90
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,12 @@ axios
9595

9696
### Supported JWK formats
9797

98-
| Algorithm | kty | alg |
99-
| ----------------- | --- | --------------------------- |
100-
| RSASSA-PKCS1-v1_5 | RSA | RS256, RS384, RS512 |
101-
| RSASSA-PSS | RSA | PS256, PS384, PS512 |
102-
| ECDSA | EC | ES256, ES256K, ES384, ES512 |
103-
| Edwards-curve DSA | OKP | EdDSA (Ed25519 / Ed448) |
98+
| Algorithm | kty | alg |
99+
| ----------------- | --- | ------------------- |
100+
| RSASSA-PKCS1-v1_5 | RSA | RS256, RS384, RS512 |
101+
| RSASSA-PSS | RSA | PS256, PS384, PS512 |
102+
| ECDSA | EC | ES256, ES384, ES512 |
103+
| EdDSA | OKP | Ed25519 |
104104

105105
### Customization hooks
106106

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"cors": "^2.8.5",
6262
"express": "^5.1.0",
6363
"is-plain-obj": "^4.1.0",
64-
"jose": "^5.10.0"
64+
"jose": "^6.0.11"
6565
},
6666
"devDependencies": {
6767
"@eslint/js": "^9.26.0",

src/lib/helpers.ts

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { webcrypto as crypto } from 'node:crypto';
2222

2323
import isPlainObject from 'is-plain-obj';
2424

25-
import type { CodeChallenge, PKCEAlgorithm, TokenRequest } from './types';
25+
import type { CodeChallenge, JWK, PKCEAlgorithm, TokenRequest } from './types';
2626

2727
export const defaultTokenTtl = 3600;
2828

@@ -162,3 +162,66 @@ export const createPKCECodeChallenge = async (
162162
}
163163
return challenge;
164164
};
165+
166+
type JwkTransformer = (jwk: JWK) => JWK;
167+
168+
const RsaPrivateFieldsRemover: JwkTransformer = (jwk) => {
169+
const x = { ...jwk };
170+
171+
delete x.d;
172+
delete x.p;
173+
delete x.q;
174+
delete x.dp;
175+
delete x.dq;
176+
delete x.qi;
177+
178+
return x;
179+
};
180+
181+
const EcdsaPrivateFieldsRemover: JwkTransformer = (jwk) => {
182+
const x = { ...jwk };
183+
184+
delete x.d;
185+
186+
return x;
187+
};
188+
189+
const EddsaPrivateFieldsRemover: JwkTransformer = (jwk) => {
190+
const x = { ...jwk };
191+
192+
delete x.d;
193+
194+
return x;
195+
};
196+
197+
const privateToPublicTransformerMap: Record<string, JwkTransformer> = {
198+
// RSASSA-PKCS1-v1_5
199+
RS256: RsaPrivateFieldsRemover,
200+
RS384: RsaPrivateFieldsRemover,
201+
RS512: RsaPrivateFieldsRemover,
202+
203+
// RSASSA-PSS
204+
PS256: RsaPrivateFieldsRemover,
205+
PS384: RsaPrivateFieldsRemover,
206+
PS512: RsaPrivateFieldsRemover,
207+
208+
// ECDSA
209+
ES256: EcdsaPrivateFieldsRemover,
210+
ES384: EcdsaPrivateFieldsRemover,
211+
ES512: EcdsaPrivateFieldsRemover,
212+
213+
// Edwards-curve DSA
214+
EdDSA: EddsaPrivateFieldsRemover,
215+
};
216+
217+
export const supportedAlgs = Object.keys(privateToPublicTransformerMap);
218+
219+
export const privateToPublicKeyTransformer = (privateKey: JWK): JWK => {
220+
const transformer = privateToPublicTransformerMap[privateKey.alg];
221+
222+
if (transformer === undefined) {
223+
throw new Error(`Unsupported algo '${privateKey.alg}'`);
224+
}
225+
226+
return transformer(privateKey);
227+
};

src/lib/jwk-store.ts

Lines changed: 21 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -18,74 +18,24 @@
1818
* @module lib/jwk-store
1919
*/
2020

21-
import { KeyObject, randomBytes } from 'node:crypto';
21+
import { randomBytes } from 'node:crypto';
2222
import { AssertionError } from 'node:assert';
2323

2424
import type { GenerateKeyPairOptions } from 'jose';
2525
import { exportJWK, importJWK, generateKeyPair } from 'jose';
2626

2727
import type { JWK } from './types';
2828
import type { JWKWithKid } from './types-internals';
29-
import { assertIsPlainObject } from './helpers';
29+
import {
30+
assertIsPlainObject,
31+
privateToPublicKeyTransformer,
32+
supportedAlgs,
33+
} from './helpers';
3034

3135
const generateRandomKid = () => {
3236
return randomBytes(40).toString('hex');
3337
};
3438

35-
type JwkTransformer = (jwk: JWK) => JWK;
36-
37-
const RsaPrivateFieldsRemover: JwkTransformer = (jwk) => {
38-
const x = { ...jwk };
39-
40-
delete x.d;
41-
delete x.p;
42-
delete x.q;
43-
delete x.dp;
44-
delete x.dq;
45-
delete x.qi;
46-
47-
return x;
48-
};
49-
50-
const EcdsaPrivateFieldsRemover: JwkTransformer = (jwk) => {
51-
const x = { ...jwk };
52-
53-
delete x.d;
54-
55-
return x;
56-
};
57-
58-
const EddsaPrivateFieldsRemover: JwkTransformer = (jwk) => {
59-
const x = { ...jwk };
60-
61-
delete x.d;
62-
63-
return x;
64-
};
65-
66-
const privateToPublicTransformerMap: Record<string, JwkTransformer> = {
67-
// RSASSA-PKCS1-v1_5
68-
RS256: RsaPrivateFieldsRemover,
69-
RS384: RsaPrivateFieldsRemover,
70-
RS512: RsaPrivateFieldsRemover,
71-
72-
// RSASSA-PSS
73-
PS256: RsaPrivateFieldsRemover,
74-
PS384: RsaPrivateFieldsRemover,
75-
PS512: RsaPrivateFieldsRemover,
76-
77-
// ECDSA
78-
ES256: EcdsaPrivateFieldsRemover,
79-
ES256K: EcdsaPrivateFieldsRemover,
80-
ES384: EcdsaPrivateFieldsRemover,
81-
ES512: EcdsaPrivateFieldsRemover,
82-
83-
// Edwards-curve DSA
84-
EdDSA: EddsaPrivateFieldsRemover,
85-
};
86-
87-
const supportedAlgs = Object.keys(privateToPublicTransformerMap);
88-
8939
function normalizeKeyKid(
9040
jwk: unknown,
9141
opts?: { kid?: string },
@@ -131,9 +81,20 @@ export class JWKStore {
13181
const generateOpts: GenerateKeyPairOptions =
13282
opts?.crv !== undefined ? { crv: opts.crv } : {};
13383

84+
generateOpts.extractable = true;
85+
86+
if (
87+
alg === 'EdDSA' &&
88+
generateOpts.crv !== undefined &&
89+
generateOpts.crv !== 'Ed25519'
90+
) {
91+
throw new Error(
92+
'Invalid or unsupported crv option provided, supported values are: Ed25519',
93+
);
94+
}
95+
13496
const pair = await generateKeyPair(alg, generateOpts);
13597
const joseJwk = await exportJWK(pair.privateKey);
136-
13798
normalizeKeyKid(joseJwk, opts);
13899
joseJwk.alg = alg;
139100

@@ -162,9 +123,9 @@ export class JWKStore {
162123

163124
const jwk = tempJwk as JWK;
164125

165-
const privateKey = await importJWK(jwk);
126+
const privateKey = await importJWK(jwk, jwk.alg, { extractable: false });
166127

167-
if (!(privateKey instanceof KeyObject) || privateKey.type !== 'private') {
128+
if (privateKey instanceof Uint8Array || privateKey.type !== 'private') {
168129
throw new Error(
169130
`Invalid JWK type. No "private" key related data has been found.`,
170131
);
@@ -229,13 +190,7 @@ class KeyRotator {
229190
continue;
230191
}
231192

232-
const cleaner = privateToPublicTransformerMap[key.alg];
233-
234-
if (cleaner === undefined) {
235-
throw new Error(`Unsupported algo '{key.alg}'`);
236-
}
237-
238-
keys.push(cleaner(key));
193+
keys.push(privateToPublicKeyTransformer(key));
239194
}
240195

241196
return keys;

test/jwk-store.test.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { describe, it, expect } from 'vitest';
22

33
import { JWKStore } from '../src/lib/jwk-store';
4+
import type { JWK } from '../src/lib/types';
5+
import { privateToPublicKeyTransformer } from '../src/lib/helpers';
46

57
import * as testKeys from './keys';
68

@@ -14,7 +16,6 @@ describe('JWK Store', () => {
1416
["RSASSA-PSS", "PS384", "RSA"],
1517
["RSASSA-PSS", "PS512", "RSA"],
1618
["ECDSA", "ES256", "EC"],
17-
["ECDSA", "ES256K", "EC"],
1819
["ECDSA", "ES384", "EC"],
1920
["ECDSA", "ES512", "EC"],
2021
])('should be able to generate a new %s based key (alg = %s)', async (_kind: string, alg: string, expectedKty: string) => {
@@ -29,7 +30,6 @@ describe('JWK Store', () => {
2930

3031
it.each([
3132
"Ed25519",
32-
"Ed448",
3333
])('should be able to generate a new EdDSA based key (crv = %s)', async (crv: string) => {
3434
const store = new JWKStore();
3535
const key = await store.generate('EdDSA', { crv });
@@ -56,7 +56,7 @@ describe('JWK Store', () => {
5656
])('throws on unsupported crv for EdDSA alg (crv = %s)', async (crv: string) => {
5757
const store = new JWKStore();
5858

59-
await expect(() => store.generate('EdDSA', { crv })).rejects.toThrow("Invalid or unsupported crv option provided, supported values are Ed25519 and Ed448");
59+
await expect(() => store.generate('EdDSA', { crv })).rejects.toThrow("Invalid or unsupported crv option provided, supported values are: Ed25519");
6060
});
6161

6262
it.each([
@@ -115,14 +115,9 @@ describe('JWK Store', () => {
115115

116116
const testKey = testKeys.getParsed('test-rs256-key.json');
117117

118-
delete testKey['d'];
119-
delete testKey['p'];
120-
delete testKey['q'];
121-
delete testKey['dp'];
122-
delete testKey['dq'];
123-
delete testKey['qi'];
118+
const publicKey = privateToPublicKeyTransformer(testKey as JWK);
124119

125-
await expect(() => store.add(testKey)).rejects.toThrow('Invalid JWK type. No "private" key related data has been found.');
120+
await expect(() => store.add(publicKey)).rejects.toThrow('Invalid JWK type. No "private" key related data has been found.');
126121
});
127122

128123
it('throws when serialized EC key is public"', async () => {

test/lib/test_helpers.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {JWTVerifyResult } from "jose";
44
import { importJWK, jwtVerify } from "jose";
55

66
import type { OAuth2Issuer } from "../../src/lib/oauth2-issuer";
7+
import { privateToPublicKeyTransformer } from "../../src/lib/helpers";
78

89
export const verifyTokenWithKey = async (issuer: OAuth2Issuer, token: string, kid: string): Promise<JWTVerifyResult> => {
910
const key = issuer.keys.get(kid);
@@ -12,8 +13,8 @@ export const verifyTokenWithKey = async (issuer: OAuth2Issuer, token: string, ki
1213
throw new AssertionError({ message: 'Key is undefined' });
1314
}
1415

15-
const privateKey = await importJWK(key);
16+
const publicKey = await importJWK(privateToPublicKeyTransformer(key));
1617

17-
const verified = await jwtVerify(token, privateKey);
18+
const verified = await jwtVerify(token, publicKey);
1819
return verified;
1920
};

0 commit comments

Comments
 (0)