@opensaas/keystone-nextjs-auth 25.0.0 → 27.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +27 -0
- package/README.md +15 -13
- package/dist/declarations/src/pages/NextAuthPage.d.ts +3 -3
- package/dist/declarations/src/types/index.d.ts +15 -23
- package/dist/opensaas-keystone-nextjs-auth.cjs.dev.js +74 -161
- package/dist/opensaas-keystone-nextjs-auth.cjs.prod.js +75 -156
- package/dist/opensaas-keystone-nextjs-auth.esm.js +73 -160
- package/package.json +7 -7
- package/pages/NextAuthPage/dist/opensaas-keystone-nextjs-auth-pages-NextAuthPage.cjs.dev.js +9 -30
- package/pages/NextAuthPage/dist/opensaas-keystone-nextjs-auth-pages-NextAuthPage.cjs.prod.js +9 -30
- package/pages/NextAuthPage/dist/opensaas-keystone-nextjs-auth-pages-NextAuthPage.esm.js +9 -30
- package/src/index.ts +49 -56
- package/src/pages/NextAuthPage.tsx +8 -5
- package/src/templates/next-config.ts +5 -54
- package/src/types/index.ts +4 -19
- package/src/types/next-auth.d.ts +13 -6
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,32 @@
|
|
1
1
|
# @opensaas-keystone/nextjs-auth
|
2
2
|
|
3
|
+
## 27.0.0
|
4
|
+
|
5
|
+
### Major Changes
|
6
|
+
|
7
|
+
- ec29144: Update dependency @keystone-6/core to v5
|
8
|
+
|
9
|
+
### Patch Changes
|
10
|
+
|
11
|
+
- 0a8ea8e: Update patch dependencies (patch)
|
12
|
+
|
13
|
+
## 26.0.0
|
14
|
+
|
15
|
+
### Major Changes
|
16
|
+
|
17
|
+
- fac7086: Upgrade to `keystone-6/core@4.0.0`
|
18
|
+
|
19
|
+
### Minor Changes
|
20
|
+
|
21
|
+
- ed67215: Update dependency next-auth to ^4.17.0
|
22
|
+
- 9643191: Upgrade next-auth and fix types
|
23
|
+
- b9dbb77: Upgrade to keystone `3.1.0`
|
24
|
+
|
25
|
+
### Patch Changes
|
26
|
+
|
27
|
+
- b9dbb77: Resolve error when `isAccessAllowed` is not defined
|
28
|
+
- 5b0af33: Simplify NextJS config of Keystone path and resolve redirect loops
|
29
|
+
|
3
30
|
## 25.0.0
|
4
31
|
|
5
32
|
### Major Changes
|
package/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# Keystone next auth
|
2
|
+
|
2
3
|
This package enables the addition of social auth to keystone-6.
|
3
4
|
|
4
5
|
## Contents
|
@@ -9,6 +10,7 @@ This package enables the addition of social auth to keystone-6.
|
|
9
10
|
- [Contributing](#contributing)
|
10
11
|
|
11
12
|
## About
|
13
|
+
|
12
14
|
This uses NextAuth.js (https://next-auth.js.org/) project to add social auth to Keystone-6 (https://keystonejs.com/). Primary testing has been done with Auth0, happy for others to test other providers/give feedback or send through a PR.
|
13
15
|
|
14
16
|
## Adding to your project
|
@@ -20,7 +22,6 @@ Add import...
|
|
20
22
|
```javascript
|
21
23
|
import { createAuth } from '@opensaas/keystone-nextjs-auth';
|
22
24
|
import Auth0 from '@opensaas/keystone-nextjs-auth/providers/auth0';
|
23
|
-
|
24
25
|
```
|
25
26
|
|
26
27
|
Add you Auth configuration including providers
|
@@ -60,7 +61,8 @@ const auth = createAuth({
|
|
60
61
|
]
|
61
62
|
});
|
62
63
|
```
|
63
|
-
|
64
|
+
|
65
|
+
Wrap your keystone config in `auth.withAuth`.
|
64
66
|
|
65
67
|
```javascript
|
66
68
|
export default auth.withAuth(
|
@@ -69,32 +71,32 @@ export default auth.withAuth(
|
|
69
71
|
db: {},
|
70
72
|
ui: {},
|
71
73
|
lists,
|
72
|
-
experimental: {
|
73
|
-
generateNodeAPI: true,
|
74
|
-
},
|
75
74
|
...
|
76
75
|
});
|
77
76
|
```
|
78
77
|
|
79
78
|
## Configuration
|
79
|
+
|
80
80
|
Provider configuration see https://next-auth.js.org/configuration/providers.
|
81
81
|
For Keystone-6 Configuration see https://keystonejs.com/
|
82
82
|
for example see the example [backend](./backend)
|
83
83
|
|
84
|
-
-
|
85
|
-
-
|
86
|
-
-
|
87
|
-
-
|
88
|
-
-
|
89
|
-
-
|
90
|
-
-
|
91
|
-
-
|
84
|
+
- listKey - the list for authentication (generally `'User'`). Make sure any required fields are set using the `*Map` fields, see note below.
|
85
|
+
- identityField - The field that stores the identity/subjectId in keystone (generally `'subjectId'`). You will need to add this field to your list schema specified by `listKey`. An example can be found [here](./backend/schemas/User.ts).
|
86
|
+
- sessionData - Data to be stored in the session ( something like `'id name email'`),
|
87
|
+
- autoCreate - boolean to autocreate a user when they log in
|
88
|
+
- userMap: `key:value` pairs that define what is copied from the User object returned from NextAuth in the SignIn callback (https://next-auth.js.org/configuration/callbacks#sign-in-callback) Left side is Keystone side, right is what comes from NextAuth eg: `{ subjectId: 'id', name: 'name' }`
|
89
|
+
- accountMap - As Above but for the Account object
|
90
|
+
- profileMap - As Above but for the Profile object
|
91
|
+
- keystonePath - the path you want to access keystone from your frontend app (if required).
|
92
92
|
|
93
93
|
Note: The Keystone `create-keystone-app` CLI app (generally run with `yarn create keystone-app`/`npm init keystone-app`) will set a required `password` field on the `User` list. If you've used this to set up your project you will need to modify your list schema to set the field as not required, or remove it entirely if you don't plan to use the default Keystone auth system at all.
|
94
94
|
|
95
95
|
## Contributing
|
96
|
+
|
96
97
|
If you want to run this package locally
|
97
98
|
After cloning run `yarn install` and either:
|
99
|
+
|
98
100
|
- `yarn dev` to run both the frontend and backend or
|
99
101
|
- `yarn dev:backend` for just the backend
|
100
102
|
|
@@ -2,14 +2,14 @@ import { CookiesOptions, EventCallbacks, PagesOptions } from 'next-auth';
|
|
2
2
|
import type { KeystoneListsAPI } from '@keystone-6/core/types';
|
3
3
|
import { Provider } from 'next-auth/providers';
|
4
4
|
import { JWTOptions } from 'next-auth/jwt';
|
5
|
-
export
|
5
|
+
export type NextAuthTemplateProps = {
|
6
6
|
autoCreate: boolean;
|
7
7
|
identityField: string;
|
8
8
|
listKey: string;
|
9
9
|
sessionData: string | undefined;
|
10
10
|
sessionSecret: string;
|
11
11
|
};
|
12
|
-
export
|
12
|
+
export type CoreNextAuthPageProps = {
|
13
13
|
cookies?: Partial<CookiesOptions>;
|
14
14
|
events?: Partial<EventCallbacks>;
|
15
15
|
jwt?: Partial<JWTOptions>;
|
@@ -23,7 +23,7 @@ export declare type CoreNextAuthPageProps = {
|
|
23
23
|
[key: string]: boolean | string | number;
|
24
24
|
}>;
|
25
25
|
} & NextAuthTemplateProps;
|
26
|
-
export
|
26
|
+
export type NextAuthPageProps = CoreNextAuthPageProps & {
|
27
27
|
query: KeystoneListsAPI<any>;
|
28
28
|
};
|
29
29
|
export default function NextAuthPage(props: NextAuthPageProps): any;
|
@@ -1,37 +1,29 @@
|
|
1
|
-
/// <reference types="node" />
|
2
|
-
import type { ServerResponse, IncomingMessage } from 'http';
|
3
|
-
import type { NextRequest } from 'next/server';
|
4
1
|
import { Provider } from 'next-auth/providers';
|
5
2
|
import { CookiesOptions, PagesOptions } from 'next-auth';
|
6
|
-
import { BaseListTypeInfo, KeystoneConfig,
|
7
|
-
declare type NextAuthResponse = IncomingMessage & NextRequest;
|
3
|
+
import { BaseListTypeInfo, KeystoneConfig, KeystoneContext } from '@keystone-6/core/types';
|
8
4
|
export declare type AuthSessionStrategy<StoredSessionData> = {
|
9
5
|
start: (args: {
|
10
|
-
res: ServerResponse;
|
11
6
|
data: any;
|
12
|
-
|
13
|
-
}) => Promise<
|
7
|
+
context: KeystoneContext;
|
8
|
+
}) => Promise<unknown>;
|
14
9
|
end: (args: {
|
15
|
-
|
16
|
-
|
17
|
-
createContext: CreateContext;
|
18
|
-
}) => Promise<void>;
|
10
|
+
context: KeystoneContext;
|
11
|
+
}) => Promise<unknown>;
|
19
12
|
get: (args: {
|
20
|
-
|
21
|
-
createContext: CreateContext;
|
13
|
+
context: KeystoneContext;
|
22
14
|
}) => Promise<StoredSessionData | undefined>;
|
23
15
|
};
|
24
|
-
export
|
25
|
-
|
16
|
+
export type NextAuthProviders = Provider[];
|
17
|
+
type KeytoneOAuthOptions = {
|
26
18
|
providers: NextAuthProviders;
|
27
19
|
pages?: Partial<PagesOptions>;
|
28
20
|
};
|
29
|
-
|
21
|
+
type NextAuthOptions = {
|
30
22
|
cookies?: Partial<CookiesOptions>;
|
31
23
|
resolver: any;
|
32
24
|
};
|
33
|
-
export
|
34
|
-
export
|
25
|
+
export type KeystoneOAuthConfig = KeystoneConfig & KeytoneOAuthOptions & NextAuthOptions;
|
26
|
+
export type AuthConfig<GeneratedListTypes extends BaseListTypeInfo> = {
|
35
27
|
/** Auth Create users in Keystone DB from Auth Provider */
|
36
28
|
autoCreate: boolean;
|
37
29
|
/** Adds ability to customize cookie options, for example, to facilitate cross-subdomain functionality */
|
@@ -58,8 +50,8 @@ export declare type AuthConfig<GeneratedListTypes extends BaseListTypeInfo> = {
|
|
58
50
|
/** Next-Auth Session Secret */
|
59
51
|
sessionSecret: string;
|
60
52
|
};
|
61
|
-
export
|
62
|
-
export
|
63
|
-
export
|
64
|
-
export
|
53
|
+
export type AuthTokenRequestErrorCode = 'IDENTITY_NOT_FOUND' | 'MULTIPLE_IDENTITY_MATCHES';
|
54
|
+
export type PasswordAuthErrorCode = AuthTokenRequestErrorCode | 'FAILURE' | 'SECRET_NOT_SET' | 'SECRET_MISMATCH';
|
55
|
+
export type NextAuthErrorCode = AuthTokenRequestErrorCode | 'FAILURE' | 'SUBJECT_NOT_FOUND';
|
56
|
+
export type AuthTokenRedemptionErrorCode = AuthTokenRequestErrorCode | 'FAILURE' | 'TOKEN_NOT_SET' | 'TOKEN_MISMATCH' | 'TOKEN_EXPIRED' | 'TOKEN_REDEEMED';
|
65
57
|
export {};
|
@@ -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
|
-
var
|
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,68 +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
|
42
|
+
var _startsWithInstanceProperty__default = /*#__PURE__*/_interopDefault(_startsWithInstanceProperty);
|
43
43
|
var url__default = /*#__PURE__*/_interopDefault(url);
|
44
44
|
var cookie__namespace = /*#__PURE__*/_interopNamespace(cookie);
|
45
45
|
var ejs__default = /*#__PURE__*/_interopDefault(ejs);
|
46
46
|
var _filterInstanceProperty__default = /*#__PURE__*/_interopDefault(_filterInstanceProperty);
|
47
47
|
|
48
48
|
const template$1 = `
|
49
|
-
const
|
50
|
-
// @ts-ignore
|
51
|
-
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;
|
52
50
|
|
53
|
-
module.exports =
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
env: {
|
58
|
-
NEXTAUTH_URL: process.env.NEXTAUTH_URL || 'http://localhost:<%= process.env.PORT || 3000 %><%= keystonePath || '' %>/api/auth',
|
59
|
-
},
|
60
|
-
eslint: {
|
61
|
-
ignoreDuringBuilds: true,
|
62
|
-
},
|
63
|
-
webpack(config, { isServer }) {
|
64
|
-
config.resolve.alias = {
|
65
|
-
...config.resolve.alias,
|
66
|
-
react: Path.dirname(require.resolve('react/package.json')),
|
67
|
-
'react-dom': Path.dirname(require.resolve('react-dom/package.json')),
|
68
|
-
'@keystone-6/core': Path.dirname(
|
69
|
-
require.resolve('@keystone-6/core/package.json')
|
70
|
-
),
|
71
|
-
};
|
72
|
-
if (isServer) {
|
73
|
-
config.externals = [
|
74
|
-
...config.externals,
|
75
|
-
/@keystone-6\\/core(?!\\/___internal-do-not-use-will-break-in-patch\\/admin-ui\\/id-field-view|\\/fields\\/types\\/[^\\/]+\\/views)/,
|
76
|
-
/.prisma\\/client/
|
77
|
-
];
|
78
|
-
// we need to set these to true so that when __dirname/__filename is used
|
79
|
-
// to resolve the location of field views, we will get a path that we can use
|
80
|
-
// rather than just the __dirname/__filename of the generated file.
|
81
|
-
// https://webpack.js.org/configuration/node/#node__filename
|
82
|
-
(_config$node = config.node) !== null && _config$node !== void 0 ? _config$node : config.node = {};
|
83
|
-
config.node.__dirname = true;
|
84
|
-
config.node.__filename = true;
|
85
|
-
}
|
86
|
-
return config;
|
87
|
-
},
|
88
|
-
<% if (keystonePath) { %>
|
89
|
-
<% if (process.env.NODE_ENV != 'production') { %>
|
90
|
-
async rewrites() {
|
91
|
-
return [
|
92
|
-
{
|
93
|
-
source: '/api/__keystone_api_build',
|
94
|
-
destination: 'http://localhost:<%= process.env.PORT || 3000 %><%= keystonePath || '' %>/api/__keystone_api_build',
|
95
|
-
basePath: false
|
96
|
-
}
|
97
|
-
];
|
98
|
-
},
|
99
|
-
<% }%>
|
100
|
-
basePath: '<%= keystonePath || '' %>'
|
101
|
-
<% } %>
|
102
|
-
});
|
103
|
-
`;
|
51
|
+
module.exports = {
|
52
|
+
...keystoneConfig,
|
53
|
+
basePath: '<%= keystonePath || '' %>'
|
54
|
+
};`;
|
104
55
|
const nextConfigTemplate = _ref => {
|
105
56
|
let {
|
106
57
|
keystonePath
|
@@ -124,17 +75,14 @@ function getBaseAuthSchema(_ref) {
|
|
124
75
|
types: [base.object(listKey)],
|
125
76
|
resolveType: (root, context) => {
|
126
77
|
var _context$session;
|
127
|
-
|
128
78
|
return (_context$session = context.session) === null || _context$session === void 0 ? void 0 : _context$session.listKey;
|
129
79
|
}
|
130
80
|
}),
|
131
|
-
|
132
81
|
resolve(root, args, _ref2) {
|
133
82
|
let {
|
134
83
|
session,
|
135
84
|
db
|
136
85
|
} = _ref2;
|
137
|
-
|
138
86
|
if (typeof (session === null || session === void 0 ? void 0 : session.itemId) === 'string' && typeof session.listKey === 'string') {
|
139
87
|
return db[session.listKey].findOne({
|
140
88
|
where: {
|
@@ -142,10 +90,8 @@ function getBaseAuthSchema(_ref) {
|
|
142
90
|
}
|
143
91
|
});
|
144
92
|
}
|
145
|
-
|
146
93
|
return null;
|
147
94
|
}
|
148
|
-
|
149
95
|
})
|
150
96
|
}
|
151
97
|
};
|
@@ -160,7 +106,6 @@ const getSchemaExtension = _ref => {
|
|
160
106
|
} = _ref;
|
161
107
|
return core.graphql.extend(base => {
|
162
108
|
var _context;
|
163
|
-
|
164
109
|
const baseSchema = getBaseAuthSchema({
|
165
110
|
listKey,
|
166
111
|
base
|
@@ -210,6 +155,7 @@ const authTemplate = _ref => {
|
|
210
155
|
};
|
211
156
|
|
212
157
|
const _excluded = ["get", "end"];
|
158
|
+
|
213
159
|
/**
|
214
160
|
* createAuth function
|
215
161
|
*
|
@@ -233,6 +179,7 @@ function createAuth(_ref) {
|
|
233
179
|
// part of the createAuth API (in which case its use cases need to be documented and tested)
|
234
180
|
// or whether always being true is what we want, in which case we can refactor our code
|
235
181
|
// to match this. -TL
|
182
|
+
|
236
183
|
const customPath = !keystonePath || keystonePath === '/' ? '' : keystonePath;
|
237
184
|
/**
|
238
185
|
* pageMiddleware
|
@@ -244,40 +191,25 @@ function createAuth(_ref) {
|
|
244
191
|
* - to the init page when initFirstItem is configured, and there are no user in the database
|
245
192
|
* - to the signin page when no valid session is present
|
246
193
|
*/
|
247
|
-
|
248
|
-
const pageMiddleware = async _ref2 => {
|
194
|
+
const authMiddleware = async _ref2 => {
|
249
195
|
let {
|
250
196
|
context,
|
251
|
-
|
197
|
+
wasAccessAllowed
|
252
198
|
} = _ref2;
|
253
199
|
const {
|
254
200
|
req,
|
255
201
|
session
|
256
202
|
} = context;
|
257
203
|
const pathname = url__default["default"].parse(req === null || req === void 0 ? void 0 : req.url).pathname;
|
258
|
-
|
259
|
-
if (isValidSession) {
|
260
|
-
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)) {
|
261
|
-
return {
|
262
|
-
kind: 'redirect',
|
263
|
-
to: `${customPath}`
|
264
|
-
};
|
265
|
-
}
|
266
|
-
|
204
|
+
if (wasAccessAllowed) {
|
267
205
|
if (customPath !== '' && pathname === '/') {
|
268
206
|
return {
|
269
207
|
kind: 'redirect',
|
270
208
|
to: `${customPath}`
|
271
209
|
};
|
272
210
|
}
|
273
|
-
|
274
211
|
return;
|
275
212
|
}
|
276
|
-
|
277
|
-
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)) {
|
278
|
-
return;
|
279
|
-
}
|
280
|
-
|
281
213
|
if (!session && !_includesInstanceProperty__default["default"](pathname).call(pathname, `${customPath}/api/auth/`)) {
|
282
214
|
return {
|
283
215
|
kind: 'redirect',
|
@@ -285,17 +217,16 @@ function createAuth(_ref) {
|
|
285
217
|
};
|
286
218
|
}
|
287
219
|
};
|
220
|
+
|
288
221
|
/**
|
289
|
-
*
|
222
|
+
* authGetAdditionalFiles
|
290
223
|
*
|
291
224
|
* This function adds files to be generated into the Admin UI build. Must be added to the
|
292
225
|
* ui.getAdditionalFiles config.
|
293
226
|
*
|
294
227
|
* The signin page is always included, and the init page is included when initFirstItem is set
|
295
228
|
*/
|
296
|
-
|
297
|
-
|
298
|
-
const getAdditionalFiles = () => {
|
229
|
+
const authGetAdditionalFiles = () => {
|
299
230
|
const filesToWrite = [{
|
300
231
|
mode: 'write',
|
301
232
|
outputPath: 'pages/api/auth/[...nextauth].js',
|
@@ -315,102 +246,91 @@ function createAuth(_ref) {
|
|
315
246
|
}];
|
316
247
|
return filesToWrite;
|
317
248
|
};
|
249
|
+
|
318
250
|
/**
|
319
251
|
* publicAuthPages
|
320
252
|
*
|
321
253
|
* Must be added to the ui.publicPages config
|
322
254
|
*/
|
323
|
-
|
324
|
-
|
325
|
-
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
|
326
257
|
// @ts-ignore
|
327
|
-
|
328
258
|
function addPages(provider) {
|
329
259
|
const name = provider.id;
|
330
|
-
|
331
|
-
|
260
|
+
authPublicPages.push(`${customPath}/api/auth/signin/${name}`);
|
261
|
+
authPublicPages.push(`${customPath}/api/auth/callback/${name}`);
|
332
262
|
}
|
333
|
-
|
334
263
|
_mapInstanceProperty__default["default"](providers).call(providers, addPages);
|
264
|
+
|
335
265
|
/**
|
336
266
|
* extendGraphqlSchema
|
337
267
|
*
|
338
268
|
* Must be added to the extendGraphqlSchema config. Can be composed.
|
339
269
|
*/
|
340
|
-
|
341
|
-
|
342
270
|
const extendGraphqlSchema = getSchemaExtension({
|
343
271
|
identityField,
|
344
272
|
listKey
|
345
273
|
});
|
274
|
+
|
346
275
|
/**
|
347
276
|
* validateConfig
|
348
277
|
*
|
349
278
|
* Validates the provided auth config; optional step when integrating auth
|
350
279
|
*/
|
351
|
-
|
352
280
|
const validateConfig = keystoneConfig => {
|
353
281
|
const listConfig = keystoneConfig.lists[listKey];
|
354
|
-
|
355
282
|
if (listConfig === undefined) {
|
356
283
|
const msg = `A createAuth() invocation specifies the list "${listKey}" but no list with that key has been defined.`;
|
357
284
|
throw new Error(msg);
|
358
|
-
}
|
285
|
+
}
|
286
|
+
|
287
|
+
// TODO: Check if providers
|
359
288
|
// TODO: Check other required commands/data
|
289
|
+
|
360
290
|
// TODO: Check for String-like typing for identityField? How?
|
361
291
|
// TODO: Validate that the identifyField is unique.
|
362
292
|
// TODO: If this field isn't required, what happens if I try to log in as `null`?
|
363
|
-
|
364
|
-
|
365
293
|
const identityFieldConfig = listConfig.fields[identityField];
|
366
|
-
|
367
294
|
if (identityFieldConfig === undefined) {
|
368
295
|
const identityFieldName = _JSON$stringify__default["default"](identityField);
|
369
|
-
|
370
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.`;
|
371
297
|
throw new Error(msg);
|
372
298
|
}
|
373
299
|
};
|
300
|
+
|
374
301
|
/**
|
375
302
|
* withItemData
|
376
303
|
*
|
377
304
|
* Automatically injects a session.data value with the authenticated item
|
378
305
|
*/
|
379
|
-
|
380
306
|
/* TODO:
|
381
307
|
- [ ] We could support additional where input to validate item sessions (e.g an isEnabled boolean)
|
382
308
|
*/
|
383
|
-
|
384
|
-
|
385
309
|
const withItemData = _sessionStrategy => {
|
386
310
|
const {
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
311
|
+
get,
|
312
|
+
end
|
313
|
+
} = _sessionStrategy,
|
314
|
+
sessionStrategy = _objectWithoutProperties(_sessionStrategy, _excluded);
|
392
315
|
return _objectSpread(_objectSpread({}, sessionStrategy), {}, {
|
393
316
|
get: async _ref3 => {
|
394
317
|
var _req$headers, _req$headers$authoriz;
|
395
|
-
|
396
318
|
let {
|
397
|
-
|
398
|
-
createContext
|
319
|
+
context
|
399
320
|
} = _ref3;
|
321
|
+
const {
|
322
|
+
req
|
323
|
+
} = context;
|
400
324
|
const pathname = url__default["default"].parse(req === null || req === void 0 ? void 0 : req.url).pathname;
|
401
325
|
let nextSession;
|
402
|
-
|
326
|
+
if (!req) return;
|
403
327
|
if (_includesInstanceProperty__default["default"](pathname).call(pathname, '/api/auth')) {
|
404
328
|
return;
|
405
329
|
}
|
406
|
-
|
407
|
-
const sudoContext = createContext({
|
408
|
-
sudo: true
|
409
|
-
});
|
410
|
-
|
330
|
+
const sudoContext = context.sudo();
|
411
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') {
|
412
332
|
nextSession = await jwt.getToken({
|
413
|
-
req,
|
333
|
+
req: req,
|
414
334
|
secret: sessionSecret
|
415
335
|
});
|
416
336
|
} else {
|
@@ -418,11 +338,9 @@ function createAuth(_ref) {
|
|
418
338
|
req
|
419
339
|
});
|
420
340
|
}
|
421
|
-
|
422
341
|
if (!nextSession || !nextSession.listKey || nextSession.listKey !== listKey || !nextSession.itemId || !sudoContext.query[listKey] || !nextSession.itemId) {
|
423
342
|
return;
|
424
343
|
}
|
425
|
-
|
426
344
|
const reqWithUser = req;
|
427
345
|
reqWithUser.user = {
|
428
346
|
istKey: nextSession.listKey,
|
@@ -430,8 +348,7 @@ function createAuth(_ref) {
|
|
430
348
|
data: nextSession.data
|
431
349
|
};
|
432
350
|
const userSession = await get({
|
433
|
-
|
434
|
-
createContext
|
351
|
+
context
|
435
352
|
});
|
436
353
|
return _objectSpread(_objectSpread(_objectSpread({}, userSession), nextSession), {}, {
|
437
354
|
data: nextSession.data,
|
@@ -441,16 +358,17 @@ function createAuth(_ref) {
|
|
441
358
|
},
|
442
359
|
end: async _ref4 => {
|
443
360
|
let {
|
444
|
-
|
445
|
-
req,
|
446
|
-
createContext
|
361
|
+
context
|
447
362
|
} = _ref4;
|
448
363
|
await end({
|
449
|
-
|
450
|
-
req,
|
451
|
-
createContext
|
364
|
+
context
|
452
365
|
});
|
453
366
|
const TOKEN_NAME = process.env.NODE_ENV === 'production' ? '__Secure-next-auth.session-token' : 'next-auth.session-token';
|
367
|
+
const {
|
368
|
+
req,
|
369
|
+
res
|
370
|
+
} = context;
|
371
|
+
if (!req || !res) return;
|
454
372
|
res.setHeader('Set-Cookie', cookie__namespace.serialize(TOKEN_NAME, '', {
|
455
373
|
maxAge: 0,
|
456
374
|
expires: new Date(),
|
@@ -464,6 +382,12 @@ function createAuth(_ref) {
|
|
464
382
|
}
|
465
383
|
});
|
466
384
|
};
|
385
|
+
function defaultIsAccessAllowed(_ref5) {
|
386
|
+
let {
|
387
|
+
session
|
388
|
+
} = _ref5;
|
389
|
+
return session !== undefined;
|
390
|
+
}
|
467
391
|
/**
|
468
392
|
* withAuth
|
469
393
|
*
|
@@ -474,47 +398,37 @@ function createAuth(_ref) {
|
|
474
398
|
* It validates the auth config against the provided keystone config, and preserves existing
|
475
399
|
* config by composing existing extendGraphqlSchema functions and ui config.
|
476
400
|
*/
|
477
|
-
|
478
|
-
|
479
401
|
const withAuth = keystoneConfig => {
|
402
|
+
var _ui;
|
480
403
|
validateConfig(keystoneConfig);
|
481
404
|
let {
|
482
405
|
ui
|
483
406
|
} = keystoneConfig;
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
publicPages
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
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));
|
495
|
-
},
|
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],
|
496
416
|
isAccessAllowed: async context => {
|
497
|
-
var _context$req
|
498
|
-
|
499
|
-
|
500
|
-
req
|
501
|
-
} = context;
|
502
|
-
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
|
503
|
-
|
504
|
-
if (_includesInstanceProperty__default["default"](pathname).call(pathname, '/_next/')) {
|
505
|
-
return true;
|
506
|
-
} // Allow keystone to access /api/__keystone_api_build for hot reloading
|
507
|
-
|
508
|
-
|
509
|
-
if (process.env.NODE_ENV !== 'production' && ((_context$req = context.req) === null || _context$req === void 0 ? void 0 : _context$req.url) !== undefined && new _URL__default["default"](context.req.url, 'http://example.com').pathname === `${customPath}/api/__keystone_api_build`) {
|
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)) {
|
510
420
|
return true;
|
511
421
|
}
|
512
|
-
|
513
|
-
|
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);
|
514
429
|
}
|
515
430
|
});
|
516
431
|
}
|
517
|
-
|
518
432
|
if (!keystoneConfig.session) throw new TypeError('Missing .session configuration');
|
519
433
|
const session = withItemData(keystoneConfig.session);
|
520
434
|
const existingExtendGraphQLSchema = keystoneConfig.extendGraphqlSchema;
|
@@ -529,16 +443,15 @@ function createAuth(_ref) {
|
|
529
443
|
extendGraphqlSchema: existingExtendGraphQLSchema ? schema => existingExtendGraphQLSchema(extendGraphqlSchema(schema)) : extendGraphqlSchema
|
530
444
|
});
|
531
445
|
};
|
532
|
-
|
533
446
|
return {
|
534
|
-
withAuth
|
447
|
+
withAuth
|
448
|
+
// In the future we may want to return the following so that developers can
|
535
449
|
// roll their own. This is pending a review of the use cases this might be
|
536
450
|
// appropriate for, along with documentation and testing.
|
537
451
|
// ui: { enableSessionItem: true, pageMiddleware, getAdditionalFiles, publicPages },
|
538
452
|
// fields,
|
539
453
|
// extendGraphqlSchema,
|
540
454
|
// validateConfig,
|
541
|
-
|
542
455
|
};
|
543
456
|
}
|
544
457
|
|