Skip to content

Commit 1e886b9

Browse files
author
Francis Gulotta
authored
fix(EmailProvider): proper required fields and allow all nodemailer types (#8016)
1 parent ecb14cc commit 1e886b9

File tree

1 file changed

+82
-51
lines changed

1 file changed

+82
-51
lines changed

packages/core/src/providers/email.ts

Lines changed: 82 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1-
import { createTransport } from "nodemailer"
2-
31
import type { CommonProviderOptions } from "./index.js"
4-
import type { Options as SMTPTransportOptions } from "nodemailer/lib/smtp-transport"
52
import type { Awaitable, Theme } from "../types.js"
63

4+
import { Transport, TransportOptions, createTransport } from "nodemailer"
5+
import * as JSONTransport from "nodemailer/lib/json-transport/index.js"
6+
import * as SendmailTransport from "nodemailer/lib/sendmail-transport/index.js"
7+
import * as SESTransport from "nodemailer/lib/ses-transport/index.js"
8+
import * as SMTPTransport from "nodemailer/lib/smtp-transport/index.js"
9+
import * as SMTPPool from "nodemailer/lib/smtp-pool/index.js"
10+
import * as StreamTransport from "nodemailer/lib/stream-transport/index.js"
11+
12+
// TODO: Make use of https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html for the string
13+
type AllTransportOptions = string | SMTPTransport | SMTPTransport.Options | SMTPPool | SMTPPool.Options | SendmailTransport | SendmailTransport.Options | StreamTransport | StreamTransport.Options | JSONTransport | JSONTransport.Options | SESTransport | SESTransport.Options | Transport<any> | TransportOptions
14+
715
export interface SendVerificationRequestParams {
816
identifier: string
917
url: string
@@ -26,10 +34,9 @@ export interface SendVerificationRequestParams {
2634
*
2735
* [Custom email service with Auth.js](https://authjs.dev/guides/providers/email#custom-email-service)
2836
*/
29-
export interface EmailConfig extends CommonProviderOptions {
30-
type: "email"
31-
// TODO: Make use of https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html
32-
server?: string | SMTPTransportOptions
37+
export interface EmailUserConfig {
38+
server?: AllTransportOptions
39+
type?: "email"
3340
/** @default `"Auth.js <[email protected]>"` */
3441
from?: string
3542
/**
@@ -40,7 +47,7 @@ export interface EmailConfig extends CommonProviderOptions {
4047
*/
4148
maxAge?: number
4249
/** [Documentation](https://authjs.dev/guides/providers/email#customizing-emails) */
43-
sendVerificationRequest: (
50+
sendVerificationRequest?: (
4451
params: SendVerificationRequestParams
4552
) => Awaitable<void>
4653
/**
@@ -77,24 +84,49 @@ export interface EmailConfig extends CommonProviderOptions {
7784
normalizeIdentifier?: (identifier: string) => string
7885
}
7986

87+
export interface EmailConfig extends CommonProviderOptions {
88+
// defaults
89+
id: "email"
90+
type: "email"
91+
name: "Email"
92+
server: AllTransportOptions
93+
from: string
94+
maxAge: number
95+
sendVerificationRequest: (
96+
params: SendVerificationRequestParams
97+
) => Awaitable<void>
98+
99+
/**
100+
* This is copied into EmailConfig in parseProviders() don't use elsewhere
101+
*/
102+
options: EmailUserConfig
103+
104+
// user options
105+
// TODO figure out a better way than copying from EmailUserConfig
106+
secret?: string
107+
generateVerificationToken?: () => Awaitable<string>
108+
normalizeIdentifier?: (identifier: string) => string
109+
}
110+
111+
80112
// TODO: Rename to Token provider
81113
// when started working on https://github.com/nextauthjs/next-auth/discussions/1465
82114
export type EmailProviderType = "email"
83115

84-
/**
116+
/**
85117
* ## Overview
86118
* The Email provider uses email to send "magic links" that can be used to sign in, you will likely have seen these if you have used services like Slack before.
87-
*
119+
*
88120
* Adding support for signing in via email in addition to one or more OAuth services provides a way for users to sign in if they lose access to their OAuth account (e.g. if it is locked or deleted).
89-
*
121+
*
90122
* The Email provider can be used in conjunction with (or instead of) one or more OAuth providers.
91123
* ### How it works
92-
*
124+
*
93125
* On initial sign in, a **Verification Token** is sent to the email address provided. By default this token is valid for 24 hours. If the Verification Token is used within that time (i.e. by clicking on the link in the email) an account is created for the user and they are signed in.
94-
*
95-
*
126+
*
127+
*
96128
* If someone provides the email address of an _existing account_ when signing in, an email is sent and they are signed into the account associated with that email address when they follow the link in the email.
97-
*
129+
*
98130
* :::tip
99131
* The Email Provider can be used with both JSON Web Tokens and database sessions, but you **must** configure a database to use it. It is not possible to enable email sign in without using a database.
100132
* :::
@@ -103,20 +135,20 @@ export type EmailProviderType = "email"
103135
* 1. NextAuth.js does not include `nodemailer` as a dependency, so you'll need to install it yourself if you want to use the Email Provider. Run `npm install nodemailer` or `yarn add nodemailer`.
104136
* 2. You will need an SMTP account; ideally for one of the [services known to work with `nodemailer`](https://community.nodemailer.com/2-0-0-beta/setup-smtp/well-known-services/).
105137
* 3. There are two ways to configure the SMTP server connection.
106-
*
138+
*
107139
* You can either use a connection string or a `nodemailer` configuration object.
108-
*
140+
*
109141
* 3.1 **Using a connection string**
110-
*
142+
*
111143
* Create an `.env` file to the root of your project and add the connection string and email address.
112-
*
144+
*
113145
* ```js title=".env" {1}
114146
* EMAIL_SERVER=smtp://username:[email protected]:587
115147
116148
* ```
117-
*
149+
*
118150
* Now you can add the email provider like this:
119-
*
151+
*
120152
* ```js {3} title="pages/api/auth/[...nextauth].js"
121153
* import EmailProvider from "next-auth/providers/email";
122154
* ...
@@ -127,21 +159,21 @@ export type EmailProviderType = "email"
127159
* }),
128160
* ],
129161
* ```
130-
*
162+
*
131163
* 3.2 **Using a configuration object**
132-
*
164+
*
133165
* In your `.env` file in the root of your project simply add the configuration object options individually:
134-
*
166+
*
135167
* ```js title=".env"
136168
* EMAIL_SERVER_USER=username
137169
* EMAIL_SERVER_PASSWORD=password
138170
* EMAIL_SERVER_HOST=smtp.example.com
139171
* EMAIL_SERVER_PORT=587
140172
141173
* ```
142-
*
174+
*
143175
* Now you can add the provider settings to the NextAuth.js options object in the Email Provider.
144-
*
176+
*
145177
* ```js title="pages/api/auth/[...nextauth].js"
146178
* import EmailProvider from "next-auth/providers/email";
147179
* ...
@@ -159,19 +191,19 @@ export type EmailProviderType = "email"
159191
* }),
160192
* ],
161193
* ```
162-
*
194+
*
163195
* 4. Do not forget to setup one of the database [adapters](https://authjs.dev/reference/adapters) for storing the Email verification token.
164-
*
196+
*
165197
* 5. You can now sign in with an email address at `/api/auth/signin`.
166-
*
198+
*
167199
* A user account (i.e. an entry in the Users table) will not be created for the user until the first time they verify their email address. If an email address is already associated with an account, the user will be signed in to that account when they use the link in the email.
168-
*
200+
*
169201
* ## Customizing emails
170-
*
202+
*
171203
* You can fully customize the sign in email that is sent by passing a custom function as the `sendVerificationRequest` option to `EmailProvider()`.
172-
*
204+
*
173205
* e.g.
174-
*
206+
*
175207
* ```js {3} title="pages/api/auth/[...nextauth].js"
176208
* import EmailProvider from "next-auth/providers/email";
177209
* ...
@@ -189,12 +221,12 @@ export type EmailProviderType = "email"
189221
* }),
190222
* ]
191223
* ```
192-
*
224+
*
193225
* The following code shows the complete source for the built-in `sendVerificationRequest()` method:
194-
*
226+
*
195227
* ```js
196228
* import { createTransport } from "nodemailer"
197-
*
229+
*
198230
* async function sendVerificationRequest(params) {
199231
* const { identifier, url, provider, theme } = params
200232
* const { host } = new URL(url)
@@ -212,12 +244,12 @@ export type EmailProviderType = "email"
212244
* throw new Error(`Email(s) (${failed.join(", ")}) could not be sent`)
213245
* }
214246
* }
215-
*
247+
*
216248
* function html(params: { url: string; host: string; theme: Theme }) {
217249
* const { url, host, theme } = params
218-
*
250+
*
219251
* const escapedHost = host.replace(/\./g, "&#8203;.")
220-
*
252+
*
221253
* const brandColor = theme.brandColor || "#346df1"
222254
* const color = {
223255
* background: "#f9f9f9",
@@ -227,7 +259,7 @@ export type EmailProviderType = "email"
227259
* buttonBorder: brandColor,
228260
* buttonText: theme.buttonText || "#fff",
229261
* }
230-
*
262+
*
231263
* return `
232264
* <body style="background: ${color.background};">
233265
* <table width="100%" border="0" cellspacing="20" cellpadding="0"
@@ -260,21 +292,21 @@ export type EmailProviderType = "email"
260292
* </body>
261293
* `
262294
* }
263-
*
295+
*
264296
* // Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)
265297
* function text({ url, host }: { url: string; host: string }) {
266298
* return `Sign in to ${host}\n${url}\n\n`
267299
* }
268300
* ```
269-
*
301+
*
270302
* :::tip
271303
* If you want to generate great looking email client compatible HTML with React, check out https://mjml.io
272304
* :::
273-
*
305+
*
274306
* ## Customizing the Verification Token
275-
*
307+
*
276308
* By default, we are generating a random verification token. You can define a `generateVerificationToken` method in your provider options if you want to override it:
277-
*
309+
*
278310
* ```js title="pages/api/auth/[...nextauth].js"
279311
* providers: [
280312
* EmailProvider({
@@ -284,9 +316,9 @@ export type EmailProviderType = "email"
284316
* })
285317
* ],
286318
* ```
287-
*
319+
*
288320
* ## Normalizing the email address
289-
*
321+
*
290322
* By default, Auth.js will normalize the email address. It treats values as case-insensitive (which is technically not compliant to the [RFC 2821 spec](https://datatracker.ietf.org/doc/html/rfc2821), but in practice this causes more problems than it solves, eg. when looking up users by e-mail from databases.) and also removes any secondary email address that was passed in as a comma-separated list. You can apply your own normalization via the `normalizeIdentifier` method on the `EmailProvider`. The following example shows the default behavior:
291323
* ```ts
292324
* EmailProvider({
@@ -299,7 +331,7 @@ export type EmailProviderType = "email"
299331
* // but we remove it on the domain part
300332
* domain = domain.split(",")[0]
301333
* return `${local}@${domain}`
302-
*
334+
*
303335
* // You can also throw an error, which will redirect the user
304336
* // to the sign-in page with error=EmailSignin in the URL
305337
* // if (identifier.split("@").length > 2) {
@@ -308,12 +340,12 @@ export type EmailProviderType = "email"
308340
* },
309341
* })
310342
* ```
311-
*
343+
*
312344
* :::warning
313345
* Always make sure this returns a single e-mail address, even if multiple ones were passed in.
314346
* :::
315347
*/
316-
export default function Email(config: EmailConfig): EmailConfig {
348+
export default function Email(config: EmailUserConfig): EmailConfig {
317349
return {
318350
id: "email",
319351
type: "email",
@@ -337,7 +369,6 @@ export default function Email(config: EmailConfig): EmailConfig {
337369
throw new Error(`Email (${failed.join(", ")}) could not be sent`)
338370
}
339371
},
340-
// @ts-expect-error
341372
options: config,
342373
}
343374
}

0 commit comments

Comments
 (0)