Skip to content

Commit afedf9d

Browse files
author
Josh Calder
committed
Add next-auth example
1 parent 612431b commit afedf9d

File tree

8 files changed

+471
-4
lines changed

8 files changed

+471
-4
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import NextAuth from 'next-auth';
2+
import GithubProvider from 'next-auth/providers/github';
3+
4+
// See https://next-auth.js.org/configuration/options for more details on what goes in here
5+
export const authOptions = {
6+
secret: '--DEV--COOKIE--SECRET--CHANGE--ME--==',
7+
// Configure one or more authentication providers
8+
providers: [
9+
GithubProvider({
10+
clientId: process.env.GITHUB_ID!,
11+
clientSecret: process.env.GITHUB_SECRET!,
12+
}),
13+
// ...add more providers here
14+
],
15+
};
16+
17+
export default NextAuth(authOptions);
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { config } from '@keystone-6/core';
2+
import { getServerSession } from 'next-auth/next';
3+
import type { Session } from 'next-auth';
4+
import { fixPrismaPath } from '../example-utils';
5+
import { lists } from './schema';
6+
import { authOptions } from './admin/pages/api/auth/[...nextauth]';
7+
import type { Context, TypeInfo } from '.keystone/types';
8+
9+
// WARNING: this example is for demonstration purposes only
10+
// as with each of our examples, it has not been vetted
11+
// or tested for any particular usage
12+
13+
const nextAuthSession = {
14+
async get({ context }: { context: Context }): Promise<Session | undefined> {
15+
const { req, res } = context;
16+
if (!req || !res || !req.headers.cookie) return;
17+
// We need to construct the cookies object so next-auth gets to correct object
18+
const cookies: Record<string, string> = {};
19+
req.headers.cookie.split(';').forEach(cookie => {
20+
const [key, value] = cookie.trim().split('=');
21+
cookies[key] = decodeURIComponent(value);
22+
});
23+
return (await getServerSession({ headers: req.headers, cookies } as any, res, authOptions)) as
24+
| Session
25+
| undefined;
26+
},
27+
// we don't need these unless we want to support the functions
28+
// context.sessionStrategy.start
29+
// context.sessionStrategy.end
30+
//
31+
async start() {},
32+
async end() {},
33+
};
34+
35+
export default config<TypeInfo>({
36+
db: {
37+
provider: 'sqlite',
38+
url: process.env.DATABASE_URL || 'file:./keystone-example.db',
39+
40+
// WARNING: this is only needed for our monorepo examples, dont do this
41+
...fixPrismaPath,
42+
},
43+
ui: {
44+
// The following API routes are required for NextAuth.js
45+
publicPages: [
46+
'/api/auth/csrf',
47+
'/api/auth/signin',
48+
'/api/auth/callback',
49+
'/api/auth/session',
50+
'/api/auth/providers',
51+
'/api/auth/signout',
52+
'/api/auth/error',
53+
// Each provider will need a separate callback and signin page listed here
54+
'/api/auth/signin/github',
55+
'/api/auth/callback/github',
56+
],
57+
// Adding Page middleware ensures that users are redirected to the signin page if they are not signed in.
58+
pageMiddleware: async ({ wasAccessAllowed }) => {
59+
if (wasAccessAllowed) return;
60+
return {
61+
kind: 'redirect',
62+
to: '/api/auth/signin',
63+
};
64+
},
65+
},
66+
lists,
67+
// you can find out more at https://keystonejs.com/docs/apis/session#session-api
68+
session: nextAuthSession,
69+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "@keystone-6/custom-session-next-auth",
3+
"version": "0.1.1",
4+
"private": true,
5+
"license": "MIT",
6+
"scripts": {
7+
"dev": "keystone dev",
8+
"start": "keystone start",
9+
"build": "keystone build",
10+
"postinstall": "keystone postinstall"
11+
},
12+
"dependencies": {
13+
"@keystone-6/core": "^5.0.0",
14+
"@prisma/client": "^4.14.0",
15+
"next-auth": "^4.22.1"
16+
},
17+
"devDependencies": {
18+
"prisma": "^4.14.0",
19+
"typescript": "~5.0.0"
20+
}
21+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"template": "node",
3+
"container": {
4+
"startScript": "keystone dev",
5+
"node": "16"
6+
}
7+
}
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
# This file is automatically generated by Keystone, do not modify it manually.
2+
# Modify your Keystone config when you want to change this.
3+
4+
type Post {
5+
id: ID!
6+
title: String
7+
content: String
8+
}
9+
10+
input PostWhereUniqueInput {
11+
id: ID
12+
}
13+
14+
input PostWhereInput {
15+
AND: [PostWhereInput!]
16+
OR: [PostWhereInput!]
17+
NOT: [PostWhereInput!]
18+
id: IDFilter
19+
title: StringFilter
20+
content: StringFilter
21+
}
22+
23+
input IDFilter {
24+
equals: ID
25+
in: [ID!]
26+
notIn: [ID!]
27+
lt: ID
28+
lte: ID
29+
gt: ID
30+
gte: ID
31+
not: IDFilter
32+
}
33+
34+
input StringFilter {
35+
equals: String
36+
in: [String!]
37+
notIn: [String!]
38+
lt: String
39+
lte: String
40+
gt: String
41+
gte: String
42+
contains: String
43+
startsWith: String
44+
endsWith: String
45+
not: NestedStringFilter
46+
}
47+
48+
input NestedStringFilter {
49+
equals: String
50+
in: [String!]
51+
notIn: [String!]
52+
lt: String
53+
lte: String
54+
gt: String
55+
gte: String
56+
contains: String
57+
startsWith: String
58+
endsWith: String
59+
not: NestedStringFilter
60+
}
61+
62+
input PostOrderByInput {
63+
id: OrderDirection
64+
title: OrderDirection
65+
content: OrderDirection
66+
}
67+
68+
enum OrderDirection {
69+
asc
70+
desc
71+
}
72+
73+
input PostUpdateInput {
74+
title: String
75+
content: String
76+
}
77+
78+
input PostUpdateArgs {
79+
where: PostWhereUniqueInput!
80+
data: PostUpdateInput!
81+
}
82+
83+
input PostCreateInput {
84+
title: String
85+
content: String
86+
}
87+
88+
"""
89+
The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
90+
"""
91+
scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf")
92+
93+
type Mutation {
94+
createPost(data: PostCreateInput!): Post
95+
createPosts(data: [PostCreateInput!]!): [Post]
96+
updatePost(where: PostWhereUniqueInput!, data: PostUpdateInput!): Post
97+
updatePosts(data: [PostUpdateArgs!]!): [Post]
98+
deletePost(where: PostWhereUniqueInput!): Post
99+
deletePosts(where: [PostWhereUniqueInput!]!): [Post]
100+
endSession: Boolean!
101+
}
102+
103+
type Query {
104+
posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: PostWhereUniqueInput): [Post!]
105+
post(where: PostWhereUniqueInput!): Post
106+
postsCount(where: PostWhereInput! = {}): Int
107+
keystone: KeystoneMeta!
108+
}
109+
110+
type KeystoneMeta {
111+
adminMeta: KeystoneAdminMeta!
112+
}
113+
114+
type KeystoneAdminMeta {
115+
lists: [KeystoneAdminUIListMeta!]!
116+
list(key: String!): KeystoneAdminUIListMeta
117+
}
118+
119+
type KeystoneAdminUIListMeta {
120+
key: String!
121+
itemQueryName: String!
122+
listQueryName: String!
123+
hideCreate: Boolean!
124+
hideDelete: Boolean!
125+
path: String!
126+
label: String!
127+
singular: String!
128+
plural: String!
129+
description: String
130+
initialColumns: [String!]!
131+
pageSize: Int!
132+
labelField: String!
133+
fields: [KeystoneAdminUIFieldMeta!]!
134+
groups: [KeystoneAdminUIFieldGroupMeta!]!
135+
initialSort: KeystoneAdminUISort
136+
isHidden: Boolean!
137+
isSingleton: Boolean!
138+
}
139+
140+
type KeystoneAdminUIFieldMeta {
141+
path: String!
142+
label: String!
143+
description: String
144+
isOrderable: Boolean!
145+
isFilterable: Boolean!
146+
isNonNull: [KeystoneAdminUIFieldMetaIsNonNull!]
147+
fieldMeta: JSON
148+
viewsIndex: Int!
149+
customViewsIndex: Int
150+
createView: KeystoneAdminUIFieldMetaCreateView!
151+
listView: KeystoneAdminUIFieldMetaListView!
152+
itemView(id: ID): KeystoneAdminUIFieldMetaItemView
153+
search: QueryMode
154+
}
155+
156+
enum KeystoneAdminUIFieldMetaIsNonNull {
157+
read
158+
create
159+
update
160+
}
161+
162+
type KeystoneAdminUIFieldMetaCreateView {
163+
fieldMode: KeystoneAdminUIFieldMetaCreateViewFieldMode!
164+
}
165+
166+
enum KeystoneAdminUIFieldMetaCreateViewFieldMode {
167+
edit
168+
hidden
169+
}
170+
171+
type KeystoneAdminUIFieldMetaListView {
172+
fieldMode: KeystoneAdminUIFieldMetaListViewFieldMode!
173+
}
174+
175+
enum KeystoneAdminUIFieldMetaListViewFieldMode {
176+
read
177+
hidden
178+
}
179+
180+
type KeystoneAdminUIFieldMetaItemView {
181+
fieldMode: KeystoneAdminUIFieldMetaItemViewFieldMode
182+
fieldPosition: KeystoneAdminUIFieldMetaItemViewFieldPosition
183+
}
184+
185+
enum KeystoneAdminUIFieldMetaItemViewFieldMode {
186+
edit
187+
read
188+
hidden
189+
}
190+
191+
enum KeystoneAdminUIFieldMetaItemViewFieldPosition {
192+
form
193+
sidebar
194+
}
195+
196+
enum QueryMode {
197+
default
198+
insensitive
199+
}
200+
201+
type KeystoneAdminUIFieldGroupMeta {
202+
label: String!
203+
description: String
204+
fields: [KeystoneAdminUIFieldMeta!]!
205+
}
206+
207+
type KeystoneAdminUISort {
208+
field: String!
209+
direction: KeystoneAdminUISortDirection!
210+
}
211+
212+
enum KeystoneAdminUISortDirection {
213+
ASC
214+
DESC
215+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// This file is automatically generated by Keystone, do not modify it manually.
2+
// Modify your Keystone config when you want to change this.
3+
4+
datasource sqlite {
5+
url = env("DATABASE_URL")
6+
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
7+
provider = "sqlite"
8+
}
9+
10+
generator client {
11+
provider = "prisma-client-js"
12+
output = "node_modules/.myprisma/client"
13+
}
14+
15+
model Post {
16+
id String @id @default(cuid())
17+
title String @default("")
18+
content String @default("")
19+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { list } from '@keystone-6/core';
2+
import { text } from '@keystone-6/core/fields';
3+
import type { Session } from 'next-auth';
4+
import type { Lists } from '.keystone/types';
5+
6+
// WARNING: this example is for demonstration purposes only
7+
// as with each of our examples, it has not been vetted
8+
// or tested for any particular usage
9+
10+
function hasSession({ session }: { session?: Session }) {
11+
return Boolean(session);
12+
}
13+
14+
export const lists: Lists = {
15+
Post: list({
16+
// WARNING - for this example, anyone can create, query, update and delete anything
17+
access: hasSession,
18+
19+
fields: {
20+
title: text({ validation: { isRequired: true } }),
21+
22+
// the document field can be used for making rich editable content
23+
// learn more at https://keystonejs.com/docs/guides/document-fields
24+
content: text(),
25+
},
26+
}),
27+
};

0 commit comments

Comments
 (0)