@logto/connector-apple 1.3.1 → 1.4.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/lib/index.js CHANGED
@@ -1,1361 +1,180 @@
1
- import { ConnectorPlatform, ConnectorConfigFormItemType, ConnectorType, validateConfig, ConnectorError, ConnectorErrorCodes, jsonGuard } from '@logto/connector-kit';
2
- import * as crypto from 'node:crypto';
3
- import crypto__default, { KeyObject, createPrivateKey, createPublicKey, constants, createSecretKey } from 'node:crypto';
4
- import { z } from 'zod';
5
- import { Buffer as Buffer$1 } from 'node:buffer';
6
- import * as util from 'node:util';
7
- import { promisify } from 'node:util';
8
- import * as http from 'node:http';
9
- import * as https from 'node:https';
10
- import { once } from 'node:events';
11
-
12
- // https://github.com/facebook/jest/issues/7547
13
- const assert = (value, error) => {
14
- if (!value) {
15
- // https://github.com/typescript-eslint/typescript-eslint/issues/3814
16
- // eslint-disable-next-line @typescript-eslint/no-throw-literal
17
- throw error;
18
- }
19
- };
20
-
21
- const POOL_SIZE_MULTIPLIER = 128;
22
- let pool, poolOffset;
23
- function fillPool(bytes) {
24
- if (!pool || pool.length < bytes) {
25
- pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER);
26
- crypto__default.getRandomValues(pool);
27
- poolOffset = 0;
28
- } else if (poolOffset + bytes > pool.length) {
29
- crypto__default.getRandomValues(pool);
30
- poolOffset = 0;
31
- }
32
- poolOffset += bytes;
33
- }
34
- function random(bytes) {
35
- fillPool((bytes -= 0));
36
- return pool.subarray(poolOffset - bytes, poolOffset)
37
- }
38
- function customRandom(alphabet, defaultSize, getRandom) {
39
- let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1;
40
- let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length);
41
- return (size = defaultSize) => {
42
- let id = '';
43
- while (true) {
44
- let bytes = getRandom(step);
45
- let i = step;
46
- while (i--) {
47
- id += alphabet[bytes[i] & mask] || '';
48
- if (id.length === size) return id
49
- }
50
- }
51
- }
52
- }
53
- function customAlphabet(alphabet, size = 21) {
54
- return customRandom(alphabet, size, random)
55
- }
56
-
57
- const lowercaseAlphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
58
- const alphabet = `${lowercaseAlphabet}ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
59
- const buildIdGenerator = (size, includingUppercase = true) => customAlphabet(includingUppercase ? alphabet : lowercaseAlphabet, size);
60
- /**
61
- * Generate a standard id with 21 characters, including lowercase letters and numbers.
62
- *
63
- * @see {@link lowercaseAlphabet}
64
- */
65
- const generateStandardId = buildIdGenerator(21, false);
66
- /**
67
- * Generate a standard short id with 12 characters, including lowercase letters and numbers.
68
- *
69
- * @see {@link lowercaseAlphabet}
70
- */
71
- buildIdGenerator(12, false);
72
- /**
73
- * Generate a standard secret with 32 characters, including uppercase letters, lowercase
74
- * letters, and numbers.
75
- *
76
- * @see {@link alphabet}
77
- */
78
- buildIdGenerator(32);
79
-
80
- const encoder = new TextEncoder();
81
- const decoder = new TextDecoder();
82
- function concat(...buffers) {
83
- const size = buffers.reduce((acc, { length }) => acc + length, 0);
84
- const buf = new Uint8Array(size);
85
- let i = 0;
86
- buffers.forEach((buffer) => {
87
- buf.set(buffer, i);
88
- i += buffer.length;
89
- });
90
- return buf;
91
- }
92
-
93
- function normalize(input) {
94
- let encoded = input;
95
- if (encoded instanceof Uint8Array) {
96
- encoded = decoder.decode(encoded);
97
- }
98
- return encoded;
99
- }
100
- const decode = (input) => new Uint8Array(Buffer$1.from(normalize(input), 'base64'));
101
-
102
- class JOSEError extends Error {
103
- static get code() {
104
- return 'ERR_JOSE_GENERIC';
105
- }
106
- code = 'ERR_JOSE_GENERIC';
107
- constructor(message) {
108
- super(message);
109
- this.name = this.constructor.name;
110
- Error.captureStackTrace?.(this, this.constructor);
111
- }
112
- }
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;
120
- constructor(message, claim = 'unspecified', reason = 'unspecified') {
121
- super(message);
122
- this.claim = claim;
123
- this.reason = reason;
124
- }
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;
133
- constructor(message, claim = 'unspecified', reason = 'unspecified') {
134
- super(message);
135
- this.claim = claim;
136
- this.reason = reason;
137
- }
138
- }
139
- class JOSEAlgNotAllowed extends JOSEError {
140
- static get code() {
141
- return 'ERR_JOSE_ALG_NOT_ALLOWED';
142
- }
143
- code = 'ERR_JOSE_ALG_NOT_ALLOWED';
144
- }
145
- class JOSENotSupported extends JOSEError {
146
- static get code() {
147
- return 'ERR_JOSE_NOT_SUPPORTED';
148
- }
149
- code = 'ERR_JOSE_NOT_SUPPORTED';
150
- }
151
- class JWSInvalid extends JOSEError {
152
- static get code() {
153
- return 'ERR_JWS_INVALID';
154
- }
155
- code = 'ERR_JWS_INVALID';
156
- }
157
- class JWTInvalid extends JOSEError {
158
- static get code() {
159
- return 'ERR_JWT_INVALID';
160
- }
161
- code = 'ERR_JWT_INVALID';
162
- }
163
- class JWKSInvalid extends JOSEError {
164
- static get code() {
165
- return 'ERR_JWKS_INVALID';
166
- }
167
- code = 'ERR_JWKS_INVALID';
168
- }
169
- class JWKSNoMatchingKey extends JOSEError {
170
- static get code() {
171
- return 'ERR_JWKS_NO_MATCHING_KEY';
172
- }
173
- code = 'ERR_JWKS_NO_MATCHING_KEY';
174
- message = 'no applicable key found in the JSON Web Key Set';
175
- }
176
- class JWKSMultipleMatchingKeys extends JOSEError {
177
- [Symbol.asyncIterator];
178
- static get code() {
179
- return 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
180
- }
181
- code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
182
- message = 'multiple matching keys found in the JSON Web Key Set';
183
- }
184
- class JWKSTimeout extends JOSEError {
185
- static get code() {
186
- return 'ERR_JWKS_TIMEOUT';
187
- }
188
- code = 'ERR_JWKS_TIMEOUT';
189
- message = 'request timed out';
190
- }
191
- class JWSSignatureVerificationFailed extends JOSEError {
192
- static get code() {
193
- return 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';
194
- }
195
- code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';
196
- message = 'signature verification failed';
197
- }
198
-
199
- var isKeyObject = (obj) => util.types.isKeyObject(obj);
200
-
201
- const webcrypto = crypto.webcrypto;
202
- const isCryptoKey = (key) => util.types.isCryptoKey(key);
203
-
204
- function unusable(name, prop = 'algorithm.name') {
205
- return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
206
- }
207
- function isAlgorithm(algorithm, name) {
208
- return algorithm.name === name;
209
- }
210
- function getHashLength(hash) {
211
- return parseInt(hash.name.slice(4), 10);
212
- }
213
- function getNamedCurve$1(alg) {
214
- switch (alg) {
215
- case 'ES256':
216
- return 'P-256';
217
- case 'ES384':
218
- return 'P-384';
219
- case 'ES512':
220
- return 'P-521';
221
- default:
222
- throw new Error('unreachable');
223
- }
224
- }
225
- function checkUsage(key, usages) {
226
- if (usages.length && !usages.some((expected) => key.usages.includes(expected))) {
227
- let msg = 'CryptoKey does not support this operation, its usages must include ';
228
- if (usages.length > 2) {
229
- const last = usages.pop();
230
- msg += `one of ${usages.join(', ')}, or ${last}.`;
231
- }
232
- else if (usages.length === 2) {
233
- msg += `one of ${usages[0]} or ${usages[1]}.`;
234
- }
235
- else {
236
- msg += `${usages[0]}.`;
237
- }
238
- throw new TypeError(msg);
239
- }
240
- }
241
- function checkSigCryptoKey(key, alg, ...usages) {
242
- switch (alg) {
243
- case 'HS256':
244
- case 'HS384':
245
- case 'HS512': {
246
- if (!isAlgorithm(key.algorithm, 'HMAC'))
247
- throw unusable('HMAC');
248
- const expected = parseInt(alg.slice(2), 10);
249
- const actual = getHashLength(key.algorithm.hash);
250
- if (actual !== expected)
251
- throw unusable(`SHA-${expected}`, 'algorithm.hash');
252
- break;
253
- }
254
- case 'RS256':
255
- case 'RS384':
256
- case 'RS512': {
257
- if (!isAlgorithm(key.algorithm, 'RSASSA-PKCS1-v1_5'))
258
- throw unusable('RSASSA-PKCS1-v1_5');
259
- const expected = parseInt(alg.slice(2), 10);
260
- const actual = getHashLength(key.algorithm.hash);
261
- if (actual !== expected)
262
- throw unusable(`SHA-${expected}`, 'algorithm.hash');
263
- break;
264
- }
265
- case 'PS256':
266
- case 'PS384':
267
- case 'PS512': {
268
- if (!isAlgorithm(key.algorithm, 'RSA-PSS'))
269
- throw unusable('RSA-PSS');
270
- const expected = parseInt(alg.slice(2), 10);
271
- const actual = getHashLength(key.algorithm.hash);
272
- if (actual !== expected)
273
- throw unusable(`SHA-${expected}`, 'algorithm.hash');
274
- break;
275
- }
276
- case 'EdDSA': {
277
- if (key.algorithm.name !== 'Ed25519' && key.algorithm.name !== 'Ed448') {
278
- throw unusable('Ed25519 or Ed448');
279
- }
280
- break;
281
- }
282
- case 'ES256':
283
- case 'ES384':
284
- case 'ES512': {
285
- if (!isAlgorithm(key.algorithm, 'ECDSA'))
286
- throw unusable('ECDSA');
287
- const expected = getNamedCurve$1(alg);
288
- const actual = key.algorithm.namedCurve;
289
- if (actual !== expected)
290
- throw unusable(expected, 'algorithm.namedCurve');
291
- break;
292
- }
293
- default:
294
- throw new TypeError('CryptoKey does not support this operation');
295
- }
296
- checkUsage(key, usages);
297
- }
298
-
299
- function message(msg, actual, ...types) {
300
- if (types.length > 2) {
301
- const last = types.pop();
302
- msg += `one of type ${types.join(', ')}, or ${last}.`;
303
- }
304
- else if (types.length === 2) {
305
- msg += `one of type ${types[0]} or ${types[1]}.`;
306
- }
307
- else {
308
- msg += `of type ${types[0]}.`;
309
- }
310
- if (actual == null) {
311
- msg += ` Received ${actual}`;
312
- }
313
- else if (typeof actual === 'function' && actual.name) {
314
- msg += ` Received function ${actual.name}`;
315
- }
316
- else if (typeof actual === 'object' && actual != null) {
317
- if (actual.constructor && actual.constructor.name) {
318
- msg += ` Received an instance of ${actual.constructor.name}`;
319
- }
320
- }
321
- return msg;
322
- }
323
- var invalidKeyInput = (actual, ...types) => {
324
- return message('Key must be ', actual, ...types);
325
- };
326
- function withAlg(alg, actual, ...types) {
327
- return message(`Key for the ${alg} algorithm must be `, actual, ...types);
328
- }
329
-
330
- var isKeyLike = (key) => isKeyObject(key) || isCryptoKey(key);
331
- const types = ['KeyObject'];
332
- if (globalThis.CryptoKey || webcrypto?.CryptoKey) {
333
- types.push('CryptoKey');
334
- }
335
-
336
- const isDisjoint = (...headers) => {
337
- const sources = headers.filter(Boolean);
338
- if (sources.length === 0 || sources.length === 1) {
339
- return true;
340
- }
341
- let acc;
342
- for (const header of sources) {
343
- const parameters = Object.keys(header);
344
- if (!acc || acc.size === 0) {
345
- acc = new Set(parameters);
346
- continue;
347
- }
348
- for (const parameter of parameters) {
349
- if (acc.has(parameter)) {
350
- return false;
351
- }
352
- acc.add(parameter);
353
- }
354
- }
355
- return true;
356
- };
357
-
358
- function isObjectLike(value) {
359
- return typeof value === 'object' && value !== null;
360
- }
361
- function isObject(input) {
362
- if (!isObjectLike(input) || Object.prototype.toString.call(input) !== '[object Object]') {
363
- return false;
364
- }
365
- if (Object.getPrototypeOf(input) === null) {
366
- return true;
367
- }
368
- let proto = input;
369
- while (Object.getPrototypeOf(proto) !== null) {
370
- proto = Object.getPrototypeOf(proto);
371
- }
372
- return Object.getPrototypeOf(input) === proto;
373
- }
374
-
375
- const namedCurveToJOSE = (namedCurve) => {
376
- switch (namedCurve) {
377
- case 'prime256v1':
378
- return 'P-256';
379
- case 'secp384r1':
380
- return 'P-384';
381
- case 'secp521r1':
382
- return 'P-521';
383
- case 'secp256k1':
384
- return 'secp256k1';
385
- default:
386
- throw new JOSENotSupported('Unsupported key curve for this operation');
387
- }
388
- };
389
- const getNamedCurve = (kee, raw) => {
390
- let key;
391
- if (isCryptoKey(kee)) {
392
- key = KeyObject.from(kee);
393
- }
394
- else if (isKeyObject(kee)) {
395
- key = kee;
396
- }
397
- else {
398
- throw new TypeError(invalidKeyInput(kee, ...types));
399
- }
400
- if (key.type === 'secret') {
401
- throw new TypeError('only "private" or "public" type keys can be used for this operation');
402
- }
403
- switch (key.asymmetricKeyType) {
404
- case 'ed25519':
405
- case 'ed448':
406
- return `Ed${key.asymmetricKeyType.slice(2)}`;
407
- case 'x25519':
408
- case 'x448':
409
- return `X${key.asymmetricKeyType.slice(1)}`;
410
- case 'ec': {
411
- let namedCurve = key.asymmetricKeyDetails.namedCurve;
412
- if (raw) {
413
- return namedCurve;
414
- }
415
- return namedCurveToJOSE(namedCurve);
416
- }
417
- default:
418
- throw new TypeError('Invalid asymmetric key type for this operation');
419
- }
420
- };
421
-
422
- var checkKeyLength = (key, alg) => {
423
- const { modulusLength } = key.asymmetricKeyDetails;
424
- if (typeof modulusLength !== 'number' || modulusLength < 2048) {
425
- throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
426
- }
427
- };
428
-
429
- const parse = (jwk) => {
430
- return (jwk.d ? createPrivateKey : createPublicKey)({ format: 'jwk', key: jwk });
431
- };
432
-
433
- async function importJWK(jwk, alg) {
434
- if (!isObject(jwk)) {
435
- throw new TypeError('JWK must be an object');
436
- }
437
- alg ||= jwk.alg;
438
- switch (jwk.kty) {
439
- case 'oct':
440
- if (typeof jwk.k !== 'string' || !jwk.k) {
441
- throw new TypeError('missing "k" (Key Value) Parameter value');
442
- }
443
- return decode(jwk.k);
444
- case 'RSA':
445
- if (jwk.oth !== undefined) {
446
- throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');
447
- }
448
- case 'EC':
449
- case 'OKP':
450
- return parse({ ...jwk, alg });
451
- default:
452
- throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value');
453
- }
454
- }
455
-
456
- const symmetricTypeCheck = (alg, key) => {
457
- if (key instanceof Uint8Array)
458
- return;
459
- if (!isKeyLike(key)) {
460
- throw new TypeError(withAlg(alg, key, ...types, 'Uint8Array'));
461
- }
462
- if (key.type !== 'secret') {
463
- throw new TypeError(`${types.join(' or ')} instances for symmetric algorithms must be of type "secret"`);
464
- }
465
- };
466
- const asymmetricTypeCheck = (alg, key, usage) => {
467
- if (!isKeyLike(key)) {
468
- throw new TypeError(withAlg(alg, key, ...types));
469
- }
470
- if (key.type === 'secret') {
471
- throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithms must not be of type "secret"`);
472
- }
473
- if (usage === 'sign' && key.type === 'public') {
474
- throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithm signing must be of type "private"`);
475
- }
476
- if (usage === 'decrypt' && key.type === 'public') {
477
- throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithm decryption must be of type "private"`);
478
- }
479
- if (key.algorithm && usage === 'verify' && key.type === 'private') {
480
- throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithm verifying must be of type "public"`);
481
- }
482
- if (key.algorithm && usage === 'encrypt' && key.type === 'private') {
483
- throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithm encryption must be of type "public"`);
484
- }
485
- };
486
- const checkKeyType = (alg, key, usage) => {
487
- const symmetric = alg.startsWith('HS') ||
488
- alg === 'dir' ||
489
- alg.startsWith('PBES2') ||
490
- /^A\d{3}(?:GCM)?KW$/.test(alg);
491
- if (symmetric) {
492
- symmetricTypeCheck(alg, key);
493
- }
494
- else {
495
- asymmetricTypeCheck(alg, key, usage);
496
- }
497
- };
498
-
499
- function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {
500
- if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) {
501
- throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected');
502
- }
503
- if (!protectedHeader || protectedHeader.crit === undefined) {
504
- return new Set();
505
- }
506
- if (!Array.isArray(protectedHeader.crit) ||
507
- protectedHeader.crit.length === 0 ||
508
- protectedHeader.crit.some((input) => typeof input !== 'string' || input.length === 0)) {
509
- throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');
510
- }
511
- let recognized;
512
- if (recognizedOption !== undefined) {
513
- recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]);
514
- }
515
- else {
516
- recognized = recognizedDefault;
517
- }
518
- for (const parameter of protectedHeader.crit) {
519
- if (!recognized.has(parameter)) {
520
- throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`);
521
- }
522
- if (joseHeader[parameter] === undefined) {
523
- throw new Err(`Extension Header Parameter "${parameter}" is missing`);
524
- }
525
- else if (recognized.get(parameter) && protectedHeader[parameter] === undefined) {
526
- throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`);
527
- }
528
- }
529
- return new Set(protectedHeader.crit);
530
- }
531
-
532
- const validateAlgorithms = (option, algorithms) => {
533
- if (algorithms !== undefined &&
534
- (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== 'string'))) {
535
- throw new TypeError(`"${option}" option must be an array of strings`);
536
- }
537
- if (!algorithms) {
538
- return undefined;
539
- }
540
- return new Set(algorithms);
541
- };
542
-
543
- function dsaDigest(alg) {
544
- switch (alg) {
545
- case 'PS256':
546
- case 'RS256':
547
- case 'ES256':
548
- case 'ES256K':
549
- return 'sha256';
550
- case 'PS384':
551
- case 'RS384':
552
- case 'ES384':
553
- return 'sha384';
554
- case 'PS512':
555
- case 'RS512':
556
- case 'ES512':
557
- return 'sha512';
558
- case 'EdDSA':
559
- return undefined;
560
- default:
561
- throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
562
- }
563
- }
564
-
565
- const PSS = {
566
- padding: constants.RSA_PKCS1_PSS_PADDING,
567
- saltLength: constants.RSA_PSS_SALTLEN_DIGEST,
568
- };
569
- const ecCurveAlgMap = new Map([
570
- ['ES256', 'P-256'],
571
- ['ES256K', 'secp256k1'],
572
- ['ES384', 'P-384'],
573
- ['ES512', 'P-521'],
574
- ]);
575
- function keyForCrypto(alg, key) {
576
- switch (alg) {
577
- case 'EdDSA':
578
- if (!['ed25519', 'ed448'].includes(key.asymmetricKeyType)) {
579
- throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be ed25519 or ed448');
580
- }
581
- return key;
582
- case 'RS256':
583
- case 'RS384':
584
- case 'RS512':
585
- if (key.asymmetricKeyType !== 'rsa') {
586
- throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa');
587
- }
588
- checkKeyLength(key, alg);
589
- return key;
590
- case 'PS256':
591
- case 'PS384':
592
- case 'PS512':
593
- if (key.asymmetricKeyType === 'rsa-pss') {
594
- const { hashAlgorithm, mgf1HashAlgorithm, saltLength } = key.asymmetricKeyDetails;
595
- const length = parseInt(alg.slice(-3), 10);
596
- if (hashAlgorithm !== undefined &&
597
- (hashAlgorithm !== `sha${length}` || mgf1HashAlgorithm !== hashAlgorithm)) {
598
- throw new TypeError(`Invalid key for this operation, its RSA-PSS parameters do not meet the requirements of "alg" ${alg}`);
599
- }
600
- if (saltLength !== undefined && saltLength > length >> 3) {
601
- throw new TypeError(`Invalid key for this operation, its RSA-PSS parameter saltLength does not meet the requirements of "alg" ${alg}`);
602
- }
603
- }
604
- else if (key.asymmetricKeyType !== 'rsa') {
605
- throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa or rsa-pss');
606
- }
607
- checkKeyLength(key, alg);
608
- return { key, ...PSS };
609
- case 'ES256':
610
- case 'ES256K':
611
- case 'ES384':
612
- case 'ES512': {
613
- if (key.asymmetricKeyType !== 'ec') {
614
- throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be ec');
615
- }
616
- const actual = getNamedCurve(key);
617
- const expected = ecCurveAlgMap.get(alg);
618
- if (actual !== expected) {
619
- throw new TypeError(`Invalid key curve for the algorithm, its curve must be ${expected}, got ${actual}`);
620
- }
621
- return { dsaEncoding: 'ieee-p1363', key };
622
- }
623
- default:
624
- throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
625
- }
626
- }
627
-
628
- function hmacDigest(alg) {
629
- switch (alg) {
630
- case 'HS256':
631
- return 'sha256';
632
- case 'HS384':
633
- return 'sha384';
634
- case 'HS512':
635
- return 'sha512';
636
- default:
637
- throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
638
- }
639
- }
640
-
641
- function getSignVerifyKey(alg, key, usage) {
642
- if (key instanceof Uint8Array) {
643
- if (!alg.startsWith('HS')) {
644
- throw new TypeError(invalidKeyInput(key, ...types));
645
- }
646
- return createSecretKey(key);
647
- }
648
- if (key instanceof KeyObject) {
649
- return key;
650
- }
651
- if (isCryptoKey(key)) {
652
- checkSigCryptoKey(key, alg, usage);
653
- return KeyObject.from(key);
654
- }
655
- throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array'));
656
- }
657
-
658
- const oneShotSign = promisify(crypto.sign);
659
- const sign = async (alg, key, data) => {
660
- const keyObject = getSignVerifyKey(alg, key, 'sign');
661
- if (alg.startsWith('HS')) {
662
- const hmac = crypto.createHmac(hmacDigest(alg), keyObject);
663
- hmac.update(data);
664
- return hmac.digest();
665
- }
666
- return oneShotSign(dsaDigest(alg), data, keyForCrypto(alg, keyObject));
667
- };
668
-
669
- const oneShotVerify = promisify(crypto.verify);
670
- const verify = async (alg, key, signature, data) => {
671
- const keyObject = getSignVerifyKey(alg, key, 'verify');
672
- if (alg.startsWith('HS')) {
673
- const expected = await sign(alg, keyObject, data);
674
- const actual = signature;
675
- try {
676
- return crypto.timingSafeEqual(actual, expected);
677
- }
678
- catch {
679
- return false;
680
- }
681
- }
682
- const algorithm = dsaDigest(alg);
683
- const keyInput = keyForCrypto(alg, keyObject);
684
- try {
685
- return await oneShotVerify(algorithm, data, keyInput, signature);
686
- }
687
- catch {
688
- return false;
689
- }
690
- };
691
-
692
- async function flattenedVerify(jws, key, options) {
693
- if (!isObject(jws)) {
694
- throw new JWSInvalid('Flattened JWS must be an object');
695
- }
696
- if (jws.protected === undefined && jws.header === undefined) {
697
- throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members');
698
- }
699
- if (jws.protected !== undefined && typeof jws.protected !== 'string') {
700
- throw new JWSInvalid('JWS Protected Header incorrect type');
701
- }
702
- if (jws.payload === undefined) {
703
- throw new JWSInvalid('JWS Payload missing');
704
- }
705
- if (typeof jws.signature !== 'string') {
706
- throw new JWSInvalid('JWS Signature missing or incorrect type');
707
- }
708
- if (jws.header !== undefined && !isObject(jws.header)) {
709
- throw new JWSInvalid('JWS Unprotected Header incorrect type');
710
- }
711
- let parsedProt = {};
712
- if (jws.protected) {
713
- try {
714
- const protectedHeader = decode(jws.protected);
715
- parsedProt = JSON.parse(decoder.decode(protectedHeader));
716
- }
717
- catch {
718
- throw new JWSInvalid('JWS Protected Header is invalid');
719
- }
720
- }
721
- if (!isDisjoint(parsedProt, jws.header)) {
722
- throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');
723
- }
724
- const joseHeader = {
725
- ...parsedProt,
726
- ...jws.header,
727
- };
728
- const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options?.crit, parsedProt, joseHeader);
729
- let b64 = true;
730
- if (extensions.has('b64')) {
731
- b64 = parsedProt.b64;
732
- if (typeof b64 !== 'boolean') {
733
- throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
734
- }
735
- }
736
- const { alg } = joseHeader;
737
- if (typeof alg !== 'string' || !alg) {
738
- throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
739
- }
740
- const algorithms = options && validateAlgorithms('algorithms', options.algorithms);
741
- if (algorithms && !algorithms.has(alg)) {
742
- throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
743
- }
744
- if (b64) {
745
- if (typeof jws.payload !== 'string') {
746
- throw new JWSInvalid('JWS Payload must be a string');
747
- }
748
- }
749
- else if (typeof jws.payload !== 'string' && !(jws.payload instanceof Uint8Array)) {
750
- throw new JWSInvalid('JWS Payload must be a string or an Uint8Array instance');
751
- }
752
- let resolvedKey = false;
753
- if (typeof key === 'function') {
754
- key = await key(parsedProt, jws);
755
- resolvedKey = true;
756
- }
757
- checkKeyType(alg, key, 'verify');
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
- }
766
- const verified = await verify(alg, key, signature, data);
767
- if (!verified) {
768
- throw new JWSSignatureVerificationFailed();
769
- }
770
- let payload;
771
- if (b64) {
772
- try {
773
- payload = decode(jws.payload);
774
- }
775
- catch {
776
- throw new JWSInvalid('Failed to base64url decode the payload');
777
- }
778
- }
779
- else if (typeof jws.payload === 'string') {
780
- payload = encoder.encode(jws.payload);
781
- }
782
- else {
783
- payload = jws.payload;
784
- }
785
- const result = { payload };
786
- if (jws.protected !== undefined) {
787
- result.protectedHeader = parsedProt;
788
- }
789
- if (jws.header !== undefined) {
790
- result.unprotectedHeader = jws.header;
791
- }
792
- if (resolvedKey) {
793
- return { ...result, key };
794
- }
795
- return result;
796
- }
797
-
798
- async function compactVerify(jws, key, options) {
799
- if (jws instanceof Uint8Array) {
800
- jws = decoder.decode(jws);
801
- }
802
- if (typeof jws !== 'string') {
803
- throw new JWSInvalid('Compact JWS must be a string or Uint8Array');
804
- }
805
- const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.');
806
- if (length !== 3) {
807
- throw new JWSInvalid('Invalid Compact JWS');
808
- }
809
- const verified = await flattenedVerify({ payload, protected: protectedHeader, signature }, key, options);
810
- const result = { payload: verified.payload, protectedHeader: verified.protectedHeader };
811
- if (typeof key === 'function') {
812
- return { ...result, key: verified.key };
813
- }
814
- return result;
815
- }
816
-
817
- var epoch = (date) => Math.floor(date.getTime() / 1000);
818
-
819
- const minute = 60;
820
- const hour = minute * 60;
821
- const day = hour * 24;
822
- const week = day * 7;
823
- const year = day * 365.25;
824
- const REGEX = /^(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)$/i;
825
- var secs = (str) => {
826
- const matched = REGEX.exec(str);
827
- if (!matched) {
828
- throw new TypeError('Invalid time period format');
829
- }
830
- const value = parseFloat(matched[1]);
831
- const unit = matched[2].toLowerCase();
832
- switch (unit) {
833
- case 'sec':
834
- case 'secs':
835
- case 'second':
836
- case 'seconds':
837
- case 's':
838
- return Math.round(value);
839
- case 'minute':
840
- case 'minutes':
841
- case 'min':
842
- case 'mins':
843
- case 'm':
844
- return Math.round(value * minute);
845
- case 'hour':
846
- case 'hours':
847
- case 'hr':
848
- case 'hrs':
849
- case 'h':
850
- return Math.round(value * hour);
851
- case 'day':
852
- case 'days':
853
- case 'd':
854
- return Math.round(value * day);
855
- case 'week':
856
- case 'weeks':
857
- case 'w':
858
- return Math.round(value * week);
859
- default:
860
- return Math.round(value * year);
861
- }
862
- };
863
-
864
- const normalizeTyp = (value) => value.toLowerCase().replace(/^application\//, '');
865
- const checkAudiencePresence = (audPayload, audOption) => {
866
- if (typeof audPayload === 'string') {
867
- return audOption.includes(audPayload);
868
- }
869
- if (Array.isArray(audPayload)) {
870
- return audOption.some(Set.prototype.has.bind(new Set(audPayload)));
871
- }
872
- return false;
873
- };
874
- var jwtPayload = (protectedHeader, encodedPayload, options = {}) => {
875
- const { typ } = options;
876
- if (typ &&
877
- (typeof protectedHeader.typ !== 'string' ||
878
- normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
879
- throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', 'typ', 'check_failed');
880
- }
881
- let payload;
882
- try {
883
- payload = JSON.parse(decoder.decode(encodedPayload));
884
- }
885
- catch {
886
- }
887
- if (!isObject(payload)) {
888
- throw new JWTInvalid('JWT Claims Set must be a top-level JSON object');
889
- }
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
- }
904
- if (issuer && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {
905
- throw new JWTClaimValidationFailed('unexpected "iss" claim value', 'iss', 'check_failed');
906
- }
907
- if (subject && payload.sub !== subject) {
908
- throw new JWTClaimValidationFailed('unexpected "sub" claim value', 'sub', 'check_failed');
909
- }
910
- if (audience &&
911
- !checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) {
912
- throw new JWTClaimValidationFailed('unexpected "aud" claim value', 'aud', 'check_failed');
913
- }
914
- let tolerance;
915
- switch (typeof options.clockTolerance) {
916
- case 'string':
917
- tolerance = secs(options.clockTolerance);
918
- break;
919
- case 'number':
920
- tolerance = options.clockTolerance;
921
- break;
922
- case 'undefined':
923
- tolerance = 0;
924
- break;
925
- default:
926
- throw new TypeError('Invalid clockTolerance option type');
927
- }
928
- const { currentDate } = options;
929
- const now = epoch(currentDate || new Date());
930
- if ((payload.iat !== undefined || maxTokenAge) && typeof payload.iat !== 'number') {
931
- throw new JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid');
932
- }
933
- if (payload.nbf !== undefined) {
934
- if (typeof payload.nbf !== 'number') {
935
- throw new JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid');
936
- }
937
- if (payload.nbf > now + tolerance) {
938
- throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', 'nbf', 'check_failed');
939
- }
940
- }
941
- if (payload.exp !== undefined) {
942
- if (typeof payload.exp !== 'number') {
943
- throw new JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid');
944
- }
945
- if (payload.exp <= now - tolerance) {
946
- throw new JWTExpired('"exp" claim timestamp check failed', 'exp', 'check_failed');
947
- }
948
- }
949
- if (maxTokenAge) {
950
- const age = now - payload.iat;
951
- const max = typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge);
952
- if (age - tolerance > max) {
953
- throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', 'iat', 'check_failed');
954
- }
955
- if (age < 0 - tolerance) {
956
- throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed');
957
- }
958
- }
959
- return payload;
960
- };
961
-
962
- async function jwtVerify(jwt, key, options) {
963
- const verified = await compactVerify(jwt, key, options);
964
- if (verified.protectedHeader.crit?.includes('b64') && verified.protectedHeader.b64 === false) {
965
- throw new JWTInvalid('JWTs MUST NOT use unencoded payload');
966
- }
967
- const payload = jwtPayload(verified.protectedHeader, verified.payload, options);
968
- const result = { payload, protectedHeader: verified.protectedHeader };
969
- if (typeof key === 'function') {
970
- return { ...result, key: verified.key };
971
- }
972
- return result;
973
- }
974
-
975
- function getKtyFromAlg(alg) {
976
- switch (typeof alg === 'string' && alg.slice(0, 2)) {
977
- case 'RS':
978
- case 'PS':
979
- return 'RSA';
980
- case 'ES':
981
- return 'EC';
982
- case 'Ed':
983
- return 'OKP';
984
- default:
985
- throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
986
- }
987
- }
988
- function isJWKSLike(jwks) {
989
- return (jwks &&
990
- typeof jwks === 'object' &&
991
- Array.isArray(jwks.keys) &&
992
- jwks.keys.every(isJWKLike));
993
- }
994
- function isJWKLike(key) {
995
- return isObject(key);
996
- }
997
- function clone(obj) {
998
- if (typeof structuredClone === 'function') {
999
- return structuredClone(obj);
1000
- }
1001
- return JSON.parse(JSON.stringify(obj));
1002
- }
1003
- class LocalJWKSet {
1004
- _jwks;
1005
- _cached = new WeakMap();
1006
- constructor(jwks) {
1007
- if (!isJWKSLike(jwks)) {
1008
- throw new JWKSInvalid('JSON Web Key Set malformed');
1009
- }
1010
- this._jwks = clone(jwks);
1011
- }
1012
- async getKey(protectedHeader, token) {
1013
- const { alg, kid } = { ...protectedHeader, ...token?.header };
1014
- const kty = getKtyFromAlg(alg);
1015
- const candidates = this._jwks.keys.filter((jwk) => {
1016
- let candidate = kty === jwk.kty;
1017
- if (candidate && typeof kid === 'string') {
1018
- candidate = kid === jwk.kid;
1019
- }
1020
- if (candidate && typeof jwk.alg === 'string') {
1021
- candidate = alg === jwk.alg;
1022
- }
1023
- if (candidate && typeof jwk.use === 'string') {
1024
- candidate = jwk.use === 'sig';
1025
- }
1026
- if (candidate && Array.isArray(jwk.key_ops)) {
1027
- candidate = jwk.key_ops.includes('verify');
1028
- }
1029
- if (candidate && alg === 'EdDSA') {
1030
- candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448';
1031
- }
1032
- if (candidate) {
1033
- switch (alg) {
1034
- case 'ES256':
1035
- candidate = jwk.crv === 'P-256';
1036
- break;
1037
- case 'ES256K':
1038
- candidate = jwk.crv === 'secp256k1';
1039
- break;
1040
- case 'ES384':
1041
- candidate = jwk.crv === 'P-384';
1042
- break;
1043
- case 'ES512':
1044
- candidate = jwk.crv === 'P-521';
1045
- break;
1046
- }
1047
- }
1048
- return candidate;
1049
- });
1050
- const { 0: jwk, length } = candidates;
1051
- if (length === 0) {
1052
- throw new JWKSNoMatchingKey();
1053
- }
1054
- else if (length !== 1) {
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;
1068
- }
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');
1078
- }
1079
- cached[alg] = key;
1080
- }
1081
- return cached[alg];
1082
- }
1083
-
1084
- const fetchJwks = async (url, timeout, options) => {
1085
- let get;
1086
- switch (url.protocol) {
1087
- case 'https:':
1088
- get = https.get;
1089
- break;
1090
- case 'http:':
1091
- get = http.get;
1092
- break;
1093
- default:
1094
- throw new TypeError('Unsupported URL protocol.');
1095
- }
1096
- const { agent, headers } = options;
1097
- const req = get(url.href, {
1098
- agent,
1099
- timeout,
1100
- headers,
1101
- });
1102
- const [response] = (await Promise.race([once(req, 'response'), once(req, 'timeout')]));
1103
- if (!response) {
1104
- req.destroy();
1105
- throw new JWKSTimeout();
1106
- }
1107
- if (response.statusCode !== 200) {
1108
- throw new JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response');
1109
- }
1110
- const parts = [];
1111
- for await (const part of response) {
1112
- parts.push(part);
1113
- }
1114
- try {
1115
- return JSON.parse(decoder.decode(concat(...parts)));
1116
- }
1117
- catch {
1118
- throw new JOSEError('Failed to parse the JSON Web Key Set HTTP response as JSON');
1119
- }
1120
- };
1121
-
1122
- function isCloudflareWorkers() {
1123
- return (typeof WebSocketPair !== 'undefined' ||
1124
- (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') ||
1125
- (typeof EdgeRuntime !== 'undefined' && EdgeRuntime === 'vercel'));
1126
- }
1127
- class RemoteJWKSet extends LocalJWKSet {
1128
- _url;
1129
- _timeoutDuration;
1130
- _cooldownDuration;
1131
- _cacheMaxAge;
1132
- _jwksTimestamp;
1133
- _pendingFetch;
1134
- _options;
1135
- constructor(url, options) {
1136
- super({ keys: [] });
1137
- this._jwks = undefined;
1138
- if (!(url instanceof URL)) {
1139
- throw new TypeError('url must be an instance of URL');
1140
- }
1141
- this._url = new URL(url.href);
1142
- this._options = { agent: options?.agent, headers: options?.headers };
1143
- this._timeoutDuration =
1144
- typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000;
1145
- this._cooldownDuration =
1146
- typeof options?.cooldownDuration === 'number' ? options?.cooldownDuration : 30000;
1147
- this._cacheMaxAge = typeof options?.cacheMaxAge === 'number' ? options?.cacheMaxAge : 600000;
1148
- }
1149
- coolingDown() {
1150
- return typeof this._jwksTimestamp === 'number'
1151
- ? Date.now() < this._jwksTimestamp + this._cooldownDuration
1152
- : false;
1153
- }
1154
- fresh() {
1155
- return typeof this._jwksTimestamp === 'number'
1156
- ? Date.now() < this._jwksTimestamp + this._cacheMaxAge
1157
- : false;
1158
- }
1159
- async getKey(protectedHeader, token) {
1160
- if (!this._jwks || !this.fresh()) {
1161
- await this.reload();
1162
- }
1163
- try {
1164
- return await super.getKey(protectedHeader, token);
1165
- }
1166
- catch (err) {
1167
- if (err instanceof JWKSNoMatchingKey) {
1168
- if (this.coolingDown() === false) {
1169
- await this.reload();
1170
- return super.getKey(protectedHeader, token);
1171
- }
1172
- }
1173
- throw err;
1174
- }
1175
- }
1176
- async reload() {
1177
- if (this._pendingFetch && isCloudflareWorkers()) {
1178
- this._pendingFetch = undefined;
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
- });
1193
- await this._pendingFetch;
1194
- }
1195
- }
1196
- function createRemoteJWKSet(url, options) {
1197
- const set = new RemoteJWKSet(url, options);
1198
- return async function (protectedHeader, token) {
1199
- return set.getKey(protectedHeader, token);
1200
- };
1201
- }
1202
-
1203
- // https://appleid.apple.com/.well-known/openid-configuration
1204
- const issuer = 'https://appleid.apple.com';
1205
- const authorizationEndpoint = `${issuer}/auth/authorize`;
1206
- const jwksUri = `${issuer}/auth/keys`;
1207
- const defaultMetadata = {
1208
- id: 'apple-universal',
1209
- target: 'apple',
1210
- platform: ConnectorPlatform.Universal,
1211
- name: {
1212
- en: 'Apple',
1213
- 'zh-CN': 'Apple',
1214
- 'tr-TR': 'Apple',
1215
- ko: 'Apple',
1216
- },
1217
- logo: './logo.svg',
1218
- logoDark: './logo-dark.svg',
1219
- description: {
1220
- en: 'Apple is a multinational high-end provider of hardware and software.',
1221
- 'zh-CN': 'Apple 是全球领先的高端消费者软硬件提供商。',
1222
- 'tr-TR': 'Apple, çok uluslu bir üst düzey donanım ve yazılım sağlayıcısıdır.',
1223
- ko: 'Apple은 하드웨어와 소프트웨어의 다국적 공급자 입니다.',
1
+ // src/index.ts
2
+ import { assert } from "@silverhand/essentials";
3
+ import {
4
+ ConnectorError,
5
+ ConnectorErrorCodes,
6
+ validateConfig,
7
+ ConnectorType,
8
+ jsonGuard
9
+ } from "@logto/connector-kit";
10
+ import { generateStandardId } from "@logto/shared/universal";
11
+ import { createRemoteJWKSet, jwtVerify } from "jose";
12
+
13
+ // src/constant.ts
14
+ import { ConnectorPlatform, ConnectorConfigFormItemType } from "@logto/connector-kit";
15
+ var issuer = "https://appleid.apple.com";
16
+ var authorizationEndpoint = `${issuer}/auth/authorize`;
17
+ var accessTokenEndpoint = `${issuer}/auth/token`;
18
+ var jwksUri = `${issuer}/auth/keys`;
19
+ var defaultMetadata = {
20
+ id: "apple-universal",
21
+ target: "apple",
22
+ platform: ConnectorPlatform.Universal,
23
+ name: {
24
+ en: "Apple",
25
+ "zh-CN": "Apple",
26
+ "tr-TR": "Apple",
27
+ ko: "Apple"
28
+ },
29
+ logo: "./logo.svg",
30
+ logoDark: "./logo-dark.svg",
31
+ description: {
32
+ en: "Apple is a multinational high-end provider of hardware and software.",
33
+ "zh-CN": "Apple \u662F\u5168\u7403\u9886\u5148\u7684\u9AD8\u7AEF\u6D88\u8D39\u8005\u8F6F\u786C\u4EF6\u63D0\u4F9B\u5546\u3002",
34
+ "tr-TR": "Apple, \xE7ok uluslu bir \xFCst d\xFCzey donan\u0131m ve yaz\u0131l\u0131m sa\u011Flay\u0131c\u0131s\u0131d\u0131r.",
35
+ ko: "Apple\uC740 \uD558\uB4DC\uC6E8\uC5B4\uC640 \uC18C\uD504\uD2B8\uC6E8\uC5B4\uC758 \uB2E4\uAD6D\uC801 \uACF5\uAE09\uC790 \uC785\uB2C8\uB2E4."
36
+ },
37
+ readme: "./README.md",
38
+ formItems: [
39
+ {
40
+ key: "clientId",
41
+ type: ConnectorConfigFormItemType.Text,
42
+ required: true,
43
+ label: "Identifier",
44
+ placeholder: "<your-registered-identifier>"
1224
45
  },
1225
- readme: './README.md',
1226
- formItems: [
1227
- {
1228
- key: 'clientId',
1229
- type: ConnectorConfigFormItemType.Text,
1230
- required: true,
1231
- label: 'Identifier',
1232
- placeholder: '<your-registered-identifier>',
1233
- },
1234
- {
1235
- key: 'scope',
1236
- type: ConnectorConfigFormItemType.Text,
1237
- required: false,
1238
- label: 'Scope',
1239
- placeholder: 'email name',
1240
- },
1241
- ],
46
+ {
47
+ key: "scope",
48
+ type: ConnectorConfigFormItemType.Text,
49
+ required: false,
50
+ label: "Scope",
51
+ placeholder: "email name"
52
+ }
53
+ ]
1242
54
  };
1243
55
 
1244
- const appleConfigGuard = z.object({
1245
- clientId: z.string(),
1246
- scope: z.string().optional(),
56
+ // src/types.ts
57
+ import { z } from "zod";
58
+ var appleConfigGuard = z.object({
59
+ clientId: z.string(),
60
+ scope: z.string().optional()
1247
61
  });
1248
- const stringToJson = () => z.string().transform((value, ctx) => {
1249
- try {
1250
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
1251
- return JSON.parse(value);
1252
- }
1253
- catch {
1254
- ctx.addIssue({ code: 'custom', message: 'Invalid JSON' });
1255
- return z.NEVER;
1256
- }
62
+ var stringToJson = () => z.string().transform((value, ctx) => {
63
+ try {
64
+ return JSON.parse(value);
65
+ } catch {
66
+ ctx.addIssue({ code: "custom", message: "Invalid JSON" });
67
+ return z.NEVER;
68
+ }
1257
69
  });
1258
- // https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/configuring_your_webpage_for_sign_in_with_apple#3331292
1259
- // https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms#3332113
1260
- const dataGuard = z.object({
1261
- id_token: z.string(),
1262
- user: stringToJson()
1263
- .pipe(z
1264
- .object({
1265
- name: z
1266
- .object({
1267
- firstName: z.string(),
1268
- lastName: z.string(),
1269
- })
1270
- .partial(),
1271
- email: z.string(),
1272
- })
1273
- .partial())
1274
- .optional(),
70
+ var dataGuard = z.object({
71
+ id_token: z.string(),
72
+ user: stringToJson().pipe(
73
+ z.object({
74
+ name: z.object({
75
+ firstName: z.string(),
76
+ lastName: z.string()
77
+ }).partial(),
78
+ email: z.string()
79
+ }).partial()
80
+ ).optional()
1275
81
  });
1276
82
 
1277
- const generateNonce = () => generateStandardId();
1278
- const getAuthorizationUri = (getConfig) => async ({ state, redirectUri }, setSession) => {
1279
- const config = await getConfig(defaultMetadata.id);
1280
- validateConfig(config, appleConfigGuard);
1281
- const nonce = generateNonce();
1282
- const queryParameters = new URLSearchParams({
1283
- client_id: config.clientId,
1284
- redirect_uri: redirectUri,
1285
- scope: config.scope ?? '',
1286
- state,
1287
- nonce,
1288
- // https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms#3332113
1289
- response_type: 'code id_token',
1290
- response_mode: 'form_post',
83
+ // src/index.ts
84
+ var generateNonce = () => generateStandardId();
85
+ var getAuthorizationUri = (getConfig) => async ({ state, redirectUri }, setSession) => {
86
+ const config = await getConfig(defaultMetadata.id);
87
+ validateConfig(config, appleConfigGuard);
88
+ const nonce = generateNonce();
89
+ const queryParameters = new URLSearchParams({
90
+ client_id: config.clientId,
91
+ redirect_uri: redirectUri,
92
+ scope: config.scope ?? "",
93
+ state,
94
+ nonce,
95
+ // https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms#3332113
96
+ response_type: "code id_token",
97
+ response_mode: "form_post"
98
+ });
99
+ assert(
100
+ setSession,
101
+ new ConnectorError(ConnectorErrorCodes.NotImplemented, {
102
+ message: "'setSession' is not implemented."
103
+ })
104
+ );
105
+ await setSession({ nonce });
106
+ return `${authorizationEndpoint}?${queryParameters.toString()}`;
107
+ };
108
+ var getUserInfo = (getConfig) => async (data, getSession) => {
109
+ const { id_token: idToken, user } = await authorizationCallbackHandler(data);
110
+ if (!idToken) {
111
+ throw new ConnectorError(ConnectorErrorCodes.SocialIdTokenInvalid);
112
+ }
113
+ const config = await getConfig(defaultMetadata.id);
114
+ validateConfig(config, appleConfigGuard);
115
+ const { clientId } = config;
116
+ try {
117
+ const { payload } = await jwtVerify(idToken, createRemoteJWKSet(new URL(jwksUri)), {
118
+ issuer,
119
+ audience: clientId
1291
120
  });
1292
- assert(setSession, new ConnectorError(ConnectorErrorCodes.NotImplemented, {
1293
- message: "'setSession' is not implemented.",
1294
- }));
1295
- await setSession({ nonce });
1296
- return `${authorizationEndpoint}?${queryParameters.toString()}`;
1297
- };
1298
- const getUserInfo = (getConfig) => async (data, getSession) => {
1299
- const { id_token: idToken, user } = await authorizationCallbackHandler(data);
1300
- if (!idToken) {
1301
- throw new ConnectorError(ConnectorErrorCodes.SocialIdTokenInvalid);
1302
- }
1303
- const config = await getConfig(defaultMetadata.id);
1304
- validateConfig(config, appleConfigGuard);
1305
- const { clientId } = config;
1306
- try {
1307
- const { payload } = await jwtVerify(idToken, createRemoteJWKSet(new URL(jwksUri)), {
1308
- issuer,
1309
- audience: clientId,
1310
- });
1311
- if (payload.nonce) {
1312
- // TODO @darcy: need to specify error code
1313
- assert(getSession, new ConnectorError(ConnectorErrorCodes.NotImplemented, {
1314
- message: "'getSession' is not implemented.",
1315
- }));
1316
- const { nonce: validationNonce } = await getSession();
1317
- assert(validationNonce, new ConnectorError(ConnectorErrorCodes.General, {
1318
- message: "'nonce' not presented in session storage.",
1319
- }));
1320
- assert(validationNonce === payload.nonce, new ConnectorError(ConnectorErrorCodes.SocialIdTokenInvalid, {
1321
- message: "IdToken validation failed due to 'nonce' mismatch.",
1322
- }));
1323
- }
1324
- if (!payload.sub) {
1325
- throw new ConnectorError(ConnectorErrorCodes.SocialIdTokenInvalid);
1326
- }
1327
- return {
1328
- id: payload.sub,
1329
- // The `user` object is only available at the first sign-in. Didn't find this in Apple's
1330
- // docs but it seems to be the case. Fallback to the `email` field in the ID token just in
1331
- // case.
1332
- // See desperate developer discussion here:
1333
- // https://forums.developer.apple.com/forums/thread/132223
1334
- email: user?.email ??
1335
- (payload.email && payload.email_verified === true ? String(payload.email) : undefined),
1336
- name: [user?.name?.firstName, user?.name?.lastName].filter(Boolean).join(' ') || undefined,
1337
- rawData: jsonGuard.parse(data),
1338
- };
1339
- }
1340
- catch {
1341
- throw new ConnectorError(ConnectorErrorCodes.SocialIdTokenInvalid);
121
+ if (payload.nonce) {
122
+ assert(
123
+ getSession,
124
+ new ConnectorError(ConnectorErrorCodes.NotImplemented, {
125
+ message: "'getSession' is not implemented."
126
+ })
127
+ );
128
+ const { nonce: validationNonce } = await getSession();
129
+ assert(
130
+ validationNonce,
131
+ new ConnectorError(ConnectorErrorCodes.General, {
132
+ message: "'nonce' not presented in session storage."
133
+ })
134
+ );
135
+ assert(
136
+ validationNonce === payload.nonce,
137
+ new ConnectorError(ConnectorErrorCodes.SocialIdTokenInvalid, {
138
+ message: "IdToken validation failed due to 'nonce' mismatch."
139
+ })
140
+ );
1342
141
  }
1343
- };
1344
- const authorizationCallbackHandler = async (parameterObject) => {
1345
- const result = dataGuard.safeParse(parameterObject);
1346
- if (!result.success) {
1347
- throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));
142
+ if (!payload.sub) {
143
+ throw new ConnectorError(ConnectorErrorCodes.SocialIdTokenInvalid);
1348
144
  }
1349
- return result.data;
1350
- };
1351
- const createAppleConnector = async ({ getConfig }) => {
1352
145
  return {
1353
- metadata: defaultMetadata,
1354
- type: ConnectorType.Social,
1355
- configGuard: appleConfigGuard,
1356
- getAuthorizationUri: getAuthorizationUri(getConfig),
1357
- getUserInfo: getUserInfo(getConfig),
146
+ id: payload.sub,
147
+ // The `user` object is only available at the first sign-in. Didn't find this in Apple's
148
+ // docs but it seems to be the case. Fallback to the `email` field in the ID token just in
149
+ // case.
150
+ // See desperate developer discussion here:
151
+ // https://forums.developer.apple.com/forums/thread/132223
152
+ email: user?.email ?? (payload.email && payload.email_verified === true ? String(payload.email) : void 0),
153
+ name: [user?.name?.firstName, user?.name?.lastName].filter(Boolean).join(" ") || void 0,
154
+ rawData: jsonGuard.parse(data)
1358
155
  };
156
+ } catch {
157
+ throw new ConnectorError(ConnectorErrorCodes.SocialIdTokenInvalid);
158
+ }
1359
159
  };
1360
-
1361
- export { createAppleConnector as default };
160
+ var authorizationCallbackHandler = async (parameterObject) => {
161
+ const result = dataGuard.safeParse(parameterObject);
162
+ if (!result.success) {
163
+ throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));
164
+ }
165
+ return result.data;
166
+ };
167
+ var createAppleConnector = async ({ getConfig }) => {
168
+ return {
169
+ metadata: defaultMetadata,
170
+ type: ConnectorType.Social,
171
+ configGuard: appleConfigGuard,
172
+ getAuthorizationUri: getAuthorizationUri(getConfig),
173
+ getUserInfo: getUserInfo(getConfig)
174
+ };
175
+ };
176
+ var src_default = createAppleConnector;
177
+ export {
178
+ src_default as default
179
+ };
180
+ //# sourceMappingURL=index.js.map