Skip to content

Commit 18810ec

Browse files
committed
feat: Improved users collection & add better support for customization
- Omit sessions field from collection if your are not using database session - Omit versification fields from collection if your not using an email provider - Add index to verificationTokens.token and sessions.sessionToken
1 parent c621343 commit 18810ec

File tree

14 files changed

+499
-259
lines changed

14 files changed

+499
-259
lines changed

packages/dev/src/payload-types.ts

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,19 @@ export interface UserAuthOperations {
6262
* via the `definition` "users".
6363
*/
6464
export interface User {
65-
roles?: string[];
6665
id: string;
6766
email: string;
67+
emailVerified?: string | null;
6868
name?: string | null;
6969
image?: string | null;
70-
emailVerified?: string | null;
70+
roles?: string[];
7171
accounts?:
7272
| {
7373
id?: string | null;
7474
provider: string;
7575
providerAccountId: string;
7676
type: string;
77-
}[]
78-
| null;
79-
sessions?:
80-
| {
81-
id?: string | null;
82-
sessionToken: string;
83-
expires: string;
77+
access_token?: string | null;
8478
}[]
8579
| null;
8680
verificationTokens?:
@@ -165,26 +159,20 @@ export interface PayloadMigration {
165159
* via the `definition` "users_select".
166160
*/
167161
export interface UsersSelect<T extends boolean = true> {
168-
roles?: T;
169162
id?: T;
170163
email?: T;
164+
emailVerified?: T;
171165
name?: T;
172166
image?: T;
173-
emailVerified?: T;
167+
roles?: T;
174168
accounts?:
175169
| T
176170
| {
177171
id?: T;
178172
provider?: T;
179173
providerAccountId?: T;
180174
type?: T;
181-
};
182-
sessions?:
183-
| T
184-
| {
185-
id?: T;
186-
sessionToken?: T;
187-
expires?: T;
175+
access_token?: T;
188176
};
189177
verificationTokens?:
190178
| T

packages/dev/src/payload/collections/users.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,31 @@ const Users: CollectionConfig = {
1111
}, */
1212
},
1313
fields: [
14+
{
15+
name: "id",
16+
type: "text",
17+
label: "Identifier",
18+
admin: {
19+
hidden: true, // Hide id field in admin panel
20+
},
21+
},
22+
{
23+
name: "accounts",
24+
type: "array",
25+
fields: [
26+
{
27+
name: "provider",
28+
type: "text",
29+
label: "Account Provider", // Add label to provider field
30+
},
31+
// Add new field to accounts
32+
{
33+
name: "access_token",
34+
type: "text",
35+
},
36+
],
37+
},
38+
// Add custom field
1439
{
1540
name: "roles",
1641
type: "json",

packages/payload-authjs/README.md

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,63 @@ export const config = buildConfig({
6666

6767
**And that's it! Now you can sign-in via Auth.js and you are automatically authenticated in Payload CMS. Nice 🎉**
6868

69+
> You don't need to create a collection for users. This plugin automatically creates a collection with the slug `users`.
70+
6971
---
7072

7173
## Customizing
7274

73-
You don't need to create a collection for users. This plugin automatically creates a collection with the slug `users`.
75+
<details>
76+
<summary>Customizing the users collection</summary>
7477

75-
But if you want to customize the users collection, you can create a collection with the slug `users` and add the fields you need.
78+
If you want to customize the users collection, you can create a collection with the slug `users` and add your customizations there.
7679

7780
```ts
7881
// users.ts
79-
import type { CollectionConfig } from "payload";
82+
const Users: CollectionConfig = {
83+
slug: "users",
84+
fields: [],
85+
};
86+
```
87+
88+
### Customize existing fields
89+
90+
You can customize the existing fields in the users collection by adding the field to the collection and modifying the field configuration. The fields will be merged together.
8091

92+
```ts
93+
// users.ts
94+
const Users: CollectionConfig = {
95+
slug: "users",
96+
fields: [
97+
{
98+
name: "id",
99+
type: "text",
100+
label: "Identifier", // <-- Add a label to the id field
101+
admin: {
102+
hidden: true, // <-- Hide id field in admin panel
103+
},
104+
},
105+
{
106+
name: "accounts",
107+
type: "array",
108+
fields: [
109+
{
110+
name: "provider",
111+
type: "text",
112+
label: "Account Provider", // <-- Add label to provider field
113+
},
114+
],
115+
},
116+
],
117+
};
118+
```
119+
120+
### Add additional fields
121+
122+
You can also add additional fields to the users collection. For example, you could add a `roles` field to the users collection:
123+
124+
```ts
125+
// users.ts
81126
const Users: CollectionConfig = {
82127
slug: "users",
83128
fields: [
@@ -87,8 +132,6 @@ const Users: CollectionConfig = {
87132
},
88133
],
89134
};
90-
91-
export default Users;
92135
```
93136

94137
Next, you need to extend the user object returned by your Auth.js provider. You can do this like this example:
@@ -100,7 +143,7 @@ const authConfig: NextAuthConfig = {
100143
profile(profile) {
101144
return {
102145
id: profile.id.toString(),
103-
name: profile.name,
146+
name: profile.name ?? profile.login,
104147
email: profile.email,
105148
image: profile.avatar_url,
106149
roles: ["user"], // <-- Extend the user object with a custom field
@@ -140,6 +183,8 @@ const Examples: CollectionConfig = {
140183
};
141184
```
142185

186+
</details>
187+
143188
### Utility functions
144189

145190
This plugin also export a utility function to get the current payload user
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { Access, CollectionConfig, User } from "payload";
2+
3+
const myselfAccess: Access<User> = ({ req: { user } }) => {
4+
// User is required
5+
if (!user) {
6+
return false;
7+
}
8+
9+
// Only allow to read, update and delete current user
10+
return {
11+
id: {
12+
equals: user.id,
13+
},
14+
};
15+
};
16+
17+
/**
18+
* Override the default access control.
19+
* Only allow users to read, update and delete their own user
20+
*/
21+
export const defaultAccess: CollectionConfig["access"] = {
22+
read: myselfAccess,
23+
readVersions: () => false,
24+
create: () => false,
25+
update: myselfAccess,
26+
delete: myselfAccess,
27+
unlock: () => false,
28+
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import NextAuth from "next-auth";
2+
import { NextResponse } from "next/server";
3+
import type { Endpoint } from "payload";
4+
import { withPayload } from "../../../authjs/withPayload";
5+
import type { AuthjsPluginConfig } from "../../plugin";
6+
7+
/**
8+
* Override the default logout endpoint to destroy the authjs session
9+
*
10+
* @see https://payloadcms.com/docs/authentication/operations#logout
11+
* @see https://github.com/payloadcms/payload/blob/main/packages/payload/src/auth/endpoints/logout.ts
12+
*/
13+
export const logoutEndpoint: (pluginOptions: AuthjsPluginConfig) => Endpoint = pluginOptions => ({
14+
method: "post",
15+
path: "/logout",
16+
handler: async req => {
17+
// Sign out and get cookies from authjs
18+
const { signOut } = NextAuth(
19+
withPayload(pluginOptions.authjsConfig, {
20+
payload: req.payload,
21+
userCollectionSlug: pluginOptions.userCollectionSlug,
22+
}),
23+
);
24+
const { cookies } = await signOut({ redirect: false });
25+
26+
// Create response with cookies
27+
const response = NextResponse.json({
28+
message: req.t("authentication:logoutSuccessful"),
29+
});
30+
for (const cookie of cookies) {
31+
response.cookies.set(cookie.name, cookie.value, cookie.options);
32+
}
33+
34+
return response;
35+
},
36+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { Field } from "payload";
2+
3+
/**
4+
* Accounts field for a user
5+
*
6+
* @see https://authjs.dev/concepts/database-models
7+
*/
8+
export const accountsField: Field = {
9+
name: "accounts",
10+
type: "array",
11+
fields: [
12+
{
13+
name: "id",
14+
type: "text",
15+
admin: {
16+
disabled: true,
17+
},
18+
},
19+
{
20+
type: "row",
21+
fields: [
22+
{ name: "provider", type: "text", required: true },
23+
{ name: "providerAccountId", type: "text", required: true, index: true },
24+
{ name: "type", type: "text", required: true },
25+
],
26+
},
27+
],
28+
admin: {
29+
initCollapsed: true,
30+
},
31+
access: {
32+
create: () => false,
33+
update: () => false,
34+
},
35+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { Field } from "payload";
2+
3+
/**
4+
* General fields for a user
5+
*
6+
* @see https://authjs.dev/concepts/database-models
7+
*/
8+
export const generalFields: Field[] = [
9+
{
10+
type: "row",
11+
fields: [
12+
{
13+
name: "email",
14+
type: "email",
15+
required: true,
16+
unique: true,
17+
index: true,
18+
},
19+
{
20+
name: "emailVerified",
21+
type: "date",
22+
},
23+
],
24+
},
25+
{
26+
name: "name",
27+
type: "text",
28+
},
29+
{
30+
name: "image",
31+
type: "text",
32+
},
33+
];
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { Field } from "payload";
2+
3+
/**
4+
* Sessions field for a user
5+
*
6+
* @see https://authjs.dev/concepts/database-models
7+
*/
8+
export const sessionsField: Field = {
9+
name: "sessions",
10+
type: "array",
11+
fields: [
12+
{
13+
name: "id",
14+
type: "text",
15+
admin: {
16+
disabled: true,
17+
},
18+
},
19+
{
20+
type: "row",
21+
fields: [
22+
{ name: "sessionToken", type: "text", required: true, index: true },
23+
{ name: "expires", type: "date", required: true },
24+
],
25+
},
26+
],
27+
admin: {
28+
initCollapsed: true,
29+
},
30+
access: {
31+
create: () => false,
32+
update: () => false,
33+
},
34+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { Field } from "payload";
2+
3+
/**
4+
* Verification tokens field for a user
5+
*
6+
* @see https://authjs.dev/concepts/database-models
7+
*/
8+
export const verificationTokensField: Field = {
9+
name: "verificationTokens",
10+
type: "array",
11+
fields: [
12+
{
13+
name: "id",
14+
type: "text",
15+
admin: {
16+
disabled: true,
17+
},
18+
},
19+
{
20+
type: "row",
21+
fields: [
22+
{ name: "token", type: "text", required: true, index: true },
23+
{ name: "expires", type: "date", required: true },
24+
],
25+
},
26+
],
27+
admin: {
28+
initCollapsed: true,
29+
},
30+
access: {
31+
create: () => false,
32+
update: () => false,
33+
},
34+
};

0 commit comments

Comments
 (0)