@opensaas/keystone-nextjs-auth 25.0.0 → 27.0.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.
@@ -7,7 +7,7 @@ var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProp
7
7
  var _includesInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/includes');
8
8
  var _mapInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/map');
9
9
  var _JSON$stringify = require('@babel/runtime-corejs3/core-js-stable/json/stringify');
10
- require('@babel/runtime-corejs3/core-js-stable/url');
10
+ var _startsWithInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/starts-with');
11
11
  var url = require('url');
12
12
  var react = require('next-auth/react');
13
13
  var jwt = require('next-auth/jwt');
@@ -39,67 +39,19 @@ function _interopNamespace(e) {
39
39
  var _includesInstanceProperty__default = /*#__PURE__*/_interopDefault(_includesInstanceProperty);
40
40
  var _mapInstanceProperty__default = /*#__PURE__*/_interopDefault(_mapInstanceProperty);
41
41
  var _JSON$stringify__default = /*#__PURE__*/_interopDefault(_JSON$stringify);
42
+ var _startsWithInstanceProperty__default = /*#__PURE__*/_interopDefault(_startsWithInstanceProperty);
42
43
  var url__default = /*#__PURE__*/_interopDefault(url);
43
44
  var cookie__namespace = /*#__PURE__*/_interopNamespace(cookie);
44
45
  var ejs__default = /*#__PURE__*/_interopDefault(ejs);
45
46
  var _filterInstanceProperty__default = /*#__PURE__*/_interopDefault(_filterInstanceProperty);
46
47
 
47
48
  const template$1 = `
48
- const Path = require('path');
49
- // @ts-ignore
50
- const withPreconstruct = require('@preconstruct/next');
49
+ const keystoneConfig = require('@keystone-6/core/___internal-do-not-use-will-break-in-patch/admin-ui/next-config').config;
51
50
 
52
- module.exports = withPreconstruct({
53
- typescript: {
54
- ignoreBuildErrors: true,
55
- },
56
- env: {
57
- NEXTAUTH_URL: process.env.NEXTAUTH_URL || 'http://localhost:<%= process.env.PORT || 3000 %><%= keystonePath || '' %>/api/auth',
58
- },
59
- eslint: {
60
- ignoreDuringBuilds: true,
61
- },
62
- webpack(config, { isServer }) {
63
- config.resolve.alias = {
64
- ...config.resolve.alias,
65
- react: Path.dirname(require.resolve('react/package.json')),
66
- 'react-dom': Path.dirname(require.resolve('react-dom/package.json')),
67
- '@keystone-6/core': Path.dirname(
68
- require.resolve('@keystone-6/core/package.json')
69
- ),
70
- };
71
- if (isServer) {
72
- config.externals = [
73
- ...config.externals,
74
- /@keystone-6\\/core(?!\\/___internal-do-not-use-will-break-in-patch\\/admin-ui\\/id-field-view|\\/fields\\/types\\/[^\\/]+\\/views)/,
75
- /.prisma\\/client/
76
- ];
77
- // we need to set these to true so that when __dirname/__filename is used
78
- // to resolve the location of field views, we will get a path that we can use
79
- // rather than just the __dirname/__filename of the generated file.
80
- // https://webpack.js.org/configuration/node/#node__filename
81
- (_config$node = config.node) !== null && _config$node !== void 0 ? _config$node : config.node = {};
82
- config.node.__dirname = true;
83
- config.node.__filename = true;
84
- }
85
- return config;
86
- },
87
- <% if (keystonePath) { %>
88
- <% if (process.env.NODE_ENV != 'production') { %>
89
- async rewrites() {
90
- return [
91
- {
92
- source: '/api/__keystone_api_build',
93
- destination: 'http://localhost:<%= process.env.PORT || 3000 %><%= keystonePath || '' %>/api/__keystone_api_build',
94
- basePath: false
95
- }
96
- ];
97
- },
98
- <% }%>
99
- basePath: '<%= keystonePath || '' %>'
100
- <% } %>
101
- });
102
- `;
51
+ module.exports = {
52
+ ...keystoneConfig,
53
+ basePath: '<%= keystonePath || '' %>'
54
+ };`;
103
55
  const nextConfigTemplate = _ref => {
104
56
  let {
105
57
  keystonePath
@@ -123,17 +75,14 @@ function getBaseAuthSchema(_ref) {
123
75
  types: [base.object(listKey)],
124
76
  resolveType: (root, context) => {
125
77
  var _context$session;
126
-
127
78
  return (_context$session = context.session) === null || _context$session === void 0 ? void 0 : _context$session.listKey;
128
79
  }
129
80
  }),
130
-
131
81
  resolve(root, args, _ref2) {
132
82
  let {
133
83
  session,
134
84
  db
135
85
  } = _ref2;
136
-
137
86
  if (typeof (session === null || session === void 0 ? void 0 : session.itemId) === 'string' && typeof session.listKey === 'string') {
138
87
  return db[session.listKey].findOne({
139
88
  where: {
@@ -141,10 +90,8 @@ function getBaseAuthSchema(_ref) {
141
90
  }
142
91
  });
143
92
  }
144
-
145
93
  return null;
146
94
  }
147
-
148
95
  })
149
96
  }
150
97
  };
@@ -159,7 +106,6 @@ const getSchemaExtension = _ref => {
159
106
  } = _ref;
160
107
  return core.graphql.extend(base => {
161
108
  var _context;
162
-
163
109
  const baseSchema = getBaseAuthSchema({
164
110
  listKey,
165
111
  base
@@ -209,6 +155,7 @@ const authTemplate = _ref => {
209
155
  };
210
156
 
211
157
  const _excluded = ["get", "end"];
158
+
212
159
  /**
213
160
  * createAuth function
214
161
  *
@@ -232,6 +179,7 @@ function createAuth(_ref) {
232
179
  // part of the createAuth API (in which case its use cases need to be documented and tested)
233
180
  // or whether always being true is what we want, in which case we can refactor our code
234
181
  // to match this. -TL
182
+
235
183
  const customPath = !keystonePath || keystonePath === '/' ? '' : keystonePath;
236
184
  /**
237
185
  * pageMiddleware
@@ -243,40 +191,25 @@ function createAuth(_ref) {
243
191
  * - to the init page when initFirstItem is configured, and there are no user in the database
244
192
  * - to the signin page when no valid session is present
245
193
  */
246
-
247
- const pageMiddleware = async _ref2 => {
194
+ const authMiddleware = async _ref2 => {
248
195
  let {
249
196
  context,
250
- isValidSession
197
+ wasAccessAllowed
251
198
  } = _ref2;
252
199
  const {
253
200
  req,
254
201
  session
255
202
  } = context;
256
203
  const pathname = url__default["default"].parse(req === null || req === void 0 ? void 0 : req.url).pathname;
257
-
258
- if (isValidSession) {
259
- if (pathname === `${customPath}/api/auth/signin` || pages !== null && pages !== void 0 && pages.signIn && _includesInstanceProperty__default["default"](pathname).call(pathname, pages === null || pages === void 0 ? void 0 : pages.signIn)) {
260
- return {
261
- kind: 'redirect',
262
- to: `${customPath}`
263
- };
264
- }
265
-
204
+ if (wasAccessAllowed) {
266
205
  if (customPath !== '' && pathname === '/') {
267
206
  return {
268
207
  kind: 'redirect',
269
208
  to: `${customPath}`
270
209
  };
271
210
  }
272
-
273
- return;
274
- }
275
-
276
- if (_includesInstanceProperty__default["default"](pathname).call(pathname, '/_next/') || _includesInstanceProperty__default["default"](pathname).call(pathname, '/api/auth/') || pages !== null && pages !== void 0 && pages.signIn && _includesInstanceProperty__default["default"](pathname).call(pathname, pages === null || pages === void 0 ? void 0 : pages.signIn) || pages !== null && pages !== void 0 && pages.error && _includesInstanceProperty__default["default"](pathname).call(pathname, pages === null || pages === void 0 ? void 0 : pages.error) || pages !== null && pages !== void 0 && pages.signOut && _includesInstanceProperty__default["default"](pathname).call(pathname, pages === null || pages === void 0 ? void 0 : pages.signOut)) {
277
211
  return;
278
212
  }
279
-
280
213
  if (!session && !_includesInstanceProperty__default["default"](pathname).call(pathname, `${customPath}/api/auth/`)) {
281
214
  return {
282
215
  kind: 'redirect',
@@ -284,17 +217,16 @@ function createAuth(_ref) {
284
217
  };
285
218
  }
286
219
  };
220
+
287
221
  /**
288
- * getAdditionalFiles
222
+ * authGetAdditionalFiles
289
223
  *
290
224
  * This function adds files to be generated into the Admin UI build. Must be added to the
291
225
  * ui.getAdditionalFiles config.
292
226
  *
293
227
  * The signin page is always included, and the init page is included when initFirstItem is set
294
228
  */
295
-
296
-
297
- const getAdditionalFiles = () => {
229
+ const authGetAdditionalFiles = () => {
298
230
  const filesToWrite = [{
299
231
  mode: 'write',
300
232
  outputPath: 'pages/api/auth/[...nextauth].js',
@@ -314,102 +246,91 @@ function createAuth(_ref) {
314
246
  }];
315
247
  return filesToWrite;
316
248
  };
249
+
317
250
  /**
318
251
  * publicAuthPages
319
252
  *
320
253
  * Must be added to the ui.publicPages config
321
254
  */
322
-
323
-
324
- const publicPages = [`${customPath}/api/__keystone_api_build`, `${customPath}/api/auth/csrf`, `${customPath}/api/auth/signin`, `${customPath}/api/auth/callback`, `${customPath}/api/auth/session`, `${customPath}/api/auth/providers`, `${customPath}/api/auth/signout`, `${customPath}/api/auth/error`]; // TODO: Add Provider Types
255
+ const authPublicPages = [`${customPath}/api/auth/csrf`, `${customPath}/api/auth/signin`, `${customPath}/api/auth/callback`, `${customPath}/api/auth/session`, `${customPath}/api/auth/providers`, `${customPath}/api/auth/signout`, `${customPath}/api/auth/error`];
256
+ // TODO: Add Provider Types
325
257
  // @ts-ignore
326
-
327
258
  function addPages(provider) {
328
259
  const name = provider.id;
329
- publicPages.push(`${customPath}/api/auth/signin/${name}`);
330
- publicPages.push(`${customPath}/api/auth/callback/${name}`);
260
+ authPublicPages.push(`${customPath}/api/auth/signin/${name}`);
261
+ authPublicPages.push(`${customPath}/api/auth/callback/${name}`);
331
262
  }
332
-
333
263
  _mapInstanceProperty__default["default"](providers).call(providers, addPages);
264
+
334
265
  /**
335
266
  * extendGraphqlSchema
336
267
  *
337
268
  * Must be added to the extendGraphqlSchema config. Can be composed.
338
269
  */
339
-
340
-
341
270
  const extendGraphqlSchema = getSchemaExtension({
342
271
  identityField,
343
272
  listKey
344
273
  });
274
+
345
275
  /**
346
276
  * validateConfig
347
277
  *
348
278
  * Validates the provided auth config; optional step when integrating auth
349
279
  */
350
-
351
280
  const validateConfig = keystoneConfig => {
352
281
  const listConfig = keystoneConfig.lists[listKey];
353
-
354
282
  if (listConfig === undefined) {
355
283
  const msg = `A createAuth() invocation specifies the list "${listKey}" but no list with that key has been defined.`;
356
284
  throw new Error(msg);
357
- } // TODO: Check if providers
285
+ }
286
+
287
+ // TODO: Check if providers
358
288
  // TODO: Check other required commands/data
289
+
359
290
  // TODO: Check for String-like typing for identityField? How?
360
291
  // TODO: Validate that the identifyField is unique.
361
292
  // TODO: If this field isn't required, what happens if I try to log in as `null`?
362
-
363
-
364
293
  const identityFieldConfig = listConfig.fields[identityField];
365
-
366
294
  if (identityFieldConfig === undefined) {
367
295
  const identityFieldName = _JSON$stringify__default["default"](identityField);
368
-
369
296
  const msg = `A createAuth() invocation for the "${listKey}" list specifies ${identityFieldName} as its identityField but no field with that key exists on the list.`;
370
297
  throw new Error(msg);
371
298
  }
372
299
  };
300
+
373
301
  /**
374
302
  * withItemData
375
303
  *
376
304
  * Automatically injects a session.data value with the authenticated item
377
305
  */
378
-
379
306
  /* TODO:
380
307
  - [ ] We could support additional where input to validate item sessions (e.g an isEnabled boolean)
381
308
  */
382
-
383
-
384
309
  const withItemData = _sessionStrategy => {
385
310
  const {
386
- get,
387
- end
388
- } = _sessionStrategy,
389
- sessionStrategy = _objectWithoutProperties(_sessionStrategy, _excluded);
390
-
311
+ get,
312
+ end
313
+ } = _sessionStrategy,
314
+ sessionStrategy = _objectWithoutProperties(_sessionStrategy, _excluded);
391
315
  return _objectSpread(_objectSpread({}, sessionStrategy), {}, {
392
316
  get: async _ref3 => {
393
317
  var _req$headers, _req$headers$authoriz;
394
-
395
318
  let {
396
- req,
397
- createContext
319
+ context
398
320
  } = _ref3;
321
+ const {
322
+ req
323
+ } = context;
399
324
  const pathname = url__default["default"].parse(req === null || req === void 0 ? void 0 : req.url).pathname;
400
325
  let nextSession;
401
-
326
+ if (!req) return;
402
327
  if (_includesInstanceProperty__default["default"](pathname).call(pathname, '/api/auth')) {
403
328
  return;
404
329
  }
405
-
406
- const sudoContext = createContext({
407
- sudo: true
408
- });
409
-
330
+ const sudoContext = context.sudo();
410
331
  if (((_req$headers = req.headers) === null || _req$headers === void 0 ? void 0 : (_req$headers$authoriz = _req$headers.authorization) === null || _req$headers$authoriz === void 0 ? void 0 : _req$headers$authoriz.split(' ')[0]) === 'Bearer') {
411
332
  nextSession = await jwt.getToken({
412
- req,
333
+ req: req,
413
334
  secret: sessionSecret
414
335
  });
415
336
  } else {
@@ -417,11 +338,9 @@ function createAuth(_ref) {
417
338
  req
418
339
  });
419
340
  }
420
-
421
341
  if (!nextSession || !nextSession.listKey || nextSession.listKey !== listKey || !nextSession.itemId || !sudoContext.query[listKey] || !nextSession.itemId) {
422
342
  return;
423
343
  }
424
-
425
344
  const reqWithUser = req;
426
345
  reqWithUser.user = {
427
346
  istKey: nextSession.listKey,
@@ -429,8 +348,7 @@ function createAuth(_ref) {
429
348
  data: nextSession.data
430
349
  };
431
350
  const userSession = await get({
432
- req: reqWithUser,
433
- createContext
351
+ context
434
352
  });
435
353
  return _objectSpread(_objectSpread(_objectSpread({}, userSession), nextSession), {}, {
436
354
  data: nextSession.data,
@@ -440,16 +358,17 @@ function createAuth(_ref) {
440
358
  },
441
359
  end: async _ref4 => {
442
360
  let {
443
- res,
444
- req,
445
- createContext
361
+ context
446
362
  } = _ref4;
447
363
  await end({
448
- res,
449
- req,
450
- createContext
364
+ context
451
365
  });
452
366
  const TOKEN_NAME = '__Secure-next-auth.session-token' ;
367
+ const {
368
+ req,
369
+ res
370
+ } = context;
371
+ if (!req || !res) return;
453
372
  res.setHeader('Set-Cookie', cookie__namespace.serialize(TOKEN_NAME, '', {
454
373
  maxAge: 0,
455
374
  expires: new Date(),
@@ -463,6 +382,12 @@ function createAuth(_ref) {
463
382
  }
464
383
  });
465
384
  };
385
+ function defaultIsAccessAllowed(_ref5) {
386
+ let {
387
+ session
388
+ } = _ref5;
389
+ return session !== undefined;
390
+ }
466
391
  /**
467
392
  * withAuth
468
393
  *
@@ -473,42 +398,37 @@ function createAuth(_ref) {
473
398
  * It validates the auth config against the provided keystone config, and preserves existing
474
399
  * config by composing existing extendGraphqlSchema functions and ui config.
475
400
  */
476
-
477
-
478
401
  const withAuth = keystoneConfig => {
402
+ var _ui;
479
403
  validateConfig(keystoneConfig);
480
404
  let {
481
405
  ui
482
406
  } = keystoneConfig;
483
-
484
- if (keystoneConfig.ui) {
485
- var _keystoneConfig$ui;
486
-
487
- ui = _objectSpread(_objectSpread({}, keystoneConfig.ui), {}, {
488
- publicPages: [...(keystoneConfig.ui.publicPages || []), ...publicPages],
489
- getAdditionalFiles: [...(((_keystoneConfig$ui = keystoneConfig.ui) === null || _keystoneConfig$ui === void 0 ? void 0 : _keystoneConfig$ui.getAdditionalFiles) || []), getAdditionalFiles],
490
- pageMiddleware: async args => {
491
- var _keystoneConfig$ui2, _keystoneConfig$ui2$p;
492
-
493
- return (await pageMiddleware(args)) ?? (keystoneConfig === null || keystoneConfig === void 0 ? void 0 : (_keystoneConfig$ui2 = keystoneConfig.ui) === null || _keystoneConfig$ui2 === void 0 ? void 0 : (_keystoneConfig$ui2$p = _keystoneConfig$ui2.pageMiddleware) === null || _keystoneConfig$ui2$p === void 0 ? void 0 : _keystoneConfig$ui2$p.call(_keystoneConfig$ui2, args));
494
- },
407
+ if (!((_ui = ui) !== null && _ui !== void 0 && _ui.isDisabled)) {
408
+ const {
409
+ getAdditionalFiles = [],
410
+ isAccessAllowed = defaultIsAccessAllowed,
411
+ pageMiddleware,
412
+ publicPages = []
413
+ } = ui || {};
414
+ ui = _objectSpread(_objectSpread({}, ui), {}, {
415
+ publicPages: [...publicPages, ...authPublicPages],
495
416
  isAccessAllowed: async context => {
496
- var _keystoneConfig$ui3;
497
-
498
- const {
499
- req
500
- } = context;
501
- const pathname = url__default["default"].parse(req === null || req === void 0 ? void 0 : req.url).pathname; // Allow nextjs scripts and static files to be accessed without auth
502
-
503
- if (_includesInstanceProperty__default["default"](pathname).call(pathname, '/_next/')) {
417
+ var _context$req;
418
+ const pathname = url__default["default"].parse((_context$req = context.req) === null || _context$req === void 0 ? void 0 : _context$req.url).pathname;
419
+ if (_startsWithInstanceProperty__default["default"](pathname).call(pathname, `${customPath}/_next`) || _startsWithInstanceProperty__default["default"](pathname).call(pathname, `${customPath}/__next`) || _startsWithInstanceProperty__default["default"](pathname).call(pathname, `${customPath}/api/auth/`) || pages !== null && pages !== void 0 && pages.signIn && _includesInstanceProperty__default["default"](pathname).call(pathname, pages === null || pages === void 0 ? void 0 : pages.signIn) || pages !== null && pages !== void 0 && pages.error && _includesInstanceProperty__default["default"](pathname).call(pathname, pages === null || pages === void 0 ? void 0 : pages.error) || pages !== null && pages !== void 0 && pages.signOut && _includesInstanceProperty__default["default"](pathname).call(pathname, pages === null || pages === void 0 ? void 0 : pages.signOut)) {
504
420
  return true;
505
- } // Allow keystone to access /api/__keystone_api_build for hot reloading
506
-
507
- return (_keystoneConfig$ui3 = keystoneConfig.ui) !== null && _keystoneConfig$ui3 !== void 0 && _keystoneConfig$ui3.isAccessAllowed ? keystoneConfig.ui.isAccessAllowed(context) : context.session !== undefined;
421
+ }
422
+ return await isAccessAllowed(context);
423
+ },
424
+ getAdditionalFiles: [...getAdditionalFiles, authGetAdditionalFiles],
425
+ pageMiddleware: async args => {
426
+ const shouldRedirect = await authMiddleware(args);
427
+ if (shouldRedirect) return shouldRedirect;
428
+ return pageMiddleware === null || pageMiddleware === void 0 ? void 0 : pageMiddleware(args);
508
429
  }
509
430
  });
510
431
  }
511
-
512
432
  if (!keystoneConfig.session) throw new TypeError('Missing .session configuration');
513
433
  const session = withItemData(keystoneConfig.session);
514
434
  const existingExtendGraphQLSchema = keystoneConfig.extendGraphqlSchema;
@@ -523,16 +443,15 @@ function createAuth(_ref) {
523
443
  extendGraphqlSchema: existingExtendGraphQLSchema ? schema => existingExtendGraphQLSchema(extendGraphqlSchema(schema)) : extendGraphqlSchema
524
444
  });
525
445
  };
526
-
527
446
  return {
528
- withAuth // In the future we may want to return the following so that developers can
447
+ withAuth
448
+ // In the future we may want to return the following so that developers can
529
449
  // roll their own. This is pending a review of the use cases this might be
530
450
  // appropriate for, along with documentation and testing.
531
451
  // ui: { enableSessionItem: true, pageMiddleware, getAdditionalFiles, publicPages },
532
452
  // fields,
533
453
  // extendGraphqlSchema,
534
454
  // validateConfig,
535
-
536
455
  };
537
456
  }
538
457