Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ts): improve events handlers' types #1853

Merged
merged 19 commits into from
May 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion types/adapters.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AppOptions } from "./internals"
import { User, Profile, Session } from "."
import { EmailConfig, SendVerificationRequest } from "./providers"
import { EmailConfig } from "./providers"
import { ConnectionOptions } from "typeorm"

/** Legacy */
Expand Down
69 changes: 55 additions & 14 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Minimum TypeScript Version: 3.5
// Minimum TypeScript Version: 3.6

/// <reference types="node" />

Expand Down Expand Up @@ -111,7 +111,7 @@ export interface NextAuthOptions {
*
* [Documentation](https://next-auth.js.org/configuration/options#events) | [Events documentation](https://next-auth.js.org/configuration/events)
*/
events?: EventsOptions
events?: Partial<JWTEventCallbacks | SessionEventCallbacks>
/**
* By default NextAuth.js uses a database adapter that uses TypeORM and supports MySQL, MariaDB, Postgres and MongoDB and SQLite databases.
* An alternative adapter that uses Prisma, which currently supports MySQL, MariaDB and Postgres, is also included.
Expand Down Expand Up @@ -350,20 +350,61 @@ export interface CookiesOptions {
}

/** [Documentation](https://next-auth.js.org/configuration/events) */
export type EventType =
| "signIn"
| "signOut"
| "createUser"
| "updateUser"
| "linkAccount"
| "session"
| "error"
export type EventCallback<MessageType = unknown> = (
message: MessageType
) => Promise<void>
balazsorban44 marked this conversation as resolved.
Show resolved Hide resolved

/** [Documentation](https://next-auth.js.org/configuration/events) */
export type EventCallback = (message: any) => Promise<void>
/**
* If using a `credentials` type auth, the user is the raw response from your
* credential provider.
* For other providers, you'll get the User object from your adapter, the account,
* and an indicator if the user was new to your Adapter.
*/
export interface SignInEventMessage {
user: User
account: Account
isNewUser?: boolean
}

/** [Documentation](https://next-auth.js.org/configuration/events) */
export type EventsOptions = Partial<Record<EventType, EventCallback>>
export interface LinkAccountEventMessage {
user: User
providerAccount: Record<string, unknown>
}

/**
* The various event callbacks you can register for from next-auth
*/
export interface CommonEventCallbacks {
signIn: EventCallback<SignInEventMessage>
createUser: EventCallback<User>
updateUser: EventCallback<User>
linkAccount: EventCallback<LinkAccountEventMessage>
error: EventCallback
}
/**
* The event callbacks will take this form if you are using JWTs:
* signOut will receive the JWT and session will receive the session and JWT.
*/
export interface JWTEventCallbacks extends CommonEventCallbacks {
signOut: EventCallback<JWT>
session: EventCallback<{
session: Session
jwt: JWT
}>
}
/**
* The event callbacks will take this form if you are using Sessions
* and not using JWTs:
* signOut will receive the underlying DB adapter's session object, and session
* will receive the NextAuth client session with extra data.
*/
export interface SessionEventCallbacks extends CommonEventCallbacks {
signOut: EventCallback<Session | null>
session: EventCallback<{ session: Session }>
}
export type EventCallbacks = JWTEventCallbacks | SessionEventCallbacks

export type EventType = keyof EventCallbacks

/** [Documentation](https://next-auth.js.org/configuration/pages) */
export interface PagesOptions {
Expand Down
24 changes: 11 additions & 13 deletions types/tests/server.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import Providers, {
AppProvider,
EmailConfig,
OAuthConfig,
} from "next-auth/providers"
import { Adapter, AdapterInstance } from "next-auth/adapters"
import Providers, { OAuthConfig } from "next-auth/providers"
import { Adapter } from "next-auth/adapters"
import NextAuth, * as NextAuthTypes from "next-auth"
import { IncomingMessage, ServerResponse } from "http"
import * as JWTType from "next-auth/jwt"
import { Socket } from "net"
import { NextApiRequest, NextApiResponse } from "internals/utils"
import { AppOptions } from "internals"
Expand Down Expand Up @@ -173,22 +168,25 @@ const allConfig: NextAuthTypes.NextAuthOptions = {
},
},
events: {
async signIn(message) {
async signIn(message: NextAuthTypes.SignInEventMessage) {
return undefined
},
async signOut(message) {
async signOut(message: NextAuthTypes.Session | null) {
return undefined
},
async createUser(message) {
async createUser(message: NextAuthTypes.User) {
return undefined
},
async linkAccount(message) {
async updateUser(message: NextAuthTypes.User) {
return undefined
},
async session(message) {
async linkAccount(message: NextAuthTypes.LinkAccountEventMessage) {
return undefined
},
async error(message) {
async session(message: NextAuthTypes.Session) {
return undefined
},
async error(message: any) {
return undefined
},
},
Expand Down
71 changes: 57 additions & 14 deletions www/docs/configuration/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,60 @@ Events are asynchronous functions that do not return a response, they are useful

You can specify a handler for any of these events below, for debugging or for an audit log.

```js title="pages/api/auth/[...nextauth].js"
...
events: {
async signIn(message) { /* on successful sign in */ },
async signOut(message) { /* on signout */ },
async createUser(message) { /* user created */ },
async linkAccount(message) { /* account linked to a user */ },
async session(message) { /* session is active */ },
async error(message) { /* error in authentication flow */ }
}
...
```

The content of the message object varies depending on the flow (e.g. OAuth or Email authentication flow, JWT or database sessions, etc) but typically contains a user object and/or contents of the JSON Web Token and other information relevant to the event.
:::note
Execution of your auth API will be blocked by an `await` on your event handler. If your event handler starts any burdensome work it should not block its own promise on that work.
:::

## Events

### signIn

Sent on successful sign in.

The message will be an object and contain:

- `user` (from your adapter or from the provider if a `credentials` type provider)
- `account` (from your adapter or the provider)
- `isNewUser` (whether your adapter had a user for this account already)

### signOut

Sent when the user signs out.

The message object is the JWT, if using them, or the adapter session object for the session that is being ended.

### createUser

Sent when the adapter is told to create a new user.

The message object will be the user.

### updateUser

Sent when the adapter is told to update an existing user. Currently this is only sent when the user verifies their email address.

The message object will be the user.

### linkAccount

Sent when an account in a given provider is linked to a user in our userbase. For example, when a user signs up with Twitter or when an existing user links their Google account.

The message will be an object and contain:

- `user`: The user object from your adapter
- `providerAccount`: The object returned from the provider.

### session

Sent at the end of a request for the current session.

The message will be an object and contain:

- `session`: The session object from your adapter
- `jwt`: If using JWT, the token for this session.

### error

Sent when an error occurs

The message could be any object relevant to describing the error.