@nixxie-cms/auth 1.0.1

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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -0
  3. package/components/Navigation/dist/nixxie-cms-auth-components-Navigation.cjs.d.ts +3 -0
  4. package/components/Navigation/dist/nixxie-cms-auth-components-Navigation.cjs.js +147 -0
  5. package/components/Navigation/dist/nixxie-cms-auth-components-Navigation.esm.js +143 -0
  6. package/components/Navigation/package.json +4 -0
  7. package/dist/declarations/src/components/Navigation.d.ts +6 -0
  8. package/dist/declarations/src/components/Navigation.d.ts.map +1 -0
  9. package/dist/declarations/src/index.d.ts +15 -0
  10. package/dist/declarations/src/index.d.ts.map +1 -0
  11. package/dist/declarations/src/pages/InitPage.d.ts +9 -0
  12. package/dist/declarations/src/pages/InitPage.d.ts.map +1 -0
  13. package/dist/declarations/src/pages/SigninPage.d.ts +9 -0
  14. package/dist/declarations/src/pages/SigninPage.d.ts.map +1 -0
  15. package/dist/declarations/src/types.d.ts +49 -0
  16. package/dist/declarations/src/types.d.ts.map +1 -0
  17. package/dist/nixxie-cms-auth.cjs.d.ts +2 -0
  18. package/dist/nixxie-cms-auth.cjs.js +552 -0
  19. package/dist/nixxie-cms-auth.esm.js +548 -0
  20. package/dist/useFromRedirect-2de239a9.cjs.js +26 -0
  21. package/dist/useFromRedirect-b3deee00.esm.js +24 -0
  22. package/package.json +56 -0
  23. package/pages/InitPage/dist/nixxie-cms-auth-pages-InitPage.cjs.d.ts +3 -0
  24. package/pages/InitPage/dist/nixxie-cms-auth-pages-InitPage.cjs.js +274 -0
  25. package/pages/InitPage/dist/nixxie-cms-auth-pages-InitPage.esm.js +266 -0
  26. package/pages/InitPage/package.json +4 -0
  27. package/pages/SigninPage/dist/nixxie-cms-auth-pages-SigninPage.cjs.d.ts +3 -0
  28. package/pages/SigninPage/dist/nixxie-cms-auth-pages-SigninPage.cjs.js +319 -0
  29. package/pages/SigninPage/dist/nixxie-cms-auth-pages-SigninPage.esm.js +311 -0
  30. package/pages/SigninPage/package.json +4 -0
  31. package/src/components/Navigation.tsx +182 -0
  32. package/src/gql/getBaseAuthSchema.ts +129 -0
  33. package/src/gql/getInitFirstItemSchema.ts +87 -0
  34. package/src/index.ts +291 -0
  35. package/src/lib/useFromRedirect.ts +23 -0
  36. package/src/pages/InitPage.tsx +292 -0
  37. package/src/pages/SigninPage.tsx +331 -0
  38. package/src/schema.ts +84 -0
  39. package/src/templates/config.ts +9 -0
  40. package/src/templates/init.ts +22 -0
  41. package/src/templates/signin.ts +20 -0
  42. package/src/types.ts +57 -0
@@ -0,0 +1,552 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var graphql = require('graphql');
6
+ var core = require('@nixxie-cms/core');
7
+ var password = require('@nixxie-cms/core/fields/types/password');
8
+
9
+ const AUTHENTICATION_FAILURE$1 = {
10
+ code: 'FAILURE',
11
+ message: 'Authentication failed.'
12
+ };
13
+ function getBaseAuthSchema({
14
+ authGqlNames,
15
+ listKey,
16
+ identityField,
17
+ secretField,
18
+ base
19
+ }) {
20
+ const kdf = password.getPasswordFieldKDF(base.schema, listKey, secretField);
21
+ if (!kdf) {
22
+ throw new Error(`${listKey}.${secretField} is not a valid password field.`);
23
+ }
24
+ const ItemAuthenticationWithPasswordSuccess = core.g.object()({
25
+ name: authGqlNames.ItemAuthenticationWithPasswordSuccess,
26
+ fields: {
27
+ sessionToken: core.g.field({
28
+ type: core.g.nonNull(core.g.String)
29
+ }),
30
+ item: core.g.field({
31
+ type: core.g.nonNull(base.object(listKey))
32
+ })
33
+ }
34
+ });
35
+ const ItemAuthenticationWithPasswordFailure = core.g.object()({
36
+ name: authGqlNames.ItemAuthenticationWithPasswordFailure,
37
+ fields: {
38
+ message: core.g.field({
39
+ type: core.g.nonNull(core.g.String)
40
+ })
41
+ }
42
+ });
43
+ const AuthenticationResult = core.g.union({
44
+ name: authGqlNames.ItemAuthenticationWithPasswordResult,
45
+ types: [ItemAuthenticationWithPasswordSuccess, ItemAuthenticationWithPasswordFailure],
46
+ resolveType(val) {
47
+ if ('sessionToken' in val) return authGqlNames.ItemAuthenticationWithPasswordSuccess;
48
+ return authGqlNames.ItemAuthenticationWithPasswordFailure;
49
+ }
50
+ });
51
+ const extension = {
52
+ query: {
53
+ authenticatedItem: core.g.field({
54
+ type: base.object(listKey),
55
+ resolve(rootVal, args, context) {
56
+ const {
57
+ session
58
+ } = context;
59
+ if (!(session !== null && session !== void 0 && session.itemId)) return null;
60
+ return context.db[listKey].findOne({
61
+ where: {
62
+ id: session.itemId
63
+ }
64
+ });
65
+ }
66
+ })
67
+ },
68
+ mutation: {
69
+ endSession: core.g.field({
70
+ type: core.g.nonNull(core.g.Boolean),
71
+ async resolve(rootVal, args, context) {
72
+ var _context$sessionStrat;
73
+ await ((_context$sessionStrat = context.sessionStrategy) === null || _context$sessionStrat === void 0 ? void 0 : _context$sessionStrat.end({
74
+ context
75
+ }));
76
+ return true;
77
+ }
78
+ }),
79
+ [authGqlNames.authenticateItemWithPassword]: core.g.field({
80
+ type: AuthenticationResult,
81
+ args: {
82
+ [identityField]: core.g.arg({
83
+ type: core.g.nonNull(core.g.String)
84
+ }),
85
+ [secretField]: core.g.arg({
86
+ type: core.g.nonNull(core.g.String)
87
+ })
88
+ },
89
+ async resolve(rootVal, {
90
+ [identityField]: identity,
91
+ [secretField]: secret
92
+ }, context) {
93
+ if (!context.sessionStrategy) throw new Error('No session strategy on context');
94
+ const item = await context.sudo().db[listKey].findOne({
95
+ where: {
96
+ [identityField]: identity
97
+ }
98
+ });
99
+ if (typeof (item === null || item === void 0 ? void 0 : item[secretField]) !== 'string') {
100
+ await kdf.hash('simulated-password-to-counter-timing-attack');
101
+ return AUTHENTICATION_FAILURE$1;
102
+ }
103
+ const equal = await kdf.compare(secret, item[secretField]);
104
+ if (!equal) return AUTHENTICATION_FAILURE$1;
105
+ const sessionToken = await context.sessionStrategy.start({
106
+ data: {
107
+ listKey,
108
+ itemId: item.id
109
+ },
110
+ context
111
+ });
112
+ if (typeof sessionToken !== 'string' || sessionToken.length === 0) {
113
+ return AUTHENTICATION_FAILURE$1;
114
+ }
115
+ return {
116
+ sessionToken,
117
+ item
118
+ };
119
+ }
120
+ })
121
+ }
122
+ };
123
+ return {
124
+ extension,
125
+ ItemAuthenticationWithPasswordSuccess
126
+ };
127
+ }
128
+
129
+ const AUTHENTICATION_FAILURE = 'Authentication failed.';
130
+ function getInitFirstItemSchema({
131
+ authGqlNames,
132
+ listKey,
133
+ fields,
134
+ defaultItemData,
135
+ graphQLSchema,
136
+ ItemAuthenticationWithPasswordSuccess
137
+ }) {
138
+ const createInputConfig = graphql.assertInputObjectType(graphQLSchema.getType(`${listKey}CreateInput`)).toConfig();
139
+ const fieldsSet = new Set(fields);
140
+ const initialCreateInput = new graphql.GraphQLInputObjectType({
141
+ ...createInputConfig,
142
+ fields: Object.fromEntries(Object.entries(createInputConfig.fields).filter(([fieldKey]) => fieldsSet.has(fieldKey))),
143
+ name: authGqlNames.CreateInitialInput
144
+ });
145
+ return {
146
+ mutation: {
147
+ [authGqlNames.createInitialItem]: core.g.field({
148
+ type: core.g.nonNull(ItemAuthenticationWithPasswordSuccess),
149
+ args: {
150
+ data: core.g.arg({
151
+ type: core.g.nonNull(initialCreateInput)
152
+ })
153
+ },
154
+ async resolve(rootVal, {
155
+ data
156
+ }, context) {
157
+ if (!context.sessionStrategy) throw new Error('No session strategy on context');
158
+ const sudoContext = context.sudo();
159
+
160
+ // should approximate hasInitFirstItemConditions
161
+ const count = await sudoContext.db[listKey].count();
162
+ if (count !== 0) throw AUTHENTICATION_FAILURE;
163
+
164
+ // Update system state
165
+ // this is strictly speaking incorrect. the db API will do GraphQL coercion on a value which has already been coerced
166
+ // (this is also mostly fine, the chance that people are using things where
167
+ // the input value can't round-trip like the Upload scalar here is quite low)
168
+ const item = await sudoContext.db[listKey].createOne({
169
+ data: {
170
+ ...defaultItemData,
171
+ ...data
172
+ }
173
+ });
174
+ const sessionToken = await context.sessionStrategy.start({
175
+ data: {
176
+ listKey,
177
+ itemId: item.id
178
+ },
179
+ context
180
+ });
181
+ if (typeof sessionToken !== 'string' || sessionToken.length === 0) {
182
+ throw AUTHENTICATION_FAILURE;
183
+ }
184
+ return {
185
+ sessionToken,
186
+ item
187
+ };
188
+ }
189
+ })
190
+ }
191
+ };
192
+ }
193
+
194
+ const getSchemaExtension = ({
195
+ authGqlNames,
196
+ listKey,
197
+ identityField,
198
+ secretField,
199
+ initFirstItem,
200
+ sessionData
201
+ }) => core.g.extend(base => {
202
+ const uniqueWhereInputType = graphql.assertInputObjectType(base.schema.getType(authGqlNames.whereUniqueInputName));
203
+ const identityFieldOnUniqueWhere = uniqueWhereInputType.getFields()[identityField];
204
+ if (base.schema.extensions.sudo && (identityFieldOnUniqueWhere === null || identityFieldOnUniqueWhere === void 0 ? void 0 : identityFieldOnUniqueWhere.type) !== graphql.GraphQLString && (identityFieldOnUniqueWhere === null || identityFieldOnUniqueWhere === void 0 ? void 0 : identityFieldOnUniqueWhere.type) !== graphql.GraphQLID) {
205
+ throw new Error(`createAuth was called with an identityField of ${identityField} on the list ${listKey} ` + `but that field doesn't allow being searched uniquely with a String or ID. ` + `You should likely add \`isIndexed: 'unique'\` ` + `to the field at ${listKey}.${identityField}`);
206
+ }
207
+ const baseSchema = getBaseAuthSchema({
208
+ authGqlNames: authGqlNames,
209
+ identityField,
210
+ listKey,
211
+ secretField,
212
+ base
213
+ });
214
+
215
+ // technically this will incorrectly error if someone has a schema extension that adds a field to the list output type
216
+ // and then wants to fetch that field with `sessionData` but it's extremely unlikely someone will do that since if
217
+ // they want to add a GraphQL field, they'll probably use a virtual field
218
+ const query = `query($id: ID!) { ${authGqlNames.itemQueryName}(where: { id: $id }) { ${sessionData} } }`;
219
+ let ast;
220
+ try {
221
+ ast = graphql.parse(query);
222
+ } catch (err) {
223
+ throw new Error(`The query to get session data has a syntax error, the sessionData option in your createAuth usage is likely incorrect\n${err}`);
224
+ }
225
+ const errors = graphql.validate(base.schema, ast);
226
+ if (errors.length) {
227
+ throw new Error(`The query to get session data has validation errors, the sessionData option in your createAuth usage is likely incorrect\n${errors.join('\n')}`);
228
+ }
229
+ return [baseSchema.extension, initFirstItem && getInitFirstItemSchema({
230
+ authGqlNames,
231
+ listKey,
232
+ fields: initFirstItem.fields,
233
+ defaultItemData: initFirstItem.itemData,
234
+ graphQLSchema: base.schema,
235
+ ItemAuthenticationWithPasswordSuccess: baseSchema.ItemAuthenticationWithPasswordSuccess
236
+ })].filter(x => x !== undefined);
237
+ });
238
+
239
+ function configTemplate ({
240
+ labelField
241
+ }) {
242
+ return `import { type AdminConfig } from '@nixxie-cms/core/types'
243
+ import makeNavigation from '@nixxie-cms/auth/components/Navigation'
244
+
245
+ export const components: AdminConfig['components'] = {
246
+ Navigation: makeNavigation({ labelField: '${labelField}' }),
247
+ }
248
+ `;
249
+ }
250
+
251
+ function signinTemplate ({
252
+ authGqlNames,
253
+ identityField,
254
+ secretField
255
+ }) {
256
+ return `import makeSigninPage from '@nixxie-cms/auth/pages/SigninPage'
257
+
258
+ export default makeSigninPage(${JSON.stringify({
259
+ authGqlNames,
260
+ identityField,
261
+ secretField
262
+ })})
263
+ `;
264
+ }
265
+
266
+ function initTemplate ({
267
+ authGqlNames,
268
+ listKey,
269
+ initFirstItem
270
+ }) {
271
+ return `import makeInitPage from '@nixxie-cms/auth/pages/InitPage'
272
+
273
+ export default makeInitPage(${JSON.stringify({
274
+ listKey,
275
+ authGqlNames,
276
+ fieldPaths: initFirstItem.fields,
277
+ enableWelcome: !initFirstItem.skipNixxieWelcome
278
+ })})
279
+ `;
280
+ }
281
+
282
+ function getAuthGqlNames(singular) {
283
+ const lowerSingularName = singular.charAt(0).toLowerCase() + singular.slice(1);
284
+ return {
285
+ itemQueryName: lowerSingularName,
286
+ whereUniqueInputName: `${singular}WhereUniqueInput`,
287
+ authenticateItemWithPassword: `authenticate${singular}WithPassword`,
288
+ ItemAuthenticationWithPasswordResult: `${singular}AuthenticationWithPasswordResult`,
289
+ ItemAuthenticationWithPasswordSuccess: `${singular}AuthenticationWithPasswordSuccess`,
290
+ ItemAuthenticationWithPasswordFailure: `${singular}AuthenticationWithPasswordFailure`,
291
+ CreateInitialInput: `CreateInitial${singular}Input`,
292
+ createInitialItem: `createInitial${singular}`
293
+ };
294
+ }
295
+
296
+ // TODO: use TypeInfo and listKey for types
297
+ /**
298
+ * createAuth function
299
+ *
300
+ * Generates config for Nixxie to implement standard auth features.
301
+ */
302
+ function createAuth({
303
+ listKey,
304
+ secretField,
305
+ initFirstItem,
306
+ identityField,
307
+ sessionData = 'id'
308
+ }) {
309
+ /**
310
+ * getAdditionalFiles
311
+ *
312
+ * This function adds files to be generated into the Admin UI build. Must be added to the
313
+ * ui.getAdditionalFiles config.
314
+ *
315
+ * The signin page is always included, and the init page is included when initFirstItem is set
316
+ */
317
+ const authGetAdditionalFiles = config => {
318
+ var _listConfig$ui$labelF, _listConfig$ui, _listConfig$graphql$s, _listConfig$graphql;
319
+ // TODO: FIXME: this is a duplication of initialise-lists:747
320
+ const listConfig = config.lists[listKey];
321
+ const labelField = (_listConfig$ui$labelF = (_listConfig$ui = listConfig.ui) === null || _listConfig$ui === void 0 ? void 0 : _listConfig$ui.labelField) !== null && _listConfig$ui$labelF !== void 0 ? _listConfig$ui$labelF : listConfig.fields.label ? 'label' : listConfig.fields.name ? 'name' : listConfig.fields.title ? 'title' : 'id';
322
+ const authGqlNames = getAuthGqlNames((_listConfig$graphql$s = (_listConfig$graphql = listConfig.graphql) === null || _listConfig$graphql === void 0 ? void 0 : _listConfig$graphql.singular) !== null && _listConfig$graphql$s !== void 0 ? _listConfig$graphql$s : listKey);
323
+ const filesToWrite = [{
324
+ mode: 'write',
325
+ src: signinTemplate({
326
+ authGqlNames,
327
+ identityField,
328
+ secretField
329
+ }),
330
+ outputPath: 'pages/signin.js'
331
+ }, {
332
+ mode: 'write',
333
+ src: configTemplate({
334
+ labelField
335
+ }),
336
+ outputPath: 'config.ts'
337
+ }];
338
+ if (initFirstItem) {
339
+ filesToWrite.push({
340
+ mode: 'write',
341
+ src: initTemplate({
342
+ authGqlNames,
343
+ listKey,
344
+ initFirstItem
345
+ }),
346
+ outputPath: 'pages/init.js'
347
+ });
348
+ }
349
+ return filesToWrite;
350
+ };
351
+ function throwIfInvalidConfig(config) {
352
+ if (!(listKey in config.lists)) {
353
+ throw new Error(`withAuth cannot find the list "${listKey}"`);
354
+ }
355
+
356
+ // TODO: verify that the identity field is unique
357
+ // TODO: verify that the field is required
358
+ const list = config.lists[listKey];
359
+ if (!(identityField in list.fields)) {
360
+ throw new Error(`withAuth cannot find the identity field "${listKey}.${identityField}"`);
361
+ }
362
+ if (!(secretField in list.fields)) {
363
+ throw new Error(`withAuth cannot find the secret field "${listKey}.${secretField}"`);
364
+ }
365
+ for (const fieldKey of (initFirstItem === null || initFirstItem === void 0 ? void 0 : initFirstItem.fields) || []) {
366
+ if (fieldKey in list.fields) continue;
367
+ throw new Error(`initFirstItem.fields has unknown field "${listKey}.${fieldKey}"`);
368
+ }
369
+ }
370
+
371
+ // this strategy wraps the existing session strategy,
372
+ // and injects the requested session.data before returning
373
+ function authSessionStrategy(_sessionStrategy) {
374
+ const {
375
+ get,
376
+ ...sessionStrategy
377
+ } = _sessionStrategy;
378
+ return {
379
+ ...sessionStrategy,
380
+ get: async ({
381
+ context
382
+ }) => {
383
+ const session = await get({
384
+ context
385
+ });
386
+ const sudoContext = context.sudo();
387
+ if (!(session !== null && session !== void 0 && session.itemId)) return;
388
+
389
+ // TODO: replace with SessionSecret: HMAC({ listKey, identityField, secretField }, SessionSecretVar)
390
+ // if (session.listKey !== listKey) return null
391
+
392
+ try {
393
+ const data = await sudoContext.query[listKey].findOne({
394
+ where: {
395
+ id: session.itemId
396
+ },
397
+ query: sessionData
398
+ });
399
+ if (!data) return;
400
+ return {
401
+ ...session,
402
+ itemId: session.itemId,
403
+ data
404
+ };
405
+ } catch (e) {
406
+ console.error(e);
407
+ // WARNING: this is probably an invalid configuration
408
+ return;
409
+ }
410
+ }
411
+ };
412
+ }
413
+ async function hasInitFirstItemConditions(context) {
414
+ // do nothing if they aren't using this feature
415
+ if (!initFirstItem) return false;
416
+
417
+ // if they have a session, there is no initialisation necessary
418
+ if (context.session) return false;
419
+ const count = await context.sudo().db[listKey].count({});
420
+ return count === 0;
421
+ }
422
+ async function authMiddleware({
423
+ context,
424
+ wasAccessAllowed,
425
+ basePath
426
+ }) {
427
+ const {
428
+ req
429
+ } = context;
430
+ const {
431
+ pathname
432
+ } = new URL(req.url, 'http://_');
433
+
434
+ // redirect to init if initFirstItem conditions are met
435
+ if (pathname !== `${basePath}/init` && (await hasInitFirstItemConditions(context))) {
436
+ return {
437
+ kind: 'redirect',
438
+ to: `${basePath}/init`
439
+ };
440
+ }
441
+
442
+ // redirect to / if attempting to /init and initFirstItem conditions are not met
443
+ if (pathname === `${basePath}/init` && !(await hasInitFirstItemConditions(context))) {
444
+ return {
445
+ kind: 'redirect',
446
+ to: basePath
447
+ };
448
+ }
449
+
450
+ // don't redirect if we have access
451
+ if (wasAccessAllowed) return;
452
+
453
+ // otherwise, redirect to signin
454
+ return {
455
+ kind: 'redirect',
456
+ to: `${basePath}/signin`
457
+ };
458
+ }
459
+ function defaultIsAccessAllowed({
460
+ session
461
+ }) {
462
+ return session !== undefined;
463
+ }
464
+ function defaultExtendGraphqlSchema(schema) {
465
+ return schema;
466
+ }
467
+
468
+ /**
469
+ * withAuth
470
+ *
471
+ * Automatically extends your configuration with a prescriptive implementation.
472
+ */
473
+ function withAuth(config) {
474
+ var _ui, _listConfig$graphql$s2, _listConfig$graphql2;
475
+ throwIfInvalidConfig(config);
476
+ let {
477
+ ui
478
+ } = config;
479
+ if (!((_ui = ui) !== null && _ui !== void 0 && _ui.isDisabled)) {
480
+ var _ui$basePath, _ui2;
481
+ const {
482
+ getAdditionalFiles = () => [],
483
+ isAccessAllowed = defaultIsAccessAllowed,
484
+ pageMiddleware,
485
+ publicPages = []
486
+ } = ui || {};
487
+ const authPublicPages = [`${(_ui$basePath = (_ui2 = ui) === null || _ui2 === void 0 ? void 0 : _ui2.basePath) !== null && _ui$basePath !== void 0 ? _ui$basePath : ''}/signin`];
488
+ ui = {
489
+ ...ui,
490
+ publicPages: [...publicPages, ...authPublicPages],
491
+ getAdditionalFiles: async () => [...(await getAdditionalFiles()), ...authGetAdditionalFiles(config)],
492
+ isAccessAllowed: async context => {
493
+ if (await hasInitFirstItemConditions(context)) return true;
494
+ return isAccessAllowed(context);
495
+ },
496
+ pageMiddleware: async args => {
497
+ const shouldRedirect = await authMiddleware(args);
498
+ if (shouldRedirect) return shouldRedirect;
499
+ return pageMiddleware === null || pageMiddleware === void 0 ? void 0 : pageMiddleware(args);
500
+ }
501
+ };
502
+ }
503
+ if (!config.session) throw new TypeError('Missing .session configuration');
504
+ const {
505
+ graphql
506
+ } = config;
507
+ const {
508
+ extendGraphqlSchema = defaultExtendGraphqlSchema
509
+ } = graphql !== null && graphql !== void 0 ? graphql : {};
510
+ const listConfig = config.lists[listKey];
511
+
512
+ /**
513
+ * extendGraphqlSchema
514
+ *
515
+ * Must be added to the extendGraphqlSchema config. Can be composed.
516
+ */
517
+ const authGqlNames = getAuthGqlNames((_listConfig$graphql$s2 = (_listConfig$graphql2 = listConfig.graphql) === null || _listConfig$graphql2 === void 0 ? void 0 : _listConfig$graphql2.singular) !== null && _listConfig$graphql$s2 !== void 0 ? _listConfig$graphql$s2 : listKey);
518
+ const authExtendGraphqlSchema = getSchemaExtension({
519
+ authGqlNames,
520
+ listKey,
521
+ identityField,
522
+ secretField,
523
+ initFirstItem,
524
+ sessionData
525
+ });
526
+ return {
527
+ ...config,
528
+ graphql: {
529
+ ...config.graphql,
530
+ extendGraphqlSchema: schema => {
531
+ return extendGraphqlSchema(authExtendGraphqlSchema(schema));
532
+ }
533
+ },
534
+ ui,
535
+ session: authSessionStrategy(config.session),
536
+ lists: {
537
+ ...config.lists,
538
+ [listKey]: {
539
+ ...listConfig,
540
+ fields: {
541
+ ...listConfig.fields
542
+ }
543
+ }
544
+ }
545
+ };
546
+ }
547
+ return {
548
+ withAuth
549
+ };
550
+ }
551
+
552
+ exports.createAuth = createAuth;