@opensaas/keystone-nextjs-auth 20.5.0 → 21.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -49,23 +49,42 @@ async function validateNextAuth(identityField, identity, protectIdentities, item
49
49
 
50
50
  function NextAuthPage(props) {
51
51
  const {
52
+ autoCreate,
53
+ cookies,
54
+ events,
55
+ identityField,
56
+ jwt,
57
+ listKey,
58
+ pages,
52
59
  providers,
53
60
  query,
54
- identityField,
61
+ resolver,
55
62
  sessionData,
56
- listKey,
57
- autoCreate,
58
- userMap,
59
- accountMap,
60
- profileMap,
61
63
  sessionSecret
62
- } = props;
64
+ } = props; // TODO: (v1.1). https://github.com/ijsto/keystone-6-oauth/projects/1#card-78602004
65
+
66
+ console.log('NextAuthPages... ', pages);
67
+
68
+ if (!query) {
69
+ console.error('NextAuthPage got no query.');
70
+ return null;
71
+ }
72
+
73
+ if (!providers || !providers.length) {
74
+ console.error('You need to provide at least one provider.');
75
+ return null;
76
+ }
77
+
63
78
  const list = query[listKey];
64
79
  const queryAPI = query[listKey];
65
80
  const protectIdentities = true;
66
81
  return NextAuth({
67
- secret: sessionSecret,
82
+ cookies,
68
83
  providers,
84
+ pages: pages || {},
85
+ events: events || {},
86
+ jwt: jwt || {},
87
+ secret: sessionSecret,
69
88
  callbacks: {
70
89
  async signIn({
71
90
  user,
@@ -82,28 +101,16 @@ function NextAuthPage(props) {
82
101
  identity = 0;
83
102
  }
84
103
 
85
- const result = await validateNextAuth(identityField, identity, protectIdentities, queryAPI);
86
- const data = {}; // eslint-disable-next-line no-restricted-syntax
87
-
88
- for (const key in userMap) {
89
- if (Object.prototype.hasOwnProperty.call(userMap, key)) {
90
- data[key] = user[userMap[key]];
91
- }
92
- } // eslint-disable-next-line no-restricted-syntax
93
-
104
+ const userInput = resolver ? await resolver({
105
+ user,
106
+ account,
107
+ profile
108
+ }) : {};
109
+ const result = await validateNextAuth(identityField, identity, protectIdentities, queryAPI); // ID
94
110
 
95
- for (const key in accountMap) {
96
- if (Object.prototype.hasOwnProperty.call(accountMap, key)) {
97
- data[key] = account[accountMap[key]];
98
- }
99
- } // eslint-disable-next-line no-restricted-syntax
100
-
101
-
102
- for (const key in profileMap) {
103
- if (Object.prototype.hasOwnProperty.call(profileMap, key)) {
104
- data[key] = profile[profileMap[key]];
105
- }
106
- }
111
+ const data = _objectSpread({
112
+ [identityField]: identity
113
+ }, userInput);
107
114
 
108
115
  if (!result.success) {
109
116
  if (!autoCreate) {
@@ -158,9 +165,7 @@ function NextAuthPage(props) {
158
165
  const result = await validateNextAuth(identityField, identity, protectIdentities, queryAPI);
159
166
 
160
167
  if (!result.success) {
161
- return {
162
- result: false
163
- };
168
+ return token;
164
169
  }
165
170
 
166
171
  token.itemId = result.item.id;
@@ -1,15 +1,11 @@
1
1
  import type { BaseItem } from '@keystone-6/core/types';
2
2
  import { graphql } from '@keystone-6/core';
3
3
 
4
- import { AuthGqlNames } from '../types';
5
-
6
4
  export function getBaseAuthSchema({
7
5
  listKey,
8
- gqlNames,
9
6
  base,
10
7
  }: {
11
8
  listKey: string;
12
- gqlNames: AuthGqlNames;
13
9
  base: graphql.BaseSchemaMeta;
14
10
  }) {
15
11
  const extension = {
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import url from 'url';
1
+ import url from "url";
2
2
  import {
3
3
  AdminFileToWrite,
4
4
  BaseListTypeInfo,
@@ -7,23 +7,19 @@ import {
7
7
  AdminUIConfig,
8
8
  SessionStrategy,
9
9
  BaseKeystoneTypeInfo,
10
- } from '@keystone-6/core/types';
11
- import { getSession } from 'next-auth/react';
12
- import { getToken } from 'next-auth/jwt';
13
- import * as cookie from 'cookie';
14
- import { Provider } from 'next-auth/providers';
15
- import { NextApiRequest } from 'next';
16
- import { nextConfigTemplate } from './templates/next-config';
10
+ } from "@keystone-6/core/types";
11
+ import { getSession } from "next-auth/react";
12
+ import { getToken } from "next-auth/jwt";
13
+ import { Provider } from "next-auth/providers";
14
+
15
+ import * as cookie from "cookie";
16
+
17
+ import { nextConfigTemplate } from "./templates/next-config";
17
18
  // import * as Path from 'path';
18
19
 
19
- import {
20
- AuthConfig,
21
- AuthGqlNames,
22
- KeystoneAuthConfig,
23
- NextAuthSession,
24
- } from './types';
25
- import { getSchemaExtension } from './schema';
26
- import { authTemplate } from './templates/auth';
20
+ import { AuthConfig, KeystoneOAuthConfig, NextAuthSession } from "./types";
21
+ import { getSchemaExtension } from "./schema";
22
+ import { authTemplate } from "./templates/auth";
27
23
 
28
24
  /**
29
25
  * createAuth function
@@ -31,35 +27,25 @@ import { authTemplate } from './templates/auth';
31
27
  * Generates config for Keystone to implement standard auth features.
32
28
  */
33
29
 
34
- export type { NextAuthProviders, KeystoneAuthConfig } from './types';
30
+ export type { NextAuthProviders, KeystoneOAuthConfig } from "./types";
35
31
  export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
36
- listKey,
37
- identityField,
38
- sessionData,
39
32
  autoCreate,
40
- userMap,
41
- accountMap,
42
- profileMap,
33
+ cookies,
34
+ identityField,
35
+ listKey,
43
36
  keystonePath,
37
+ pages,
38
+ resolver,
44
39
  providers,
40
+ sessionData,
45
41
  sessionSecret,
46
42
  }: AuthConfig<GeneratedListTypes>) {
47
43
  // The protectIdentities flag is currently under review to see whether it should be
48
44
  // part of the createAuth API (in which case its use cases need to be documented and tested)
49
45
  // or whether always being true is what we want, in which case we can refactor our code
50
46
  // to match this. -TL
51
- const gqlNames: AuthGqlNames = {
52
- // Core
53
- authenticateItemWithPassword: `authenticate${listKey}WithPassword`,
54
- ItemAuthenticationWithPasswordResult: `${listKey}AuthenticationWithPasswordResult`,
55
- ItemAuthenticationWithPasswordSuccess: `${listKey}AuthenticationWithPasswordSuccess`,
56
- ItemAuthenticationWithPasswordFailure: `${listKey}AuthenticationWithPasswordFailure`,
57
- // Initial data
58
- CreateInitialInput: `CreateInitial${listKey}Input`,
59
- createInitialItem: `createInitial${listKey}`,
60
- };
61
47
 
62
- const customPath = !keystonePath || keystonePath === '/' ? '' : keystonePath;
48
+ const customPath = !keystonePath || keystonePath === "/" ? "" : keystonePath;
63
49
  /**
64
50
  * pageMiddleware
65
51
  *
@@ -70,25 +56,34 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
70
56
  * - to the init page when initFirstItem is configured, and there are no user in the database
71
57
  * - to the signin page when no valid session is present
72
58
  */
73
- const pageMiddleware: AdminUIConfig<BaseKeystoneTypeInfo>['pageMiddleware'] =
59
+ const pageMiddleware: AdminUIConfig<BaseKeystoneTypeInfo>["pageMiddleware"] =
74
60
  async ({ context, isValidSession }) => {
75
61
  const { req, session } = context;
76
62
  const pathname = url.parse(req?.url!).pathname!;
77
- if (pathname === `${customPath}/api/__keystone_api_build`) {
78
- return;
79
- }
63
+
80
64
  if (isValidSession) {
81
65
  if (pathname === `${customPath}/api/auth/signin`) {
82
- return { kind: 'redirect', to: `${customPath}` };
66
+ return { kind: "redirect", to: `${customPath}` };
83
67
  }
84
- if (customPath !== '' && pathname === '/') {
85
- return { kind: 'redirect', to: `${customPath}` };
68
+ if (customPath !== "" && pathname === "/") {
69
+ return { kind: "redirect", to: `${customPath}` };
86
70
  }
87
71
  return;
88
72
  }
89
-
73
+ if (
74
+ pathname.includes("/_next/") ||
75
+ pathname.includes("/api/auth/") ||
76
+ pathname.includes(pages?.signIn) ||
77
+ pathname.includes(pages?.error) ||
78
+ pathname.includes(pages?.signOut)
79
+ ) {
80
+ return;
81
+ }
90
82
  if (!session && !pathname.includes(`${customPath}/api/auth/`)) {
91
- return { kind: 'redirect', to: `${customPath}/api/auth/signin` };
83
+ return {
84
+ kind: "redirect",
85
+ to: pages?.signIn || `${customPath}/api/auth/signin`,
86
+ };
92
87
  }
93
88
  };
94
89
 
@@ -103,23 +98,19 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
103
98
  const getAdditionalFiles = () => {
104
99
  const filesToWrite: AdminFileToWrite[] = [
105
100
  {
106
- mode: 'write',
107
- outputPath: 'pages/api/auth/[...nextauth].js',
101
+ mode: "write",
102
+ outputPath: "pages/api/auth/[...nextauth].js",
108
103
  src: authTemplate({
109
- gqlNames,
104
+ autoCreate,
110
105
  identityField,
111
- sessionData,
112
106
  listKey,
113
- autoCreate,
114
- userMap,
115
- accountMap,
116
- profileMap,
107
+ sessionData,
117
108
  sessionSecret,
118
109
  }),
119
110
  },
120
111
  {
121
- mode: 'write',
122
- outputPath: 'next.config.js',
112
+ mode: "write",
113
+ outputPath: "next.config.js",
123
114
  src: nextConfigTemplate({ keystonePath: customPath }),
124
115
  },
125
116
  ];
@@ -132,13 +123,17 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
132
123
  * Must be added to the ui.publicPages config
133
124
  */
134
125
  const publicPages = [
126
+ `${customPath}/api/__keystone_api_build`,
135
127
  `${customPath}/api/auth/csrf`,
136
128
  `${customPath}/api/auth/signin`,
137
129
  `${customPath}/api/auth/callback`,
138
130
  `${customPath}/api/auth/session`,
139
131
  `${customPath}/api/auth/providers`,
140
132
  `${customPath}/api/auth/signout`,
133
+ `${customPath}/api/auth/error`,
141
134
  ];
135
+ // TODO: Add Provider Types
136
+ // @ts-ignore
142
137
  function addPages(provider: Provider) {
143
138
  const name = provider.id;
144
139
  publicPages.push(`${customPath}/api/auth/signin/${name}`);
@@ -154,7 +149,6 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
154
149
  const extendGraphqlSchema = getSchemaExtension({
155
150
  identityField,
156
151
  listKey,
157
- gqlNames,
158
152
  });
159
153
 
160
154
  /**
@@ -169,13 +163,16 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
169
163
  throw new Error(msg);
170
164
  }
171
165
 
166
+ // TODO: Check if providers
167
+ // TODO: Check other required commands/data
168
+
172
169
  // TODO: Check for String-like typing for identityField? How?
173
170
  // TODO: Validate that the identifyField is unique.
174
171
  // TODO: If this field isn't required, what happens if I try to log in as `null`?
175
172
  const identityFieldConfig = listConfig.fields[identityField];
176
173
  if (identityFieldConfig === undefined) {
177
- const i = JSON.stringify(identityField);
178
- const msg = `A createAuth() invocation for the "${listKey}" list specifies ${i} as its identityField but no field with that key exists on the list.`;
174
+ const identityFieldName = JSON.stringify(identityField);
175
+ const msg = `A createAuth() invocation for the "${listKey}" list specifies ${identityFieldName} as its identityField but no field with that key exists on the list.`;
179
176
  throw new Error(msg);
180
177
  }
181
178
  };
@@ -194,18 +191,25 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
194
191
  const { get, start, ...sessionStrategy } = _sessionStrategy;
195
192
  return {
196
193
  ...sessionStrategy,
197
- start,
194
+ start: async ({ res }) => {
195
+ console.log("start");
196
+
197
+ const session = await start({ res });
198
+ return session;
199
+ },
198
200
  get: async ({ req }) => {
199
201
  const pathname = url.parse(req?.url!).pathname!;
200
- if (pathname.includes('/api/auth')) {
202
+ if (pathname.includes("/api/auth")) {
201
203
  return;
202
204
  }
203
- if (req.headers.authorization?.split(' ')[0] === 'Bearer') {
204
- const request = req as NextApiRequest;
205
- const token = await getToken({ req: request, secret: sessionSecret });
205
+ if (req.headers.authorization?.split(" ")[0] === "Bearer") {
206
+ const token = (await getToken({
207
+ req,
208
+ secret: sessionSecret,
209
+ })) as NextAuthSession;
206
210
 
207
211
  if (token?.data?.id) {
208
- return token as NextAuthSession;
212
+ return token;
209
213
  }
210
214
  }
211
215
  const nextSession: unknown = await getSession({ req });
@@ -216,18 +220,19 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
216
220
  },
217
221
  end: async ({ res, req }) => {
218
222
  const TOKEN_NAME =
219
- process.env.NODE_ENV === 'production'
220
- ? '__Secure-next-auth.session-token'
221
- : 'next-auth.session-token';
223
+ process.env.NODE_ENV === "production"
224
+ ? "__Secure-next-auth.session-token"
225
+ : "next-auth.session-token";
222
226
  res.setHeader(
223
- 'Set-Cookie',
224
- cookie.serialize(TOKEN_NAME, '', {
227
+ "Set-Cookie",
228
+ cookie.serialize(TOKEN_NAME, "", {
225
229
  maxAge: 0,
226
230
  expires: new Date(),
227
231
  httpOnly: true,
228
- secure: process.env.NODE_ENV === 'production',
229
- path: '/',
230
- sameSite: 'lax',
232
+ secure: process.env.NODE_ENV === "production",
233
+ path: "/",
234
+ sameSite: "lax",
235
+ // TODO: Update parse to URL
231
236
  domain: url.parse(req.url as string).hostname as string,
232
237
  })
233
238
  );
@@ -245,7 +250,7 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
245
250
  * It validates the auth config against the provided keystone config, and preserves existing
246
251
  * config by composing existing extendGraphqlSchema functions and ui config.
247
252
  */
248
- const withAuth = (keystoneConfig: KeystoneConfig): KeystoneAuthConfig => {
253
+ const withAuth = (keystoneConfig: KeystoneConfig): KeystoneOAuthConfig => {
249
254
  validateConfig(keystoneConfig);
250
255
  let { ui } = keystoneConfig;
251
256
  if (keystoneConfig.ui) {
@@ -261,47 +266,44 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
261
266
  keystoneConfig?.ui?.pageMiddleware?.(args),
262
267
  enableSessionItem: true,
263
268
  isAccessAllowed: async (context: KeystoneContext) => {
269
+ const { req } = context;
270
+ const pathname = url.parse(req?.url!).pathname!;
271
+
272
+ // Allow nextjs scripts and static files to be accessed without auth
273
+ if (pathname.includes("/_next/")) {
274
+ return true;
275
+ }
276
+
277
+ // Allow keystone to access /api/__keystone_api_build for hot reloading
264
278
  if (
265
- process.env.NODE_ENV !== 'production' &&
279
+ process.env.NODE_ENV !== "production" &&
266
280
  context.req?.url !== undefined &&
267
- new URL(context.req.url, 'http://example.com').pathname ===
281
+ new URL(context.req.url, "http://example.com").pathname ===
268
282
  `${customPath}/api/__keystone_api_build`
269
283
  ) {
270
284
  return true;
271
285
  }
272
- // Allow access to the adminMeta data from the /init path to correctly render that page
273
- // even if the user isn't logged in (which should always be the case if they're seeing /init)
274
- const headers = context.req?.headers;
275
- const host = headers
276
- ? headers['x-forwarded-host'] || headers.host
277
- : null;
278
- const thisUrl = headers?.referer
279
- ? new URL(headers.referer)
280
- : undefined;
281
- const accessingInitPage =
282
- thisUrl?.pathname === '/init' &&
283
- thisUrl?.host === host &&
284
- (await context.sudo().query[listKey].count({})) === 0;
285
- return (
286
- accessingInitPage ||
287
- (keystoneConfig.ui?.isAccessAllowed
288
- ? keystoneConfig.ui.isAccessAllowed(context)
289
- : context.session !== undefined)
290
- );
286
+
287
+ return keystoneConfig.ui?.isAccessAllowed
288
+ ? keystoneConfig.ui.isAccessAllowed(context)
289
+ : context.session !== undefined;
291
290
  },
292
291
  };
293
292
  }
294
293
 
295
294
  if (!keystoneConfig.session)
296
- throw new TypeError('Missing .session configuration');
295
+ throw new TypeError("Missing .session configuration");
297
296
  const session = withItemData(keystoneConfig.session);
298
297
 
299
298
  const existingExtendGraphQLSchema = keystoneConfig.extendGraphqlSchema;
300
299
  return {
301
300
  ...keystoneConfig,
302
301
  ui,
303
- session,
302
+ cookies,
304
303
  providers,
304
+ pages,
305
+ resolver,
306
+ session,
305
307
  lists: {
306
308
  ...keystoneConfig.lists,
307
309
  },
@@ -1,43 +1,74 @@
1
- import NextAuth from 'next-auth';
1
+ import NextAuth, {
2
+ CookiesOptions,
3
+ EventCallbacks,
4
+ PagesOptions,
5
+ } from 'next-auth';
2
6
  import type { KeystoneListsAPI } from '@keystone-6/core/types';
3
7
  import { Provider } from 'next-auth/providers';
8
+ import { JWTOptions } from 'next-auth/jwt';
4
9
  import { validateNextAuth } from '../lib/validateNextAuth';
5
10
 
6
- // Need to bring in correct props
7
- type NextAuthPageProps = {
11
+ // TODO: See if possible to merge with `type AuthConfig`
12
+ type CoreNextAuthPageProps = {
13
+ autoCreate: boolean;
14
+ cookies?: Partial<CookiesOptions>;
15
+ events?: Partial<EventCallbacks>;
8
16
  identityField: string;
9
- mutationName: string;
10
- providers: Provider[];
11
- query: KeystoneListsAPI<any>;
12
- sessionData: string;
17
+ jwt?: Partial<JWTOptions>;
13
18
  listKey: string;
14
- autoCreate: boolean;
15
- userMap: any;
16
- accountMap: any;
17
- profileMap: any;
19
+ pages?: Partial<PagesOptions>;
20
+ providers?: Provider[];
21
+ resolver?: Function | undefined;
22
+ sessionData: string | undefined;
18
23
  sessionSecret: string;
19
24
  };
20
25
 
26
+ type NextAuthGglProps = {
27
+ mutationName?: string;
28
+ query?: KeystoneListsAPI<any>;
29
+ };
30
+
31
+ export type NextAuthPageProps = CoreNextAuthPageProps & NextAuthGglProps;
32
+
21
33
  export default function NextAuthPage(props: NextAuthPageProps) {
22
34
  const {
35
+ autoCreate,
36
+ cookies,
37
+ events,
38
+ identityField,
39
+ jwt,
40
+ listKey,
41
+ pages,
23
42
  providers,
24
43
  query,
25
- identityField,
44
+ resolver,
26
45
  sessionData,
27
- listKey,
28
- autoCreate,
29
- userMap,
30
- accountMap,
31
- profileMap,
32
46
  sessionSecret,
33
47
  } = props;
48
+ // TODO: (v1.1). https://github.com/ijsto/keystone-6-oauth/projects/1#card-78602004
49
+ console.log('NextAuthPages... ', pages);
50
+
51
+ if (!query) {
52
+ console.error('NextAuthPage got no query.');
53
+ return null;
54
+ }
55
+
56
+ if (!providers || !providers.length) {
57
+ console.error('You need to provide at least one provider.');
58
+ return null;
59
+ }
60
+
34
61
  const list = query[listKey];
35
62
  const queryAPI = query[listKey];
36
63
  const protectIdentities = true;
37
64
 
38
65
  return NextAuth({
39
- secret: sessionSecret,
66
+ cookies,
40
67
  providers,
68
+ pages: pages || {},
69
+ events: events || {},
70
+ jwt: jwt || {},
71
+ secret: sessionSecret,
41
72
  callbacks: {
42
73
  async signIn({ user, account, profile }) {
43
74
  let identity;
@@ -48,31 +79,21 @@ export default function NextAuthPage(props: NextAuthPageProps) {
48
79
  } else {
49
80
  identity = 0;
50
81
  }
82
+ const userInput = resolver
83
+ ? await resolver({ user, account, profile })
84
+ : {};
85
+
51
86
  const result = await validateNextAuth(
52
87
  identityField,
53
88
  identity,
54
89
  protectIdentities,
55
90
  queryAPI
56
91
  );
57
- const data: any = {};
58
- // eslint-disable-next-line no-restricted-syntax
59
- for (const key in userMap) {
60
- if (Object.prototype.hasOwnProperty.call(userMap, key)) {
61
- data[key] = user[userMap[key]];
62
- }
63
- }
64
- // eslint-disable-next-line no-restricted-syntax
65
- for (const key in accountMap) {
66
- if (Object.prototype.hasOwnProperty.call(accountMap, key)) {
67
- data[key] = account[accountMap[key]];
68
- }
69
- }
70
- // eslint-disable-next-line no-restricted-syntax
71
- for (const key in profileMap) {
72
- if (Object.prototype.hasOwnProperty.call(profileMap, key)) {
73
- data[key] = profile[profileMap[key]];
74
- }
75
- }
92
+ // ID
93
+ const data: any = {
94
+ [identityField]: identity,
95
+ ...userInput,
96
+ };
76
97
 
77
98
  if (!result.success) {
78
99
  if (!autoCreate) {
@@ -125,7 +146,7 @@ export default function NextAuthPage(props: NextAuthPageProps) {
125
146
  );
126
147
 
127
148
  if (!result.success) {
128
- return { result: false };
149
+ return token;
129
150
  }
130
151
  token.itemId = result.item.id;
131
152
  }
package/src/schema.ts CHANGED
@@ -1,39 +1,17 @@
1
1
  import { ExtendGraphqlSchema } from '@keystone-6/core/types';
2
2
 
3
- import { assertInputObjectType, GraphQLString, GraphQLID } from 'graphql';
4
3
  import { graphql } from '@keystone-6/core';
5
- import { AuthGqlNames } from './types';
6
4
  import { getBaseAuthSchema } from './gql/getBaseAuthSchema';
7
5
 
8
6
  export const getSchemaExtension = ({
9
- identityField,
10
7
  listKey,
11
- gqlNames,
12
8
  }: {
13
9
  identityField: string;
14
10
  listKey: string;
15
- gqlNames: AuthGqlNames;
16
11
  }): ExtendGraphqlSchema =>
17
12
  graphql.extend((base) => {
18
- const uniqueWhereInputType = assertInputObjectType(
19
- base.schema.getType(`${listKey}WhereUniqueInput`)
20
- );
21
- const identityFieldOnUniqueWhere =
22
- uniqueWhereInputType.getFields()[identityField];
23
- if (
24
- identityFieldOnUniqueWhere?.type !== GraphQLString &&
25
- identityFieldOnUniqueWhere?.type !== GraphQLID
26
- ) {
27
- throw new Error(
28
- `createAuth was called with an identityField of ${identityField} on the list ${listKey} ` +
29
- `but that field doesn't allow being searched uniquely with a String or ID. ` +
30
- `You should likely add \`isIndexed: 'unique'\` ` +
31
- `to the field at ${listKey}.${identityField}`
32
- );
33
- }
34
13
  const baseSchema = getBaseAuthSchema({
35
14
  listKey,
36
- gqlNames,
37
15
  base,
38
16
  });
39
17