@logto/connector-apple 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,7 +30,7 @@ You need to enroll [Apple Developer Program](https://developer.apple.com/program
30
30
 
31
31
  You can do it via Xcode -> Project settings -> Signing & Capabilities, or visit [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/identifiers/list/bundleId).
32
32
 
33
- ![Enable Sign in with Apple](/packages/connector-apple/docs/enable-sign-in-with-apple-in-xcode.png)
33
+ ![Enable Sign in with Apple](/packages/connectors/connector-apple/docs/enable-sign-in-with-apple-in-xcode.png)
34
34
 
35
35
  See the "Enable an App ID" section in [Apple official docs](https://developer.apple.com/documentation/sign_in_with_apple/configuring_your_environment_for_sign_in_with_apple) for more info.
36
36
 
@@ -45,13 +45,13 @@ See the "Enable an App ID" section in [Apple official docs](https://developer.ap
45
45
 
46
46
  Click the identifier you just created. Check "Sign in with Apple" on the details page and click "Configure".
47
47
 
48
- ![Enable Sign in with Apple](/packages/connector-apple/docs/enable-sign-in-with-apple.png)
48
+ ![Enable Sign in with Apple](/packages/connectors/connector-apple/docs/enable-sign-in-with-apple.png)
49
49
 
50
50
  In the opening modal, select the App ID you just enabled Sign in with Apple.
51
51
 
52
52
  Enter the domain of your Logto instance without protocol and port, e.g., `your.logto.domain`; then enter the "Return URL" (i.e., Redirect URI), which is the Logto URL with `/callback/${connector_id}`, e.g., `https://your.logto.domain/callback/apple-universal`. You can get the randomly generated `connector_id` after creating Apple connector in Admin Console.
53
53
 
54
- ![domain-and-url](/packages/connector-apple/docs/domain-and-url.png)
54
+ ![domain-and-url](/packages/connectors/connector-apple/docs/domain-and-url.png)
55
55
 
56
56
  Click "Next" then "Done" to close the modal. Click "Continue" on the top-right corner, then click "Save" to save your configuration.
57
57
 
package/lib/constant.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import type { ConnectorMetadata } from '@logto/connector-kit';
2
2
  export declare const issuer = "https://appleid.apple.com";
3
- export declare const authorizationEndpoint: string;
4
- export declare const accessTokenEndpoint: string;
5
- export declare const jwksUri: string;
3
+ export declare const authorizationEndpoint = "https://appleid.apple.com/auth/authorize";
4
+ export declare const accessTokenEndpoint = "https://appleid.apple.com/auth/token";
5
+ export declare const jwksUri = "https://appleid.apple.com/auth/keys";
6
6
  export declare const scope = "";
7
7
  export declare const defaultMetadata: ConnectorMetadata;
8
8
  export declare const defaultTimeout = 5000;
package/lib/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { ConnectorPlatform, ConnectorConfigFormItemType, ConnectorType, validateConfig, ConnectorError, ConnectorErrorCodes } from '@logto/connector-kit';
2
- import * as crypto from 'crypto';
3
- import { randomFillSync, KeyObject, createPublicKey, createPrivateKey, createSecretKey, constants } from 'crypto';
2
+ import * as crypto from 'node:crypto';
3
+ import crypto__default, { KeyObject, createPrivateKey, createPublicKey, constants, createSecretKey } from 'node:crypto';
4
4
  import { z } from 'zod';
5
- import { Buffer as Buffer$1 } from 'buffer';
6
- import * as util from 'util';
7
- import { promisify } from 'util';
8
- import * as http from 'http';
9
- import * as https from 'https';
10
- import { once } from 'events';
5
+ import * as http from 'node:http';
6
+ import * as https from 'node:https';
7
+ import { once } from 'node:events';
8
+ import { Buffer as Buffer$1 } from 'node:buffer';
9
+ import * as util from 'node:util';
10
+ import { promisify } from 'node:util';
11
11
 
12
12
  // https://github.com/facebook/jest/issues/7547
13
13
  const assert = (value, error) => {
@@ -20,22 +20,22 @@ const assert = (value, error) => {
20
20
 
21
21
  const POOL_SIZE_MULTIPLIER = 128;
22
22
  let pool, poolOffset;
23
- let fillPool = bytes => {
23
+ function fillPool(bytes) {
24
24
  if (!pool || pool.length < bytes) {
25
25
  pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER);
26
- randomFillSync(pool);
26
+ crypto__default.getRandomValues(pool);
27
27
  poolOffset = 0;
28
28
  } else if (poolOffset + bytes > pool.length) {
29
- randomFillSync(pool);
29
+ crypto__default.getRandomValues(pool);
30
30
  poolOffset = 0;
31
31
  }
32
32
  poolOffset += bytes;
33
- };
34
- let random = bytes => {
33
+ }
34
+ function random(bytes) {
35
35
  fillPool((bytes -= 0));
36
36
  return pool.subarray(poolOffset - bytes, poolOffset)
37
- };
38
- let customRandom = (alphabet, defaultSize, getRandom) => {
37
+ }
38
+ function customRandom(alphabet, defaultSize, getRandom) {
39
39
  let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1;
40
40
  let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length);
41
41
  return (size = defaultSize) => {
@@ -49,9 +49,10 @@ let customRandom = (alphabet, defaultSize, getRandom) => {
49
49
  }
50
50
  }
51
51
  }
52
- };
53
- let customAlphabet = (alphabet, size = 21) =>
54
- customRandom(alphabet, size, random);
52
+ }
53
+ function customAlphabet(alphabet, size = 21) {
54
+ return customRandom(alphabet, size, random)
55
+ }
55
56
 
56
57
  const lowercaseAlphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
57
58
  const alphabet = `${lowercaseAlphabet}ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
@@ -96,141 +97,109 @@ function normalize(input) {
96
97
  }
97
98
  return encoded;
98
99
  }
99
- if (Buffer$1.isEncoding('base64url')) ;
100
- const decode = (input) => Buffer$1.from(normalize(input), 'base64');
100
+ const decode = (input) => new Uint8Array(Buffer$1.from(normalize(input), 'base64'));
101
101
 
102
102
  class JOSEError extends Error {
103
+ static get code() {
104
+ return 'ERR_JOSE_GENERIC';
105
+ }
106
+ code = 'ERR_JOSE_GENERIC';
103
107
  constructor(message) {
104
- var _a;
105
108
  super(message);
106
- this.code = 'ERR_JOSE_GENERIC';
107
109
  this.name = this.constructor.name;
108
- (_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, this, this.constructor);
109
- }
110
- static get code() {
111
- return 'ERR_JOSE_GENERIC';
110
+ Error.captureStackTrace?.(this, this.constructor);
112
111
  }
113
112
  }
114
113
  class JWTClaimValidationFailed extends JOSEError {
114
+ static get code() {
115
+ return 'ERR_JWT_CLAIM_VALIDATION_FAILED';
116
+ }
117
+ code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';
118
+ claim;
119
+ reason;
115
120
  constructor(message, claim = 'unspecified', reason = 'unspecified') {
116
121
  super(message);
117
- this.code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';
118
122
  this.claim = claim;
119
123
  this.reason = reason;
120
124
  }
121
- static get code() {
122
- return 'ERR_JWT_CLAIM_VALIDATION_FAILED';
123
- }
124
125
  }
125
126
  class JWTExpired extends JOSEError {
127
+ static get code() {
128
+ return 'ERR_JWT_EXPIRED';
129
+ }
130
+ code = 'ERR_JWT_EXPIRED';
131
+ claim;
132
+ reason;
126
133
  constructor(message, claim = 'unspecified', reason = 'unspecified') {
127
134
  super(message);
128
- this.code = 'ERR_JWT_EXPIRED';
129
135
  this.claim = claim;
130
136
  this.reason = reason;
131
137
  }
132
- static get code() {
133
- return 'ERR_JWT_EXPIRED';
134
- }
135
138
  }
136
139
  class JOSEAlgNotAllowed extends JOSEError {
137
- constructor() {
138
- super(...arguments);
139
- this.code = 'ERR_JOSE_ALG_NOT_ALLOWED';
140
- }
141
140
  static get code() {
142
141
  return 'ERR_JOSE_ALG_NOT_ALLOWED';
143
142
  }
143
+ code = 'ERR_JOSE_ALG_NOT_ALLOWED';
144
144
  }
145
145
  class JOSENotSupported extends JOSEError {
146
- constructor() {
147
- super(...arguments);
148
- this.code = 'ERR_JOSE_NOT_SUPPORTED';
149
- }
150
146
  static get code() {
151
147
  return 'ERR_JOSE_NOT_SUPPORTED';
152
148
  }
149
+ code = 'ERR_JOSE_NOT_SUPPORTED';
153
150
  }
154
151
  class JWSInvalid extends JOSEError {
155
- constructor() {
156
- super(...arguments);
157
- this.code = 'ERR_JWS_INVALID';
158
- }
159
152
  static get code() {
160
153
  return 'ERR_JWS_INVALID';
161
154
  }
155
+ code = 'ERR_JWS_INVALID';
162
156
  }
163
157
  class JWTInvalid extends JOSEError {
164
- constructor() {
165
- super(...arguments);
166
- this.code = 'ERR_JWT_INVALID';
167
- }
168
158
  static get code() {
169
159
  return 'ERR_JWT_INVALID';
170
160
  }
161
+ code = 'ERR_JWT_INVALID';
171
162
  }
172
163
  class JWKSInvalid extends JOSEError {
173
- constructor() {
174
- super(...arguments);
175
- this.code = 'ERR_JWKS_INVALID';
176
- }
177
164
  static get code() {
178
165
  return 'ERR_JWKS_INVALID';
179
166
  }
167
+ code = 'ERR_JWKS_INVALID';
180
168
  }
181
169
  class JWKSNoMatchingKey extends JOSEError {
182
- constructor() {
183
- super(...arguments);
184
- this.code = 'ERR_JWKS_NO_MATCHING_KEY';
185
- this.message = 'no applicable key found in the JSON Web Key Set';
186
- }
187
170
  static get code() {
188
171
  return 'ERR_JWKS_NO_MATCHING_KEY';
189
172
  }
173
+ code = 'ERR_JWKS_NO_MATCHING_KEY';
174
+ message = 'no applicable key found in the JSON Web Key Set';
190
175
  }
191
176
  class JWKSMultipleMatchingKeys extends JOSEError {
192
- constructor() {
193
- super(...arguments);
194
- this.code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
195
- this.message = 'multiple matching keys found in the JSON Web Key Set';
196
- }
177
+ [Symbol.asyncIterator];
197
178
  static get code() {
198
179
  return 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
199
180
  }
181
+ code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
182
+ message = 'multiple matching keys found in the JSON Web Key Set';
200
183
  }
201
184
  class JWKSTimeout extends JOSEError {
202
- constructor() {
203
- super(...arguments);
204
- this.code = 'ERR_JWKS_TIMEOUT';
205
- this.message = 'request timed out';
206
- }
207
185
  static get code() {
208
186
  return 'ERR_JWKS_TIMEOUT';
209
187
  }
188
+ code = 'ERR_JWKS_TIMEOUT';
189
+ message = 'request timed out';
210
190
  }
211
191
  class JWSSignatureVerificationFailed extends JOSEError {
212
- constructor() {
213
- super(...arguments);
214
- this.code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';
215
- this.message = 'signature verification failed';
216
- }
217
192
  static get code() {
218
193
  return 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';
219
194
  }
195
+ code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';
196
+ message = 'signature verification failed';
220
197
  }
221
198
 
222
- var isKeyObject = util.types.isKeyObject
223
- ? (obj) => util.types.isKeyObject(obj)
224
- : (obj) => obj != null && obj instanceof KeyObject;
199
+ var isKeyObject = (obj) => util.types.isKeyObject(obj);
225
200
 
226
- const isCryptoKey = util.types.isCryptoKey
227
- ? (key) => util.types.isCryptoKey(key)
228
- :
229
- (key) => false;
230
-
231
- function isCloudflareWorkers() {
232
- return false;
233
- }
201
+ const webcrypto = crypto.webcrypto;
202
+ const isCryptoKey = (key) => util.types.isCryptoKey(key);
234
203
 
235
204
  function unusable(name, prop = 'algorithm.name') {
236
205
  return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
@@ -304,11 +273,6 @@ function checkSigCryptoKey(key, alg, ...usages) {
304
273
  throw unusable(`SHA-${expected}`, 'algorithm.hash');
305
274
  break;
306
275
  }
307
- case isCloudflareWorkers() : {
308
- if (!isAlgorithm(key.algorithm, 'NODE-ED25519'))
309
- throw unusable('NODE-ED25519');
310
- break;
311
- }
312
276
  case 'EdDSA': {
313
277
  if (key.algorithm.name !== 'Ed25519' && key.algorithm.name !== 'Ed448') {
314
278
  throw unusable('Ed25519 or Ed448');
@@ -365,7 +329,7 @@ function withAlg(alg, actual, ...types) {
365
329
 
366
330
  var isKeyLike = (key) => isKeyObject(key) || isCryptoKey(key);
367
331
  const types = ['KeyObject'];
368
- if (parseInt(process.versions.node) >= 16) {
332
+ if (globalThis.CryptoKey || webcrypto?.CryptoKey) {
369
333
  types.push('CryptoKey');
370
334
  }
371
335
 
@@ -408,11 +372,6 @@ function isObject(input) {
408
372
  return Object.getPrototypeOf(input) === proto;
409
373
  }
410
374
 
411
- const p256 = Buffer$1.from([42, 134, 72, 206, 61, 3, 1, 7]);
412
- const p384 = Buffer$1.from([43, 129, 4, 0, 34]);
413
- const p521 = Buffer$1.from([43, 129, 4, 0, 35]);
414
- const secp256k1 = Buffer$1.from([43, 129, 4, 0, 10]);
415
- const weakMap$1 = new WeakMap();
416
375
  const namedCurveToJOSE = (namedCurve) => {
417
376
  switch (namedCurve) {
418
377
  case 'prime256v1':
@@ -428,7 +387,6 @@ const namedCurveToJOSE = (namedCurve) => {
428
387
  }
429
388
  };
430
389
  const getNamedCurve = (kee, raw) => {
431
- var _a;
432
390
  let key;
433
391
  if (isCryptoKey(kee)) {
434
392
  key = KeyObject.from(kee);
@@ -450,315 +408,38 @@ const getNamedCurve = (kee, raw) => {
450
408
  case 'x448':
451
409
  return `X${key.asymmetricKeyType.slice(1)}`;
452
410
  case 'ec': {
453
- if (weakMap$1.has(key)) {
454
- return weakMap$1.get(key);
455
- }
456
- let namedCurve = (_a = key.asymmetricKeyDetails) === null || _a === void 0 ? void 0 : _a.namedCurve;
457
- if (!namedCurve && key.type === 'private') {
458
- namedCurve = getNamedCurve(createPublicKey(key), true);
459
- }
460
- else if (!namedCurve) {
461
- const buf = key.export({ format: 'der', type: 'spki' });
462
- const i = buf[1] < 128 ? 14 : 15;
463
- const len = buf[i];
464
- const curveOid = buf.slice(i + 1, i + 1 + len);
465
- if (curveOid.equals(p256)) {
466
- namedCurve = 'prime256v1';
467
- }
468
- else if (curveOid.equals(p384)) {
469
- namedCurve = 'secp384r1';
470
- }
471
- else if (curveOid.equals(p521)) {
472
- namedCurve = 'secp521r1';
473
- }
474
- else if (curveOid.equals(secp256k1)) {
475
- namedCurve = 'secp256k1';
476
- }
477
- else {
478
- throw new JOSENotSupported('Unsupported key curve for this operation');
479
- }
480
- }
481
- if (raw)
411
+ let namedCurve = key.asymmetricKeyDetails.namedCurve;
412
+ if (raw) {
482
413
  return namedCurve;
483
- const curve = namedCurveToJOSE(namedCurve);
484
- weakMap$1.set(key, curve);
485
- return curve;
414
+ }
415
+ return namedCurveToJOSE(namedCurve);
486
416
  }
487
417
  default:
488
418
  throw new TypeError('Invalid asymmetric key type for this operation');
489
419
  }
490
420
  };
491
- function setCurve(keyObject, curve) {
492
- weakMap$1.set(keyObject, curve);
493
- }
494
421
 
495
- const weakMap = new WeakMap();
496
- const getLength = (buf, index) => {
497
- let len = buf.readUInt8(1);
498
- if ((len & 0x80) === 0) {
499
- if (index === 0) {
500
- return len;
501
- }
502
- return getLength(buf.subarray(2 + len), index - 1);
503
- }
504
- const num = len & 0x7f;
505
- len = 0;
506
- for (let i = 0; i < num; i++) {
507
- len <<= 8;
508
- const j = buf.readUInt8(2 + i);
509
- len |= j;
510
- }
511
- if (index === 0) {
512
- return len;
513
- }
514
- return getLength(buf.subarray(2 + len), index - 1);
515
- };
516
- const getLengthOfSeqIndex = (sequence, index) => {
517
- const len = sequence.readUInt8(1);
518
- if ((len & 0x80) === 0) {
519
- return getLength(sequence.subarray(2), index);
520
- }
521
- const num = len & 0x7f;
522
- return getLength(sequence.subarray(2 + num), index);
523
- };
524
- const getModulusLength = (key) => {
525
- var _a, _b;
526
- if (weakMap.has(key)) {
527
- return weakMap.get(key);
528
- }
529
- const modulusLength = (_b = (_a = key.asymmetricKeyDetails) === null || _a === void 0 ? void 0 : _a.modulusLength) !== null && _b !== void 0 ? _b : (getLengthOfSeqIndex(key.export({ format: 'der', type: 'pkcs1' }), key.type === 'private' ? 1 : 0) -
530
- 1) <<
531
- 3;
532
- weakMap.set(key, modulusLength);
533
- return modulusLength;
534
- };
535
- const setModulusLength = (keyObject, modulusLength) => {
536
- weakMap.set(keyObject, modulusLength);
537
- };
538
- var checkModulusLength = (key, alg) => {
539
- if (getModulusLength(key) < 2048) {
422
+ var checkKeyLength = (key, alg) => {
423
+ const { modulusLength } = key.asymmetricKeyDetails;
424
+ if (typeof modulusLength !== 'number' || modulusLength < 2048) {
540
425
  throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
541
426
  }
542
427
  };
543
428
 
544
- const tagInteger = 0x02;
545
- const tagBitStr = 0x03;
546
- const tagOctStr = 0x04;
547
- const tagSequence = 0x30;
548
- const bZero = Buffer$1.from([0x00]);
549
- const bTagInteger = Buffer$1.from([tagInteger]);
550
- const bTagBitStr = Buffer$1.from([tagBitStr]);
551
- const bTagSequence = Buffer$1.from([tagSequence]);
552
- const bTagOctStr = Buffer$1.from([tagOctStr]);
553
- const encodeLength = (len) => {
554
- if (len < 128)
555
- return Buffer$1.from([len]);
556
- const buffer = Buffer$1.alloc(5);
557
- buffer.writeUInt32BE(len, 1);
558
- let offset = 1;
559
- while (buffer[offset] === 0)
560
- offset++;
561
- buffer[offset - 1] = 0x80 | (5 - offset);
562
- return buffer.slice(offset - 1);
563
- };
564
- const oids = new Map([
565
- ['P-256', Buffer$1.from('06 08 2A 86 48 CE 3D 03 01 07'.replace(/ /g, ''), 'hex')],
566
- ['secp256k1', Buffer$1.from('06 05 2B 81 04 00 0A'.replace(/ /g, ''), 'hex')],
567
- ['P-384', Buffer$1.from('06 05 2B 81 04 00 22'.replace(/ /g, ''), 'hex')],
568
- ['P-521', Buffer$1.from('06 05 2B 81 04 00 23'.replace(/ /g, ''), 'hex')],
569
- ['ecPublicKey', Buffer$1.from('06 07 2A 86 48 CE 3D 02 01'.replace(/ /g, ''), 'hex')],
570
- ['X25519', Buffer$1.from('06 03 2B 65 6E'.replace(/ /g, ''), 'hex')],
571
- ['X448', Buffer$1.from('06 03 2B 65 6F'.replace(/ /g, ''), 'hex')],
572
- ['Ed25519', Buffer$1.from('06 03 2B 65 70'.replace(/ /g, ''), 'hex')],
573
- ['Ed448', Buffer$1.from('06 03 2B 65 71'.replace(/ /g, ''), 'hex')],
574
- ]);
575
- class DumbAsn1Encoder {
576
- constructor() {
577
- this.length = 0;
578
- this.elements = [];
579
- }
580
- oidFor(oid) {
581
- const bOid = oids.get(oid);
582
- if (!bOid) {
583
- throw new JOSENotSupported('Invalid or unsupported OID');
584
- }
585
- this.elements.push(bOid);
586
- this.length += bOid.length;
587
- }
588
- zero() {
589
- this.elements.push(bTagInteger, Buffer$1.from([0x01]), bZero);
590
- this.length += 3;
591
- }
592
- one() {
593
- this.elements.push(bTagInteger, Buffer$1.from([0x01]), Buffer$1.from([0x01]));
594
- this.length += 3;
595
- }
596
- unsignedInteger(integer) {
597
- if (integer[0] & 0x80) {
598
- const len = encodeLength(integer.length + 1);
599
- this.elements.push(bTagInteger, len, bZero, integer);
600
- this.length += 2 + len.length + integer.length;
601
- }
602
- else {
603
- let i = 0;
604
- while (integer[i] === 0 && (integer[i + 1] & 0x80) === 0)
605
- i++;
606
- const len = encodeLength(integer.length - i);
607
- this.elements.push(bTagInteger, encodeLength(integer.length - i), integer.slice(i));
608
- this.length += 1 + len.length + integer.length - i;
609
- }
610
- }
611
- octStr(octStr) {
612
- const len = encodeLength(octStr.length);
613
- this.elements.push(bTagOctStr, encodeLength(octStr.length), octStr);
614
- this.length += 1 + len.length + octStr.length;
615
- }
616
- bitStr(bitS) {
617
- const len = encodeLength(bitS.length + 1);
618
- this.elements.push(bTagBitStr, encodeLength(bitS.length + 1), bZero, bitS);
619
- this.length += 1 + len.length + bitS.length + 1;
620
- }
621
- add(seq) {
622
- this.elements.push(seq);
623
- this.length += seq.length;
624
- }
625
- end(tag = bTagSequence) {
626
- const len = encodeLength(this.length);
627
- return Buffer$1.concat([tag, len, ...this.elements], 1 + len.length + this.length);
628
- }
629
- }
630
-
631
- const [major$2, minor$2] = process.version
632
- .slice(1)
633
- .split('.')
634
- .map((str) => parseInt(str, 10));
635
- const jwkImportSupported = major$2 >= 16 || (major$2 === 15 && minor$2 >= 12);
636
429
  const parse = (jwk) => {
637
- if (jwkImportSupported && jwk.kty !== 'oct') {
638
- return jwk.d
639
- ? createPrivateKey({ format: 'jwk', key: jwk })
640
- : createPublicKey({ format: 'jwk', key: jwk });
641
- }
642
- switch (jwk.kty) {
643
- case 'oct': {
644
- return createSecretKey(decode(jwk.k));
645
- }
646
- case 'RSA': {
647
- const enc = new DumbAsn1Encoder();
648
- const isPrivate = jwk.d !== undefined;
649
- const modulus = Buffer$1.from(jwk.n, 'base64');
650
- const exponent = Buffer$1.from(jwk.e, 'base64');
651
- if (isPrivate) {
652
- enc.zero();
653
- enc.unsignedInteger(modulus);
654
- enc.unsignedInteger(exponent);
655
- enc.unsignedInteger(Buffer$1.from(jwk.d, 'base64'));
656
- enc.unsignedInteger(Buffer$1.from(jwk.p, 'base64'));
657
- enc.unsignedInteger(Buffer$1.from(jwk.q, 'base64'));
658
- enc.unsignedInteger(Buffer$1.from(jwk.dp, 'base64'));
659
- enc.unsignedInteger(Buffer$1.from(jwk.dq, 'base64'));
660
- enc.unsignedInteger(Buffer$1.from(jwk.qi, 'base64'));
661
- }
662
- else {
663
- enc.unsignedInteger(modulus);
664
- enc.unsignedInteger(exponent);
665
- }
666
- const der = enc.end();
667
- const createInput = {
668
- key: der,
669
- format: 'der',
670
- type: 'pkcs1',
671
- };
672
- const keyObject = isPrivate ? createPrivateKey(createInput) : createPublicKey(createInput);
673
- setModulusLength(keyObject, modulus.length << 3);
674
- return keyObject;
675
- }
676
- case 'EC': {
677
- const enc = new DumbAsn1Encoder();
678
- const isPrivate = jwk.d !== undefined;
679
- const pub = Buffer$1.concat([
680
- Buffer$1.alloc(1, 4),
681
- Buffer$1.from(jwk.x, 'base64'),
682
- Buffer$1.from(jwk.y, 'base64'),
683
- ]);
684
- if (isPrivate) {
685
- enc.zero();
686
- const enc$1 = new DumbAsn1Encoder();
687
- enc$1.oidFor('ecPublicKey');
688
- enc$1.oidFor(jwk.crv);
689
- enc.add(enc$1.end());
690
- const enc$2 = new DumbAsn1Encoder();
691
- enc$2.one();
692
- enc$2.octStr(Buffer$1.from(jwk.d, 'base64'));
693
- const enc$3 = new DumbAsn1Encoder();
694
- enc$3.bitStr(pub);
695
- const f2 = enc$3.end(Buffer$1.from([0xa1]));
696
- enc$2.add(f2);
697
- const f = enc$2.end();
698
- const enc$4 = new DumbAsn1Encoder();
699
- enc$4.add(f);
700
- const f3 = enc$4.end(Buffer$1.from([0x04]));
701
- enc.add(f3);
702
- const der = enc.end();
703
- const keyObject = createPrivateKey({ key: der, format: 'der', type: 'pkcs8' });
704
- setCurve(keyObject, jwk.crv);
705
- return keyObject;
706
- }
707
- const enc$1 = new DumbAsn1Encoder();
708
- enc$1.oidFor('ecPublicKey');
709
- enc$1.oidFor(jwk.crv);
710
- enc.add(enc$1.end());
711
- enc.bitStr(pub);
712
- const der = enc.end();
713
- const keyObject = createPublicKey({ key: der, format: 'der', type: 'spki' });
714
- setCurve(keyObject, jwk.crv);
715
- return keyObject;
716
- }
717
- case 'OKP': {
718
- const enc = new DumbAsn1Encoder();
719
- const isPrivate = jwk.d !== undefined;
720
- if (isPrivate) {
721
- enc.zero();
722
- const enc$1 = new DumbAsn1Encoder();
723
- enc$1.oidFor(jwk.crv);
724
- enc.add(enc$1.end());
725
- const enc$2 = new DumbAsn1Encoder();
726
- enc$2.octStr(Buffer$1.from(jwk.d, 'base64'));
727
- const f = enc$2.end(Buffer$1.from([0x04]));
728
- enc.add(f);
729
- const der = enc.end();
730
- return createPrivateKey({ key: der, format: 'der', type: 'pkcs8' });
731
- }
732
- const enc$1 = new DumbAsn1Encoder();
733
- enc$1.oidFor(jwk.crv);
734
- enc.add(enc$1.end());
735
- enc.bitStr(Buffer$1.from(jwk.x, 'base64'));
736
- const der = enc.end();
737
- return createPublicKey({ key: der, format: 'der', type: 'spki' });
738
- }
739
- default:
740
- throw new JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value');
741
- }
430
+ return (jwk.d ? createPrivateKey : createPublicKey)({ format: 'jwk', key: jwk });
742
431
  };
743
432
 
744
- async function importJWK(jwk, alg, octAsKeyObject) {
745
- var _a;
433
+ async function importJWK(jwk, alg) {
746
434
  if (!isObject(jwk)) {
747
435
  throw new TypeError('JWK must be an object');
748
436
  }
749
- alg || (alg = jwk.alg);
750
- if (typeof alg !== 'string' || !alg) {
751
- throw new TypeError('"alg" argument is required when "jwk.alg" is not present');
752
- }
437
+ alg ||= jwk.alg;
753
438
  switch (jwk.kty) {
754
439
  case 'oct':
755
440
  if (typeof jwk.k !== 'string' || !jwk.k) {
756
441
  throw new TypeError('missing "k" (Key Value) Parameter value');
757
442
  }
758
- octAsKeyObject !== null && octAsKeyObject !== void 0 ? octAsKeyObject : (octAsKeyObject = jwk.ext !== true);
759
- if (octAsKeyObject) {
760
- return parse({ ...jwk, alg, ext: (_a = jwk.ext) !== null && _a !== void 0 ? _a : false });
761
- }
762
443
  return decode(jwk.k);
763
444
  case 'RSA':
764
445
  if (jwk.oth !== undefined) {
@@ -881,12 +562,6 @@ function dsaDigest(alg) {
881
562
  }
882
563
  }
883
564
 
884
- const [major$1, minor$1] = process.version
885
- .slice(1)
886
- .split('.')
887
- .map((str) => parseInt(str, 10));
888
- const electron = 'electron' in process.versions;
889
- const rsaPssParams = !electron && (major$1 >= 17 || (major$1 === 16 && minor$1 >= 9));
890
565
  const PSS = {
891
566
  padding: constants.RSA_PKCS1_PSS_PADDING,
892
567
  saltLength: constants.RSA_PSS_SALTLEN_DIGEST,
@@ -910,11 +585,11 @@ function keyForCrypto(alg, key) {
910
585
  if (key.asymmetricKeyType !== 'rsa') {
911
586
  throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa');
912
587
  }
913
- checkModulusLength(key, alg);
588
+ checkKeyLength(key, alg);
914
589
  return key;
915
- case rsaPssParams && 'PS256':
916
- case rsaPssParams && 'PS384':
917
- case rsaPssParams && 'PS512':
590
+ case 'PS256':
591
+ case 'PS384':
592
+ case 'PS512':
918
593
  if (key.asymmetricKeyType === 'rsa-pss') {
919
594
  const { hashAlgorithm, mgf1HashAlgorithm, saltLength } = key.asymmetricKeyDetails;
920
595
  const length = parseInt(alg.slice(-3), 10);
@@ -929,15 +604,7 @@ function keyForCrypto(alg, key) {
929
604
  else if (key.asymmetricKeyType !== 'rsa') {
930
605
  throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa or rsa-pss');
931
606
  }
932
- checkModulusLength(key, alg);
933
- return { key, ...PSS };
934
- case !rsaPssParams && 'PS256':
935
- case !rsaPssParams && 'PS384':
936
- case !rsaPssParams && 'PS512':
937
- if (key.asymmetricKeyType !== 'rsa') {
938
- throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa');
939
- }
940
- checkModulusLength(key, alg);
607
+ checkKeyLength(key, alg);
941
608
  return { key, ...PSS };
942
609
  case 'ES256':
943
610
  case 'ES256K':
@@ -988,13 +655,7 @@ function getSignVerifyKey(alg, key, usage) {
988
655
  throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array'));
989
656
  }
990
657
 
991
- let oneShotSign;
992
- if (crypto.sign.length > 3) {
993
- oneShotSign = promisify(crypto.sign);
994
- }
995
- else {
996
- oneShotSign = crypto.sign;
997
- }
658
+ const oneShotSign = promisify(crypto.sign);
998
659
  const sign = async (alg, key, data) => {
999
660
  const keyObject = getSignVerifyKey(alg, key, 'sign');
1000
661
  if (alg.startsWith('HS')) {
@@ -1005,18 +666,7 @@ const sign = async (alg, key, data) => {
1005
666
  return oneShotSign(dsaDigest(alg), data, keyForCrypto(alg, keyObject));
1006
667
  };
1007
668
 
1008
- const [major, minor] = process.version
1009
- .slice(1)
1010
- .split('.')
1011
- .map((str) => parseInt(str, 10));
1012
- const oneShotCallbackSupported = major >= 16 || (major === 15 && minor >= 13);
1013
- let oneShotVerify;
1014
- if (crypto.verify.length > 4 && oneShotCallbackSupported) {
1015
- oneShotVerify = promisify(crypto.verify);
1016
- }
1017
- else {
1018
- oneShotVerify = crypto.verify;
1019
- }
669
+ const oneShotVerify = promisify(crypto.verify);
1020
670
  const verify = async (alg, key, signature, data) => {
1021
671
  const keyObject = getSignVerifyKey(alg, key, 'verify');
1022
672
  if (alg.startsWith('HS')) {
@@ -1040,7 +690,6 @@ const verify = async (alg, key, signature, data) => {
1040
690
  };
1041
691
 
1042
692
  async function flattenedVerify(jws, key, options) {
1043
- var _a;
1044
693
  if (!isObject(jws)) {
1045
694
  throw new JWSInvalid('Flattened JWS must be an object');
1046
695
  }
@@ -1076,7 +725,7 @@ async function flattenedVerify(jws, key, options) {
1076
725
  ...parsedProt,
1077
726
  ...jws.header,
1078
727
  };
1079
- const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options === null || options === void 0 ? void 0 : options.crit, parsedProt, joseHeader);
728
+ const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options?.crit, parsedProt, joseHeader);
1080
729
  let b64 = true;
1081
730
  if (extensions.has('b64')) {
1082
731
  b64 = parsedProt.b64;
@@ -1090,7 +739,7 @@ async function flattenedVerify(jws, key, options) {
1090
739
  }
1091
740
  const algorithms = options && validateAlgorithms('algorithms', options.algorithms);
1092
741
  if (algorithms && !algorithms.has(alg)) {
1093
- throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed');
742
+ throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
1094
743
  }
1095
744
  if (b64) {
1096
745
  if (typeof jws.payload !== 'string') {
@@ -1106,15 +755,26 @@ async function flattenedVerify(jws, key, options) {
1106
755
  resolvedKey = true;
1107
756
  }
1108
757
  checkKeyType(alg, key, 'verify');
1109
- const data = concat(encoder.encode((_a = jws.protected) !== null && _a !== void 0 ? _a : ''), encoder.encode('.'), typeof jws.payload === 'string' ? encoder.encode(jws.payload) : jws.payload);
1110
- const signature = decode(jws.signature);
758
+ const data = concat(encoder.encode(jws.protected ?? ''), encoder.encode('.'), typeof jws.payload === 'string' ? encoder.encode(jws.payload) : jws.payload);
759
+ let signature;
760
+ try {
761
+ signature = decode(jws.signature);
762
+ }
763
+ catch {
764
+ throw new JWSInvalid('Failed to base64url decode the signature');
765
+ }
1111
766
  const verified = await verify(alg, key, signature, data);
1112
767
  if (!verified) {
1113
768
  throw new JWSSignatureVerificationFailed();
1114
769
  }
1115
770
  let payload;
1116
771
  if (b64) {
1117
- payload = decode(jws.payload);
772
+ try {
773
+ payload = decode(jws.payload);
774
+ }
775
+ catch {
776
+ throw new JWSInvalid('Failed to base64url decode the payload');
777
+ }
1118
778
  }
1119
779
  else if (typeof jws.payload === 'string') {
1120
780
  payload = encoder.encode(jws.payload);
@@ -1227,15 +887,26 @@ var jwtPayload = (protectedHeader, encodedPayload, options = {}) => {
1227
887
  if (!isObject(payload)) {
1228
888
  throw new JWTInvalid('JWT Claims Set must be a top-level JSON object');
1229
889
  }
1230
- const { issuer } = options;
890
+ const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;
891
+ if (maxTokenAge !== undefined)
892
+ requiredClaims.push('iat');
893
+ if (audience !== undefined)
894
+ requiredClaims.push('aud');
895
+ if (subject !== undefined)
896
+ requiredClaims.push('sub');
897
+ if (issuer !== undefined)
898
+ requiredClaims.push('iss');
899
+ for (const claim of new Set(requiredClaims.reverse())) {
900
+ if (!(claim in payload)) {
901
+ throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, claim, 'missing');
902
+ }
903
+ }
1231
904
  if (issuer && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {
1232
905
  throw new JWTClaimValidationFailed('unexpected "iss" claim value', 'iss', 'check_failed');
1233
906
  }
1234
- const { subject } = options;
1235
907
  if (subject && payload.sub !== subject) {
1236
908
  throw new JWTClaimValidationFailed('unexpected "sub" claim value', 'sub', 'check_failed');
1237
909
  }
1238
- const { audience } = options;
1239
910
  if (audience &&
1240
911
  !checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) {
1241
912
  throw new JWTClaimValidationFailed('unexpected "aud" claim value', 'aud', 'check_failed');
@@ -1256,7 +927,7 @@ var jwtPayload = (protectedHeader, encodedPayload, options = {}) => {
1256
927
  }
1257
928
  const { currentDate } = options;
1258
929
  const now = epoch(currentDate || new Date());
1259
- if ((payload.iat !== undefined || options.maxTokenAge) && typeof payload.iat !== 'number') {
930
+ if ((payload.iat !== undefined || maxTokenAge) && typeof payload.iat !== 'number') {
1260
931
  throw new JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid');
1261
932
  }
1262
933
  if (payload.nbf !== undefined) {
@@ -1275,9 +946,9 @@ var jwtPayload = (protectedHeader, encodedPayload, options = {}) => {
1275
946
  throw new JWTExpired('"exp" claim timestamp check failed', 'exp', 'check_failed');
1276
947
  }
1277
948
  }
1278
- if (options.maxTokenAge) {
949
+ if (maxTokenAge) {
1279
950
  const age = now - payload.iat;
1280
- const max = typeof options.maxTokenAge === 'number' ? options.maxTokenAge : secs(options.maxTokenAge);
951
+ const max = typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge);
1281
952
  if (age - tolerance > max) {
1282
953
  throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', 'iat', 'check_failed');
1283
954
  }
@@ -1289,9 +960,8 @@ var jwtPayload = (protectedHeader, encodedPayload, options = {}) => {
1289
960
  };
1290
961
 
1291
962
  async function jwtVerify(jwt, key, options) {
1292
- var _a;
1293
963
  const verified = await compactVerify(jwt, key, options);
1294
- if (((_a = verified.protectedHeader.crit) === null || _a === void 0 ? void 0 : _a.includes('b64')) && verified.protectedHeader.b64 === false) {
964
+ if (verified.protectedHeader.crit?.includes('b64') && verified.protectedHeader.b64 === false) {
1295
965
  throw new JWTInvalid('JWTs MUST NOT use unencoded payload');
1296
966
  }
1297
967
  const payload = jwtPayload(verified.protectedHeader, verified.payload, options);
@@ -1331,15 +1001,16 @@ function clone(obj) {
1331
1001
  return JSON.parse(JSON.stringify(obj));
1332
1002
  }
1333
1003
  class LocalJWKSet {
1004
+ _jwks;
1005
+ _cached = new WeakMap();
1334
1006
  constructor(jwks) {
1335
- this._cached = new WeakMap();
1336
1007
  if (!isJWKSLike(jwks)) {
1337
1008
  throw new JWKSInvalid('JSON Web Key Set malformed');
1338
1009
  }
1339
1010
  this._jwks = clone(jwks);
1340
1011
  }
1341
1012
  async getKey(protectedHeader, token) {
1342
- const { alg, kid } = { ...protectedHeader, ...token.header };
1013
+ const { alg, kid } = { ...protectedHeader, ...token?.header };
1343
1014
  const kty = getKtyFromAlg(alg);
1344
1015
  const candidates = this._jwks.keys.filter((jwk) => {
1345
1016
  let candidate = kty === jwk.kty;
@@ -1381,18 +1052,33 @@ class LocalJWKSet {
1381
1052
  throw new JWKSNoMatchingKey();
1382
1053
  }
1383
1054
  else if (length !== 1) {
1384
- throw new JWKSMultipleMatchingKeys();
1055
+ const error = new JWKSMultipleMatchingKeys();
1056
+ const { _cached } = this;
1057
+ error[Symbol.asyncIterator] = async function* () {
1058
+ for (const jwk of candidates) {
1059
+ try {
1060
+ yield await importWithAlgCache(_cached, jwk, alg);
1061
+ }
1062
+ catch {
1063
+ continue;
1064
+ }
1065
+ }
1066
+ };
1067
+ throw error;
1385
1068
  }
1386
- const cached = this._cached.get(jwk) || this._cached.set(jwk, {}).get(jwk);
1387
- if (cached[alg] === undefined) {
1388
- const keyObject = await importJWK({ ...jwk, ext: true }, alg);
1389
- if (keyObject instanceof Uint8Array || keyObject.type !== 'public') {
1390
- throw new JWKSInvalid('JSON Web Key Set members must be public keys');
1391
- }
1392
- cached[alg] = keyObject;
1069
+ return importWithAlgCache(this._cached, jwk, alg);
1070
+ }
1071
+ }
1072
+ async function importWithAlgCache(cache, jwk, alg) {
1073
+ const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk);
1074
+ if (cached[alg] === undefined) {
1075
+ const key = await importJWK({ ...jwk, ext: true }, alg);
1076
+ if (key instanceof Uint8Array || key.type !== 'public') {
1077
+ throw new JWKSInvalid('JSON Web Key Set members must be public keys');
1393
1078
  }
1394
- return cached[alg];
1079
+ cached[alg] = key;
1395
1080
  }
1081
+ return cached[alg];
1396
1082
  }
1397
1083
 
1398
1084
  const fetchJwks = async (url, timeout, options) => {
@@ -1433,7 +1119,19 @@ const fetchJwks = async (url, timeout, options) => {
1433
1119
  }
1434
1120
  };
1435
1121
 
1122
+ function isCloudflareWorkers() {
1123
+ return (typeof WebSocketPair !== 'undefined' ||
1124
+ (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') ||
1125
+ (typeof EdgeRuntime !== 'undefined' && EdgeRuntime === 'vercel'));
1126
+ }
1436
1127
  class RemoteJWKSet extends LocalJWKSet {
1128
+ _url;
1129
+ _timeoutDuration;
1130
+ _cooldownDuration;
1131
+ _cacheMaxAge;
1132
+ _jwksTimestamp;
1133
+ _pendingFetch;
1134
+ _options;
1437
1135
  constructor(url, options) {
1438
1136
  super({ keys: [] });
1439
1137
  this._jwks = undefined;
@@ -1441,12 +1139,12 @@ class RemoteJWKSet extends LocalJWKSet {
1441
1139
  throw new TypeError('url must be an instance of URL');
1442
1140
  }
1443
1141
  this._url = new URL(url.href);
1444
- this._options = { agent: options === null || options === void 0 ? void 0 : options.agent, headers: options === null || options === void 0 ? void 0 : options.headers };
1142
+ this._options = { agent: options?.agent, headers: options?.headers };
1445
1143
  this._timeoutDuration =
1446
- typeof (options === null || options === void 0 ? void 0 : options.timeoutDuration) === 'number' ? options === null || options === void 0 ? void 0 : options.timeoutDuration : 5000;
1144
+ typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000;
1447
1145
  this._cooldownDuration =
1448
- typeof (options === null || options === void 0 ? void 0 : options.cooldownDuration) === 'number' ? options === null || options === void 0 ? void 0 : options.cooldownDuration : 30000;
1449
- this._cacheMaxAge = typeof (options === null || options === void 0 ? void 0 : options.cacheMaxAge) === 'number' ? options === null || options === void 0 ? void 0 : options.cacheMaxAge : 600000;
1146
+ typeof options?.cooldownDuration === 'number' ? options?.cooldownDuration : 30000;
1147
+ this._cacheMaxAge = typeof options?.cacheMaxAge === 'number' ? options?.cacheMaxAge : 600000;
1450
1148
  }
1451
1149
  coolingDown() {
1452
1150
  return typeof this._jwksTimestamp === 'number'
@@ -1477,38 +1175,29 @@ class RemoteJWKSet extends LocalJWKSet {
1477
1175
  }
1478
1176
  async reload() {
1479
1177
  if (this._pendingFetch && isCloudflareWorkers()) {
1480
- return new Promise((resolve) => {
1481
- const isDone = () => {
1482
- if (this._pendingFetch === undefined) {
1483
- resolve();
1484
- }
1485
- else {
1486
- setTimeout(isDone, 5);
1487
- }
1488
- };
1489
- isDone();
1490
- });
1491
- }
1492
- if (!this._pendingFetch) {
1493
- this._pendingFetch = fetchJwks(this._url, this._timeoutDuration, this._options)
1494
- .then((json) => {
1495
- if (!isJWKSLike(json)) {
1496
- throw new JWKSInvalid('JSON Web Key Set malformed');
1497
- }
1498
- this._jwks = { keys: json.keys };
1499
- this._jwksTimestamp = Date.now();
1500
- this._pendingFetch = undefined;
1501
- })
1502
- .catch((err) => {
1503
- this._pendingFetch = undefined;
1504
- throw err;
1505
- });
1178
+ this._pendingFetch = undefined;
1506
1179
  }
1180
+ this._pendingFetch ||= fetchJwks(this._url, this._timeoutDuration, this._options)
1181
+ .then((json) => {
1182
+ if (!isJWKSLike(json)) {
1183
+ throw new JWKSInvalid('JSON Web Key Set malformed');
1184
+ }
1185
+ this._jwks = { keys: json.keys };
1186
+ this._jwksTimestamp = Date.now();
1187
+ this._pendingFetch = undefined;
1188
+ })
1189
+ .catch((err) => {
1190
+ this._pendingFetch = undefined;
1191
+ throw err;
1192
+ });
1507
1193
  await this._pendingFetch;
1508
1194
  }
1509
1195
  }
1510
1196
  function createRemoteJWKSet(url, options) {
1511
- return RemoteJWKSet.prototype.getKey.bind(new RemoteJWKSet(url, options));
1197
+ const set = new RemoteJWKSet(url, options);
1198
+ return async function (protectedHeader, token) {
1199
+ return set.getKey(protectedHeader, token);
1200
+ };
1512
1201
  }
1513
1202
 
1514
1203
  // https://appleid.apple.com/.well-known/openid-configuration
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@logto/connector-apple",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Apple web connector implementation.",
5
5
  "dependencies": {
6
- "@logto/connector-kit": "^1.1.1",
7
- "@logto/shared": "^3.0.0",
8
- "jose": "^4.3.8"
6
+ "@logto/connector-kit": "^2.1.0",
7
+ "@logto/shared": "^3.1.0",
8
+ "jose": "^5.0.0"
9
9
  },
10
10
  "main": "./lib/index.js",
11
11
  "module": "./lib/index.js",
@@ -19,7 +19,7 @@
19
19
  "logo-dark.svg"
20
20
  ],
21
21
  "engines": {
22
- "node": "^18.12.0"
22
+ "node": "^20.9.0"
23
23
  },
24
24
  "eslintConfig": {
25
25
  "extends": "@silverhand",