Skip to content

Commit 89e749c

Browse files
committed
feat(withPayload): Add custom evens to withPayload function
1 parent d20f863 commit 89e749c

File tree

3 files changed

+140
-5
lines changed

3 files changed

+140
-5
lines changed

packages/dev/src/auth.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,24 @@ import { withPayload } from "payload-authjs";
44
import { authConfig } from "./auth.config";
55

66
export const { handlers, signIn, signOut, auth } = NextAuth(
7-
withPayload(authConfig, {
7+
withPayload(authConfig as any, {
88
payloadConfig,
9-
updateUserOnSignIn: true,
10-
}),
9+
events: {
10+
/**
11+
* Update user on every sign in
12+
*/
13+
signIn: async ({ adapter, payload, user, profile }) => {
14+
payload?.logger.info("signIn event");
15+
if (!user.id || !profile) {
16+
return;
17+
}
18+
await adapter.updateUser!({
19+
id: user.id,
20+
name: profile.name ?? (profile.login as string | undefined),
21+
image: profile.avatar_url as string | undefined,
22+
additionalUserDatabaseField: `Create by updateUserOnSignIn at ${new Date().toISOString()}`,
23+
});
24+
},
25+
},
26+
}) as any,
1127
);

packages/payload-authjs/README.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ const authConfig: NextAuthConfig = {
177177
};
178178
```
179179

180-
⚠ Keep in mind that Auth.js doesn't update the user after the first sign-in.
180+
> ⚠ Keep in mind that Auth.js doesn't update the user after the first sign-in. If you want to update the user on every sign-in, you can use the `signIn` event. (See [Events](#events))
181181
182182
### 2. Virtual fields (jwt session only)
183183

@@ -251,6 +251,48 @@ const Examples: CollectionConfig = {
251251

252252
</details>
253253

254+
## 🎉 Events
255+
256+
Auth.js emits some [events](https://authjs.dev/reference/nextjs#events) that you can listen to. This plugin extends the events with additional parameters like the `adapter` and `payload` instance.
257+
258+
_More information about the events can be found in the [Auth.js documentation](https://authjs.dev/reference/nextjs#events)._
259+
260+
The following events are available:
261+
262+
- signIn
263+
- signOut
264+
- createUser
265+
- updateUser
266+
- linkAccount
267+
- session
268+
269+
## `signIn` Event
270+
271+
The `signIn` event is emitted when a user successfully signs in. For example, you could use this event to update the user's name on every sign-in:
272+
273+
```ts
274+
// auth.ts
275+
export const { handlers, signIn, signOut, auth } = NextAuth(
276+
withPayload(authConfig, {
277+
payloadConfig,
278+
events: {
279+
/**
280+
* Update user 'name' on every sign in
281+
*/
282+
signIn: async ({ adapter, user, profile }) => {
283+
if (!user.id || !profile) {
284+
return;
285+
}
286+
await adapter.updateUser!({
287+
id: user.id,
288+
name: profile.name ?? (profile.login as string | undefined),
289+
});
290+
},
291+
},
292+
}),
293+
);
294+
```
295+
254296
## 📐 Utility functions
255297

256298
This plugin also export a utility function to get the current payload user in the server-side:

packages/payload-authjs/src/authjs/withPayload.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,63 @@
11
import type { NextAuthConfig } from "next-auth";
2+
import type { Adapter } from "next-auth/adapters";
3+
import type { Payload } from "payload";
4+
import { deepCopyObjectSimple, getPayload } from "payload";
25
import { PayloadAdapter, type PayloadAdapterOptions } from "./PayloadAdapter";
36

7+
type CustomEvent<T extends keyof NonNullable<NextAuthConfig["events"]>> = (
8+
message: {
9+
/**
10+
* The Auth.js database adapter
11+
*/
12+
adapter: Adapter;
13+
/**
14+
* The payload instance
15+
*/
16+
payload?: Payload;
17+
} & Parameters<NonNullable<NonNullable<NextAuthConfig["events"]>[T]>>[0],
18+
) => void | PromiseLike<void>;
19+
420
export interface WithPayloadOptions extends PayloadAdapterOptions {
521
/**
622
* Update the user after every sign in
723
*
24+
* @deprecated updateUserOnSignIn is deprecated and will be removed in the future. Use events.signIn instead
25+
*
826
* @default false
927
*/
1028
updateUserOnSignIn?: boolean;
29+
/**
30+
* Auth.js events
31+
* All events from authjs will be forwarded and enriched with additional parameters like the adapter and the payload instance
32+
*
33+
* @see https://authjs.dev/reference/nextjs#events
34+
*/
35+
events?: {
36+
/**
37+
* Sign in event
38+
*/
39+
signIn?: CustomEvent<"signIn">;
40+
/**
41+
* Sign out event
42+
*/
43+
signOut?: CustomEvent<"signOut">;
44+
/**
45+
* Create user event
46+
*/
47+
createUser?: CustomEvent<"createUser">;
48+
/**
49+
* Update user event
50+
*/
51+
updateUser?: CustomEvent<"updateUser">;
52+
/**
53+
* Link account event
54+
*/
55+
linkAccount?: CustomEvent<"linkAccount">;
56+
/**
57+
* Session event
58+
*/
59+
session?: CustomEvent<"session">;
60+
};
1161
}
1262

1363
/**
@@ -22,7 +72,7 @@ export interface WithPayloadOptions extends PayloadAdapterOptions {
2272
*/
2373
export function withPayload(
2474
authjsConfig: NextAuthConfig,
25-
{ updateUserOnSignIn, ...options }: WithPayloadOptions,
75+
{ updateUserOnSignIn, events, ...options }: WithPayloadOptions,
2676
): NextAuthConfig {
2777
authjsConfig = { ...authjsConfig };
2878

@@ -71,5 +121,32 @@ export function withPayload(
71121
};
72122
}
73123

124+
// Forward all events from authjs and enrich them with additional parameters
125+
const originalEvents = deepCopyObjectSimple(authjsConfig.events ?? {});
126+
authjsConfig.events = Object.entries(events ?? {}).reduce((result, [key, customEvent]) => {
127+
const originalEvent = result[key as keyof typeof result];
128+
129+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
130+
result[key as keyof typeof result] = async (message: any) => {
131+
// Call the original event
132+
await originalEvent?.(message);
133+
134+
// Call the custom event
135+
await customEvent?.({
136+
...message,
137+
// Add the adapter to the event
138+
adapter: authjsConfig.adapter!,
139+
// Add payload to the event
140+
payload: options.payload
141+
? await options.payload
142+
: options.payloadConfig
143+
? await getPayload({ config: options.payloadConfig })
144+
: undefined,
145+
});
146+
};
147+
148+
return result;
149+
}, originalEvents);
150+
74151
return authjsConfig;
75152
}

0 commit comments

Comments
 (0)