mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
1 Commits
next-auth@
...
next-auth@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10bdea24b4 |
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -70,7 +70,6 @@ jobs:
|
|||||||
pnpm release
|
pnpm release
|
||||||
env:
|
env:
|
||||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
|
||||||
NPM_TOKEN_PKG: ${{ secrets.NPM_TOKEN_PKG }}
|
NPM_TOKEN_PKG: ${{ secrets.NPM_TOKEN_PKG }}
|
||||||
NPM_TOKEN_ORG: ${{ secrets.NPM_TOKEN_ORG }}
|
NPM_TOKEN_ORG: ${{ secrets.NPM_TOKEN_ORG }}
|
||||||
release-pr:
|
release-pr:
|
||||||
|
|||||||
@@ -46,10 +46,7 @@ import BoxyHQSAMLProvider from "next-auth/providers/boxyhq-saml"
|
|||||||
// })
|
// })
|
||||||
// const adapter = FaunaAdapter(client)
|
// const adapter = FaunaAdapter(client)
|
||||||
export const authOptions: NextAuthOptions = {
|
export const authOptions: NextAuthOptions = {
|
||||||
// adapter: {
|
// adapter,
|
||||||
// getUserByEmail: (email) => ({ id: "1", email, emailVerified: null }),
|
|
||||||
// createVerificationToken: (token) => token,
|
|
||||||
// } as any,
|
|
||||||
providers: [
|
providers: [
|
||||||
// E-mail
|
// E-mail
|
||||||
// Start fake e-mail server with `npm run start:email`
|
// Start fake e-mail server with `npm run start:email`
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ This is the Dgraph Adapter for [`next-auth`](https://next-auth.js.org).
|
|||||||
|
|
||||||
1. Install the necessary packages
|
1. Install the necessary packages
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install next-auth @next-auth/dgraph-adapter
|
npm install next-auth @next-auth/dgraph-adapter
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -226,22 +226,22 @@ database you must customize next-auth `encode` and `decode` functions, as the de
|
|||||||
further customize the jwt with roles if you want to implement [`RBAC logic`](https://dgraph.io/docs/graphql/authorization/directive/#role-based-access-control).
|
further customize the jwt with roles if you want to implement [`RBAC logic`](https://dgraph.io/docs/graphql/authorization/directive/#role-based-access-control).
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import * as jwt from "jsonwebtoken"
|
import * as jwt from "jsonwebtoken";
|
||||||
export default NextAuth({
|
export default NextAuth({
|
||||||
session: {
|
session: {
|
||||||
strategy: "jwt",
|
strategy: "jwt"
|
||||||
},
|
},
|
||||||
jwt: {
|
jwt: {
|
||||||
secret: process.env.SECRET,
|
secret: process.env.SECRET,
|
||||||
encode: async ({ secret, token }) => {
|
encode: async ({ secret, token }) => {
|
||||||
return jwt.sign({ ...token, userId: token.id }, secret, {
|
return jwt.sign({...token, userId: token.id}, secret, {
|
||||||
algorithm: "HS256",
|
algorithm: "HS256",
|
||||||
expiresIn: 30 * 24 * 60 * 60, // 30 days
|
expiresIn: 30 * 24 * 60 * 60, // 30 days
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
decode: async ({ secret, token }) => {
|
decode: async ({ secret, token }) => {
|
||||||
return jwt.verify(token, secret, { algorithms: ["HS256"] })
|
return jwt.verify(token, secret, { algorithms: ["HS256"] });
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ You can find the full schema in the table structure section below.
|
|||||||
|
|
||||||
1. Install `next-auth` and `@next-auth/dynamodb-adapter`
|
1. Install `next-auth` and `@next-auth/dynamodb-adapter`
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install next-auth @next-auth/dynamodb-adapter
|
npm install next-auth @next-auth/dynamodb-adapter
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ You can find the Fauna schema and seed information in the docs at [next-auth.js.
|
|||||||
|
|
||||||
1. Install the necessary packages
|
1. Install the necessary packages
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install next-auth @next-auth/fauna-adapter faunadb
|
npm install next-auth @next-auth/fauna-adapter faunadb
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ This is the Firebase Adapter for [`next-auth`](https://next-auth.js.org). This p
|
|||||||
|
|
||||||
1. Install the necessary packages
|
1. Install the necessary packages
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install next-auth @next-auth/firebase-adapter@experimental
|
npm install next-auth @next-auth/firebase-adapter@experimental
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ title: MikroORM
|
|||||||
|
|
||||||
To use this Adapter, you need to install Mikro ORM, the driver that suits your database, and the separate `@next-auth/mikro-orm-adapter` package:
|
To use this Adapter, you need to install Mikro ORM, the driver that suits your database, and the separate `@next-auth/mikro-orm-adapter` package:
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install next-auth @next-auth/mikro-orm-adapter @mikro-orm/core @mikro-orm/[YOUR DRIVER]
|
npm install next-auth @next-auth/mikro-orm-adapter @mikro-orm/core @mikro-orm/[YOUR DRIVER]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ The MongoDB adapter does not handle connections automatically, so you will have
|
|||||||
|
|
||||||
1. Install the necessary packages
|
1. Install the necessary packages
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install next-auth @next-auth/mongodb-adapter mongodb
|
npm install next-auth @next-auth/mongodb-adapter mongodb
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ This is the Neo4j Adapter for [`next-auth`](https://next-auth.js.org). This pack
|
|||||||
|
|
||||||
1. Install the necessary packages
|
1. Install the necessary packages
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install next-auth @next-auth/neo4j-adapter neo4j-driver
|
npm install next-auth @next-auth/neo4j-adapter neo4j-driver
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Depending on your architecture you can use PouchDB's http adapter to reach any d
|
|||||||
|
|
||||||
1. Install `next-auth` and `@next-auth/pouchdb-adapter`
|
1. Install `next-auth` and `@next-auth/pouchdb-adapter`
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install next-auth @next-auth/pouchdb-adapter
|
npm install next-auth @next-auth/pouchdb-adapter
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ title: Prisma
|
|||||||
|
|
||||||
To use this Adapter, you need to install Prisma Client, Prisma CLI, and the separate `@next-auth/prisma-adapter` package:
|
To use this Adapter, you need to install Prisma Client, Prisma CLI, and the separate `@next-auth/prisma-adapter` package:
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install next-auth @prisma/client @next-auth/prisma-adapter
|
npm install next-auth @prisma/client @next-auth/prisma-adapter
|
||||||
npm install prisma --save-dev
|
npm install prisma --save-dev
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ This is the Sequelize Adapter for [`next-auth`](https://next-auth.js.org).
|
|||||||
|
|
||||||
1. Install the necessary packages
|
1. Install the necessary packages
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install next-auth @next-auth/sequelize-adapter sequelize
|
npm install next-auth @next-auth/sequelize-adapter sequelize
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ In the future, we might split up this adapter to support single flavors of SQL f
|
|||||||
|
|
||||||
To use this Adapter, you need to install the following packages:
|
To use this Adapter, you need to install the following packages:
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install next-auth @next-auth/typeorm-legacy-adapter typeorm
|
npm install next-auth @next-auth/typeorm-legacy-adapter typeorm
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ title: Upstash Redis
|
|||||||
|
|
||||||
To use this Adapter, you need to install `@upstash/redis` and `@next-auth/upstash-redis-adapter` package:
|
To use this Adapter, you need to install `@upstash/redis` and `@next-auth/upstash-redis-adapter` package:
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install @upstash/redis @next-auth/upstash-redis-adapter
|
npm install @upstash/redis @next-auth/upstash-redis-adapter
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ The most simple usage is when you want to require authentication for your entire
|
|||||||
export { default } from "next-auth/middleware"
|
export { default } from "next-auth/middleware"
|
||||||
```
|
```
|
||||||
|
|
||||||
That's it! Your application is now secured. 🎉
|
That's it! Your application is not secured. 🎉
|
||||||
|
|
||||||
If you only want to secure certain pages, export a `config` object with a `matcher`:
|
If you only want to secure certain pages, export a `config` object with a `matcher`:
|
||||||
|
|
||||||
|
|||||||
@@ -366,14 +366,11 @@ Changes the color scheme theme of [pages](/configuration/pages) as well as allow
|
|||||||
|
|
||||||
In addition, you can define a logo URL in `theme.logo` which will be rendered above the main card in the default signin/signout/error/verify-request pages, as well as a `theme.brandColor` which will affect the accent color of these pages.
|
In addition, you can define a logo URL in `theme.logo` which will be rendered above the main card in the default signin/signout/error/verify-request pages, as well as a `theme.brandColor` which will affect the accent color of these pages.
|
||||||
|
|
||||||
The sign-in button's background color will match the `brandColor` and defaults to `"#346df1"`. The text color is `#fff` by default, but if your brand color gives a weak contrast, correct it with the `buttonText` color option.
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
theme: {
|
theme: {
|
||||||
colorScheme: "auto", // "auto" | "dark" | "light"
|
colorScheme: "auto", // "auto" | "dark" | "light"
|
||||||
brandColor: "", // Hex color code
|
brandColor: "", // Hex color code
|
||||||
logo: "", // Absolute URL to image
|
logo: "" // Absolute URL to image
|
||||||
buttonText: "" // Hex color code
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -165,12 +165,6 @@ See repository [`README`](https://github.com/nextauthjs/react-query) for more de
|
|||||||
|
|
||||||
NextAuth.js provides a `getSession()` helper which should be called **client side only** to return the current active session.
|
NextAuth.js provides a `getSession()` helper which should be called **client side only** to return the current active session.
|
||||||
|
|
||||||
On the server side, **this is still available to use**, however, we recommend using `unstable_getServerSession` going forward. The idea behind this is to avoid an additional unnecessary `fetch` call on the server side. For more information, please check out [this issue](https://github.com/nextauthjs/next-auth/issues/1535).
|
|
||||||
|
|
||||||
:::note
|
|
||||||
The `unstable_getServerSession` only has the prefix `unstable_` at the moment, because the API may change in the future. There are no known bugs at the moment and it is safe to use. If you discover any issues, please do report them as a [GitHub Issue](https://github.com/nextauthjs/next-auth/issues) and we will patch them as soon as possible.
|
|
||||||
:::
|
|
||||||
|
|
||||||
This helper is helpful in case you want to read the session outside of the context of React.
|
This helper is helpful in case you want to read the session outside of the context of React.
|
||||||
|
|
||||||
When called, `getSession()` will send a request to `/api/auth/session` and returns a promise with a [session object](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/core/types.ts#L407-L425), or `null` if no session exists.
|
When called, `getSession()` will send a request to `/api/auth/session` and returns a promise with a [session object](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/core/types.ts#L407-L425), or `null` if no session exists.
|
||||||
@@ -427,14 +421,13 @@ This only works on pages where you provide the correct `pageProps`, however. Thi
|
|||||||
|
|
||||||
```js title="pages/index.js"
|
```js title="pages/index.js"
|
||||||
import { unstable_getServerSession } from "next-auth/next"
|
import { unstable_getServerSession } from "next-auth/next"
|
||||||
import { authOptions } from './api/auth/[...nextauth]'
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
export async function getServerSideProps({ req, res }) {
|
export async function getServerSideProps(ctx) {
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
session: await unstable_getServerSession(req, res, authOptions)
|
session: await unstable_getServerSession(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ To protect an API Route, you can use the [`unstable_getServerSession()`](/config
|
|||||||
|
|
||||||
```javascript title="pages/api/restricted.js" showLineNumbers
|
```javascript title="pages/api/restricted.js" showLineNumbers
|
||||||
import { unstable_getServerSession } from "next-auth/next"
|
import { unstable_getServerSession } from "next-auth/next"
|
||||||
import { authOptions } from "./api/auth/[...nextauth]"
|
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const session = await unstable_getServerSession(req, res, authOptions)
|
const session = await unstable_getServerSession(req, res, authOptions)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ We encourage users to try it out and report any and all issues they come across.
|
|||||||
|
|
||||||
You can upgrade to the new version by running:
|
You can upgrade to the new version by running:
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install next-auth
|
npm install next-auth
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -124,74 +124,67 @@ providers: [
|
|||||||
The following code shows the complete source for the built-in `sendVerificationRequest()` method:
|
The following code shows the complete source for the built-in `sendVerificationRequest()` method:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { createTransport } from "nodemailer"
|
import nodemailer from "nodemailer"
|
||||||
|
|
||||||
async function sendVerificationRequest(params) {
|
async function sendVerificationRequest({
|
||||||
const { identifier, url, provider, theme } = params
|
identifier: email,
|
||||||
|
url,
|
||||||
|
provider: { server, from },
|
||||||
|
}) {
|
||||||
const { host } = new URL(url)
|
const { host } = new URL(url)
|
||||||
// NOTE: You are not required to use `nodemailer`, use whatever you want.
|
const transport = nodemailer.createTransport(server)
|
||||||
const transport = createTransport(provider.server)
|
await transport.sendMail({
|
||||||
const result = await transport.sendMail({
|
to: email,
|
||||||
to: identifier,
|
from,
|
||||||
from: provider.from,
|
|
||||||
subject: `Sign in to ${host}`,
|
subject: `Sign in to ${host}`,
|
||||||
text: text({ url, host }),
|
text: text({ url, host }),
|
||||||
html: html({ url, host, theme }),
|
html: html({ url, host, email }),
|
||||||
})
|
})
|
||||||
const failed = result.rejected.concat(result.pending).filter(Boolean)
|
|
||||||
if (failed.length) {
|
|
||||||
throw new Error(`Email(s) (${failed.join(", ")}) could not be sent`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Email HTML body
|
||||||
* Email HTML body
|
function html({ url, host, email }: Record<"url" | "host" | "email", string>) {
|
||||||
* Insert invisible space into domains from being turned into a hyperlink by email
|
// Insert invisible space into domains and email address to prevent both the
|
||||||
* clients like Outlook and Apple mail, as this is confusing because it seems
|
// email address and the domain from being turned into a hyperlink by email
|
||||||
* like they are supposed to click on it to sign in.
|
// clients like Outlook and Apple mail, as this is confusing because it seems
|
||||||
*
|
// like they are supposed to click on their email address to sign in.
|
||||||
* @note We don't add the email address to avoid needing to escape it, if you do, remember to sanitize it!
|
const escapedEmail = `${email.replace(/\./g, "​.")}`
|
||||||
*/
|
const escapedHost = `${host.replace(/\./g, "​.")}`
|
||||||
function html(params: { url: string; host: string; theme: Theme }) {
|
|
||||||
const { url, host, theme } = params
|
|
||||||
|
|
||||||
const escapedHost = host.replace(/\./g, "​.")
|
// Some simple styling options
|
||||||
|
const backgroundColor = "#f9f9f9"
|
||||||
const brandColor = theme.brandColor || "#346df1"
|
const textColor = "#444444"
|
||||||
const color = {
|
const mainBackgroundColor = "#ffffff"
|
||||||
background: "#f9f9f9",
|
const buttonBackgroundColor = "#346df1"
|
||||||
text: "#444",
|
const buttonBorderColor = "#346df1"
|
||||||
mainBackground: "#fff",
|
const buttonTextColor = "#ffffff"
|
||||||
buttonBackground: brandColor,
|
|
||||||
buttonBorder: brandColor,
|
|
||||||
buttonText: theme.buttonText || "#fff",
|
|
||||||
}
|
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<body style="background: ${color.background};">
|
<body style="background: ${backgroundColor};">
|
||||||
<table width="100%" border="0" cellspacing="20" cellpadding="0"
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;">
|
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"
|
<td align="center" style="padding: 10px 0px 20px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${textColor};">
|
||||||
style="padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
|
<strong>${escapedHost}</strong>
|
||||||
Sign in to <strong>${escapedHost}</strong>
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table width="100%" border="0" cellspacing="20" cellpadding="0" style="background: ${mainBackgroundColor}; max-width: 600px; margin: auto; border-radius: 10px;">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding: 10px 0px 0px 0px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${textColor};">
|
||||||
|
Sign in as <strong>${escapedEmail}</strong>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="padding: 20px 0;">
|
<td align="center" style="padding: 20px 0;">
|
||||||
<table border="0" cellspacing="0" cellpadding="0">
|
<table border="0" cellspacing="0" cellpadding="0">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="border-radius: 5px;" bgcolor="${color.buttonBackground}"><a href="${url}"
|
<td align="center" style="border-radius: 5px;" bgcolor="${buttonBackgroundColor}"><a href="${url}" target="_blank" style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${buttonTextColor}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${buttonBorderColor}; display: inline-block; font-weight: bold;">Sign in</a></td>
|
||||||
target="_blank"
|
|
||||||
style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;">Sign
|
|
||||||
in</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"
|
<td align="center" style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${textColor};">
|
||||||
style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
|
|
||||||
If you did not request this email you can safely ignore it.
|
If you did not request this email you can safely ignore it.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -200,8 +193,8 @@ function html(params: { url: string; host: string; theme: Theme }) {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Email Text body (fallback for email clients that don't render HTML, e.g. feature phones) */
|
// Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)
|
||||||
function text({ url, host }: { url: string; host: string }) {
|
function text({ url, host }: Record<"url" | "host", string>) {
|
||||||
return `Sign in to ${host}\n${url}\n\n`
|
return `Sign in to ${host}\n${url}\n\n`
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ title: Overview
|
|||||||
|
|
||||||
Authentication Providers in **NextAuth.js** are services that can be used to sign in a user.
|
Authentication Providers in **NextAuth.js** are services that can be used to sign in a user.
|
||||||
|
|
||||||
There are four ways a user can be signed in:
|
There's four ways a user can be signed in:
|
||||||
|
|
||||||
- [Using a built-in OAuth Provider](/configuration/providers/oauth) (e.g Github, Twitter, Google, etc...)
|
- [Using a built-in OAuth Provider](/configuration/providers/oauth) (e.g Github, Twitter, Google, etc...)
|
||||||
- [Using a custom OAuth Provider](/configuration/providers/oauth#using-a-custom-provider)
|
- [Using a custom OAuth Provider](/configuration/providers/oauth#using-a-custom-provider)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ NextAuth.js provides the ability to setup a [custom Credential provider](/config
|
|||||||
|
|
||||||
You will need an additional dependency, `ldapjs`, which you can install by running
|
You will need an additional dependency, `ldapjs`, which you can install by running
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install ldapjs
|
npm install ldapjs
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -58,11 +58,10 @@ More details can be found [here](https://next-auth.js.org/configuration/nextjs#m
|
|||||||
|
|
||||||
You can protect server side rendered pages using the `unstable_getServerSession` method. This is different from the old `getSession()` method, in that it does not do an extra fetch out over the internet to confirm data from itself, increasing performance significantly.
|
You can protect server side rendered pages using the `unstable_getServerSession` method. This is different from the old `getSession()` method, in that it does not do an extra fetch out over the internet to confirm data from itself, increasing performance significantly.
|
||||||
|
|
||||||
You need to add this to every server rendered page you want to protect. Be aware, `unstable_getServerSession` takes slightly different arguments than the method it is replacing, `getSession`.
|
You need to add this to every server rendered page you want to protect.
|
||||||
|
|
||||||
```js title="pages/server-side-example.js"
|
```js title="pages/server-side-example.js"
|
||||||
import { useSession, unstable_getServerSession } from "next-auth/next"
|
import { useSession, unstable_getServerSession } from "next-auth/next"
|
||||||
import { authOptions } from "./api/auth/[...nextauth]"
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const { data: session } = useSession()
|
const { data: session } = useSession()
|
||||||
@@ -83,11 +82,7 @@ export default function Page() {
|
|||||||
export async function getServerSideProps(context) {
|
export async function getServerSideProps(context) {
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
session: await unstable_getServerSession(
|
session: await unstable_getServerSession(context.req, context.res, authOptions),
|
||||||
context.req,
|
|
||||||
context.res,
|
|
||||||
authOptions
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,7 +116,6 @@ You can protect API routes using the `unstable_getServerSession()` method.
|
|||||||
|
|
||||||
```js title="pages/api/get-session-example.js"
|
```js title="pages/api/get-session-example.js"
|
||||||
import { unstable_getServerSession } from "next-auth/next"
|
import { unstable_getServerSession } from "next-auth/next"
|
||||||
import { authOptions } from "./api/auth/[...nextauth]"
|
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const session = await unstable_getServerSession(req, res, authOptions)
|
const session = await unstable_getServerSession(req, res, authOptions)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ To test an implementation of NextAuth.js, we encourage you to use [Cypress](http
|
|||||||
|
|
||||||
To get started, install the dependencies:
|
To get started, install the dependencies:
|
||||||
|
|
||||||
```bash npm2yarn2pnpm
|
```bash npm2yarn
|
||||||
npm install --save-dev cypress cypress-social-logins @testing-library/cypress
|
npm install --save-dev cypress cypress-social-logins @testing-library/cypress
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -155,9 +155,9 @@ module.exports = {
|
|||||||
showLastUpdateAuthor: true,
|
showLastUpdateAuthor: true,
|
||||||
showLastUpdateTime: true,
|
showLastUpdateTime: true,
|
||||||
remarkPlugins: [
|
remarkPlugins: [
|
||||||
require("@sapphire/docusaurus-plugin-npm2yarn2pnpm").npm2yarn2pnpm,
|
|
||||||
require("remark-github"),
|
require("remark-github"),
|
||||||
require("mdx-mermaid"),
|
require("mdx-mermaid"),
|
||||||
|
[require("@docusaurus/remark-plugin-npm2yarn"), { sync: true }],
|
||||||
],
|
],
|
||||||
versions: {
|
versions: {
|
||||||
current: {
|
current: {
|
||||||
|
|||||||
@@ -21,9 +21,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "^2.0.0-beta.21",
|
"@docusaurus/core": "^2.0.0-beta.21",
|
||||||
"@docusaurus/preset-classic": "^2.0.0-beta.21",
|
"@docusaurus/preset-classic": "^2.0.0-beta.21",
|
||||||
|
"@docusaurus/remark-plugin-npm2yarn": "^2.0.0-beta.21",
|
||||||
"@docusaurus/theme-common": "2.0.0-beta.21",
|
"@docusaurus/theme-common": "2.0.0-beta.21",
|
||||||
"@mdx-js/react": "1.6.22",
|
"@mdx-js/react": "1.6.22",
|
||||||
"@sapphire/docusaurus-plugin-npm2yarn2pnpm": "^1.1.0",
|
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"mdx-mermaid": "^1.2.2",
|
"mdx-mermaid": "^1.2.2",
|
||||||
"mermaid": "^9.0.1",
|
"mermaid": "^9.0.1",
|
||||||
|
|||||||
@@ -166,10 +166,6 @@ export default function App({
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
If you think you have found a vulnerability (or not sure) in NextAuth.js or any of the related packages (i.e. Adapters), we ask you to have a read of our [Security Policy](https://github.com/nextauthjs/next-auth/blob/main/SECURITY.md) to reach out responsibly. Please do not open Pull Requests/Issues/Discussions before consulting with us.
|
|
||||||
|
|
||||||
## Acknowledgments
|
## Acknowledgments
|
||||||
|
|
||||||
[NextAuth.js is made possible thanks to all of its contributors.](https://next-auth.js.org/contributors)
|
[NextAuth.js is made possible thanks to all of its contributors.](https://next-auth.js.org/contributors)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "next-auth",
|
"name": "next-auth",
|
||||||
"version": "4.9.0",
|
"version": "4.7.0",
|
||||||
"description": "Authentication for Next.js",
|
"description": "Authentication for Next.js",
|
||||||
"homepage": "https://next-auth.js.org",
|
"homepage": "https://next-auth.js.org",
|
||||||
"repository": "https://github.com/nextauthjs/next-auth.git",
|
"repository": "https://github.com/nextauthjs/next-auth.git",
|
||||||
@@ -138,4 +138,4 @@
|
|||||||
"**/tests",
|
"**/tests",
|
||||||
"**/__tests__"
|
"**/__tests__"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,6 @@ import { SessionStore } from "./lib/cookie"
|
|||||||
import type { NextAuthAction, NextAuthOptions } from "./types"
|
import type { NextAuthAction, NextAuthOptions } from "./types"
|
||||||
import type { Cookie } from "./lib/cookie"
|
import type { Cookie } from "./lib/cookie"
|
||||||
import type { ErrorType } from "./pages/error"
|
import type { ErrorType } from "./pages/error"
|
||||||
import { parse as parseCookie } from "cookie"
|
|
||||||
|
|
||||||
export interface RequestInternal {
|
export interface RequestInternal {
|
||||||
/** @default "http://localhost:3000" */
|
/** @default "http://localhost:3000" */
|
||||||
@@ -69,7 +68,7 @@ async function toInternalRequest(
|
|||||||
method: req.method,
|
method: req.method,
|
||||||
headers,
|
headers,
|
||||||
body: await getBody(req),
|
body: await getBody(req),
|
||||||
cookies: parseCookie(req.headers.get("cookie") ?? ""),
|
cookies: {},
|
||||||
providerId: nextauth[1],
|
providerId: nextauth[1],
|
||||||
error: url.searchParams.get("error") ?? nextauth[1],
|
error: url.searchParams.get("error") ?? nextauth[1],
|
||||||
host: detectHost(headers["x-forwarded-host"] ?? headers.host),
|
host: detectHost(headers["x-forwarded-host"] ?? headers.host),
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ export async function init({
|
|||||||
colorScheme: "auto",
|
colorScheme: "auto",
|
||||||
logo: "",
|
logo: "",
|
||||||
brandColor: "",
|
brandColor: "",
|
||||||
buttonText: "",
|
|
||||||
},
|
},
|
||||||
// Custom options override defaults
|
// Custom options override defaults
|
||||||
...userOptions,
|
...userOptions,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export default async function email(
|
|||||||
identifier: string,
|
identifier: string,
|
||||||
options: InternalOptions<"email">
|
options: InternalOptions<"email">
|
||||||
) {
|
) {
|
||||||
const { url, adapter, provider, logger, callbackUrl, theme } = options
|
const { url, adapter, provider, logger, callbackUrl } = options
|
||||||
|
|
||||||
// Generate token
|
// Generate token
|
||||||
const token =
|
const token =
|
||||||
@@ -42,7 +42,6 @@ export default async function email(
|
|||||||
expires,
|
expires,
|
||||||
url: _url,
|
url: _url,
|
||||||
provider,
|
provider,
|
||||||
theme,
|
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("SEND_VERIFICATION_EMAIL_ERROR", {
|
logger.error("SEND_VERIFICATION_EMAIL_ERROR", {
|
||||||
|
|||||||
@@ -30,16 +30,12 @@ export default async function signin(params: {
|
|||||||
return { redirect: `${url}/error?error=OAuthSignin` }
|
return { redirect: `${url}/error?error=OAuthSignin` }
|
||||||
}
|
}
|
||||||
} else if (provider.type === "email") {
|
} else if (provider.type === "email") {
|
||||||
/**
|
// Note: Technically the part of the email address local mailbox element
|
||||||
* @note Technically the part of the email address local mailbox element
|
// (everything before the @ symbol) should be treated as 'case sensitive'
|
||||||
* (everything before the @ symbol) should be treated as 'case sensitive'
|
// according to RFC 2821, but in practice this causes more problems than
|
||||||
* according to RFC 2821, but in practice this causes more problems than
|
// it solves. We treat email addresses as all lower case. If anyone
|
||||||
* it solves. We treat email addresses as all lower case. If anyone
|
// complains about this we can make strict RFC 2821 compliance an option.
|
||||||
* complains about this we can make strict RFC 2821 compliance an option.
|
const email = body?.email?.toLowerCase() ?? null
|
||||||
*/
|
|
||||||
const email = body?.email?.toLowerCase()
|
|
||||||
|
|
||||||
if (!email) return { redirect: `${url}/error?error=EmailSignin` }
|
|
||||||
|
|
||||||
// Verified in `assertConfig`
|
// Verified in `assertConfig`
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
|||||||
@@ -217,7 +217,6 @@ export interface Theme {
|
|||||||
colorScheme: "auto" | "dark" | "light"
|
colorScheme: "auto" | "dark" | "light"
|
||||||
logo?: string
|
logo?: string
|
||||||
brandColor?: string
|
brandColor?: string
|
||||||
buttonText?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -83,28 +83,20 @@ function NextAuth(
|
|||||||
|
|
||||||
export default NextAuth
|
export default NextAuth
|
||||||
|
|
||||||
let experimentalWarningShown = false
|
|
||||||
export async function unstable_getServerSession(
|
export async function unstable_getServerSession(
|
||||||
...args:
|
...args:
|
||||||
| [
|
| [GetServerSidePropsContext['req'], GetServerSidePropsContext['res'], NextAuthOptions]
|
||||||
GetServerSidePropsContext["req"],
|
|
||||||
GetServerSidePropsContext["res"],
|
|
||||||
NextAuthOptions
|
|
||||||
]
|
|
||||||
| [NextApiRequest, NextApiResponse, NextAuthOptions]
|
| [NextApiRequest, NextApiResponse, NextAuthOptions]
|
||||||
): Promise<Session | null> {
|
): Promise<Session | null> {
|
||||||
if (!experimentalWarningShown && process.env.NODE_ENV !== "production") {
|
console.warn(
|
||||||
console.warn(
|
"[next-auth][warn][EXPERIMENTAL_API]",
|
||||||
"[next-auth][warn][EXPERIMENTAL_API]",
|
"\n`unstable_getServerSession` is experimental and may be removed or changed in the future, as the name suggested.",
|
||||||
"\n`unstable_getServerSession` is experimental and may be removed or changed in the future, as the name suggested.",
|
`\nhttps://next-auth.js.org/configuration/nextjs#unstable_getServerSession}`,
|
||||||
`\nhttps://next-auth.js.org/configuration/nextjs#unstable_getServerSession}`,
|
`\nhttps://next-auth.js.org/warnings#EXPERIMENTAL_API`
|
||||||
`\nhttps://next-auth.js.org/warnings#EXPERIMENTAL_API`
|
|
||||||
)
|
)
|
||||||
experimentalWarningShown = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const [req, res, options] = args
|
|
||||||
|
|
||||||
|
const [req, res, options] = args;
|
||||||
|
|
||||||
options.secret = options.secret ?? process.env.NEXTAUTH_SECRET
|
options.secret = options.secret ?? process.env.NEXTAUTH_SECRET
|
||||||
|
|
||||||
const session = await NextAuthHandler<Session | {}>({
|
const session = await NextAuthHandler<Session | {}>({
|
||||||
|
|||||||
@@ -3,16 +3,6 @@ import { createTransport } from "nodemailer"
|
|||||||
import type { CommonProviderOptions } from "."
|
import type { CommonProviderOptions } from "."
|
||||||
import type { Options as SMTPConnectionOptions } from "nodemailer/lib/smtp-connection"
|
import type { Options as SMTPConnectionOptions } from "nodemailer/lib/smtp-connection"
|
||||||
import type { Awaitable } from ".."
|
import type { Awaitable } from ".."
|
||||||
import type { Theme } from "../core/types"
|
|
||||||
|
|
||||||
export interface SendVerificationRequestParams {
|
|
||||||
identifier: string
|
|
||||||
url: string
|
|
||||||
expires: Date
|
|
||||||
provider: EmailConfig
|
|
||||||
token: string
|
|
||||||
theme: Theme
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EmailConfig extends CommonProviderOptions {
|
export interface EmailConfig extends CommonProviderOptions {
|
||||||
type: "email"
|
type: "email"
|
||||||
@@ -26,10 +16,13 @@ export interface EmailConfig extends CommonProviderOptions {
|
|||||||
* @default 86400
|
* @default 86400
|
||||||
*/
|
*/
|
||||||
maxAge?: number
|
maxAge?: number
|
||||||
/** [Documentation](https://next-auth.js.org/providers/email#customizing-emails) */
|
sendVerificationRequest: (params: {
|
||||||
sendVerificationRequest: (
|
identifier: string
|
||||||
params: SendVerificationRequestParams
|
url: string
|
||||||
) => Awaitable<void>
|
expires: Date
|
||||||
|
provider: EmailConfig
|
||||||
|
token: string
|
||||||
|
}) => Awaitable<void>
|
||||||
/**
|
/**
|
||||||
* By default, we are generating a random verification token.
|
* By default, we are generating a random verification token.
|
||||||
* You can make it predictable or modify it as you like with this method.
|
* You can make it predictable or modify it as you like with this method.
|
||||||
@@ -63,81 +56,78 @@ export default function Email(options: EmailUserConfig): EmailConfig {
|
|||||||
type: "email",
|
type: "email",
|
||||||
name: "Email",
|
name: "Email",
|
||||||
// Server can be an SMTP connection string or a nodemailer config object
|
// Server can be an SMTP connection string or a nodemailer config object
|
||||||
server: { host: "localhost", port: 25, auth: { user: "", pass: "" } },
|
server: {
|
||||||
|
host: "localhost",
|
||||||
|
port: 25,
|
||||||
|
auth: {
|
||||||
|
user: "",
|
||||||
|
pass: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
from: "NextAuth <no-reply@example.com>",
|
from: "NextAuth <no-reply@example.com>",
|
||||||
maxAge: 24 * 60 * 60,
|
maxAge: 24 * 60 * 60,
|
||||||
async sendVerificationRequest(params) {
|
async sendVerificationRequest({
|
||||||
const { identifier, url, provider, theme } = params
|
identifier: email,
|
||||||
|
url,
|
||||||
|
provider: { server, from },
|
||||||
|
}) {
|
||||||
const { host } = new URL(url)
|
const { host } = new URL(url)
|
||||||
const transport = createTransport(provider.server)
|
const transport = createTransport(server)
|
||||||
const result = await transport.sendMail({
|
await transport.sendMail({
|
||||||
to: identifier,
|
to: email,
|
||||||
from: provider.from,
|
from,
|
||||||
subject: `Sign in to ${host}`,
|
subject: `Sign in to ${host}`,
|
||||||
text: text({ url, host }),
|
text: text({ url, host }),
|
||||||
html: html({ url, host, theme }),
|
html: html({ url, host, email }),
|
||||||
})
|
})
|
||||||
const failed = result.rejected.concat(result.pending).filter(Boolean)
|
|
||||||
if (failed.length) {
|
|
||||||
throw new Error(`Email(s) (${failed.join(", ")}) could not be sent`)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
options,
|
options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Email HTML body
|
||||||
* Email HTML body
|
function html({ url, host, email }: Record<"url" | "host" | "email", string>) {
|
||||||
* Insert invisible space into domains from being turned into a hyperlink by email
|
// Insert invisible space into domains and email address to prevent both the
|
||||||
* clients like Outlook and Apple mail, as this is confusing because it seems
|
// email address and the domain from being turned into a hyperlink by email
|
||||||
* like they are supposed to click on it to sign in.
|
// clients like Outlook and Apple mail, as this is confusing because it seems
|
||||||
*
|
// like they are supposed to click on their email address to sign in.
|
||||||
* @note We don't add the email address to avoid needing to escape it, if you do, remember to sanitize it!
|
const escapedEmail = `${email.replace(/\./g, "​.")}`
|
||||||
*/
|
const escapedHost = `${host.replace(/\./g, "​.")}`
|
||||||
function html(params: { url: string; host: string; theme: Theme }) {
|
|
||||||
const { url, host, theme } = params
|
|
||||||
|
|
||||||
const escapedHost = host.replace(/\./g, "​.")
|
// Some simple styling options
|
||||||
|
const backgroundColor = "#f9f9f9"
|
||||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
const textColor = "#444444"
|
||||||
const brandColor = theme.brandColor || "#346df1"
|
const mainBackgroundColor = "#ffffff"
|
||||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
const buttonBackgroundColor = "#346df1"
|
||||||
const buttonText = theme.buttonText || "#fff"
|
const buttonBorderColor = "#346df1"
|
||||||
|
const buttonTextColor = "#ffffff"
|
||||||
const color = {
|
|
||||||
background: "#f9f9f9",
|
|
||||||
text: "#444",
|
|
||||||
mainBackground: "#fff",
|
|
||||||
buttonBackground: brandColor,
|
|
||||||
buttonBorder: brandColor,
|
|
||||||
buttonText,
|
|
||||||
}
|
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<body style="background: ${color.background};">
|
<body style="background: ${backgroundColor};">
|
||||||
<table width="100%" border="0" cellspacing="20" cellpadding="0"
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;">
|
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"
|
<td align="center" style="padding: 10px 0px 20px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${textColor};">
|
||||||
style="padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
|
<strong>${escapedHost}</strong>
|
||||||
Sign in to <strong>${escapedHost}</strong>
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table width="100%" border="0" cellspacing="20" cellpadding="0" style="background: ${mainBackgroundColor}; max-width: 600px; margin: auto; border-radius: 10px;">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding: 10px 0px 0px 0px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${textColor};">
|
||||||
|
Sign in as <strong>${escapedEmail}</strong>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="padding: 20px 0;">
|
<td align="center" style="padding: 20px 0;">
|
||||||
<table border="0" cellspacing="0" cellpadding="0">
|
<table border="0" cellspacing="0" cellpadding="0">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="border-radius: 5px;" bgcolor="${color.buttonBackground}"><a href="${url}"
|
<td align="center" style="border-radius: 5px;" bgcolor="${buttonBackgroundColor}"><a href="${url}" target="_blank" style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${buttonTextColor}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${buttonBorderColor}; display: inline-block; font-weight: bold;">Sign in</a></td>
|
||||||
target="_blank"
|
|
||||||
style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;">Sign
|
|
||||||
in</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"
|
<td align="center" style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${textColor};">
|
||||||
style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
|
|
||||||
If you did not request this email you can safely ignore it.
|
If you did not request this email you can safely ignore it.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -146,7 +136,7 @@ function html(params: { url: string; host: string; theme: Theme }) {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Email Text body (fallback for email clients that don't render HTML, e.g. feature phones) */
|
// Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)
|
||||||
function text({ url, host }: { url: string; host: string }) {
|
function text({ url, host }: Record<"url" | "host", string>) {
|
||||||
return `Sign in to ${host}\n${url}\n\n`
|
return `Sign in to ${host}\n${url}\n\n`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,19 +50,4 @@ describe("Treat secret correctly", () => {
|
|||||||
expect(logger.error).toBeCalledTimes(1)
|
expect(logger.error).toBeCalledTimes(1)
|
||||||
expect(logger.error).toBeCalledWith("NO_SECRET", expect.any(MissingSecret))
|
expect(logger.error).toBeCalledWith("NO_SECRET", expect.any(MissingSecret))
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Only logs warning once and in development", async () => {
|
|
||||||
// Expect console.warn to NOT be called due to NODE_ENV=production
|
|
||||||
await unstable_getServerSession(req, res, { providers: [], logger })
|
|
||||||
expect(console.warn).toBeCalledTimes(0)
|
|
||||||
|
|
||||||
// Expect console.warn to be called ONCE due to NODE_ENV=development
|
|
||||||
process.env.NODE_ENV = "development"
|
|
||||||
await unstable_getServerSession(req, res, { providers: [], logger })
|
|
||||||
expect(console.warn).toBeCalledTimes(1)
|
|
||||||
|
|
||||||
// Expect console.warn to be still only be called ONCE
|
|
||||||
await unstable_getServerSession(req, res, { providers: [], logger })
|
|
||||||
expect(console.warn).toBeCalledTimes(1)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { createHash } from "crypto"
|
|
||||||
import type { LoggerInstance, NextAuthOptions } from "../src"
|
import type { LoggerInstance, NextAuthOptions } from "../src"
|
||||||
import { NextAuthHandler } from "../src/core"
|
import { NextAuthHandler } from "../src/core"
|
||||||
|
|
||||||
@@ -8,16 +7,17 @@ export const mockLogger: () => LoggerInstance = () => ({
|
|||||||
debug: jest.fn(() => {}),
|
debug: jest.fn(() => {}),
|
||||||
})
|
})
|
||||||
|
|
||||||
interface HandlerOptions {
|
|
||||||
prod?: boolean
|
|
||||||
path?: string
|
|
||||||
params?: URLSearchParams | Record<string, string>
|
|
||||||
requestInit?: RequestInit
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function handler(
|
export async function handler(
|
||||||
options: NextAuthOptions,
|
options: NextAuthOptions,
|
||||||
{ prod, path, params, requestInit }: HandlerOptions
|
{
|
||||||
|
prod,
|
||||||
|
path,
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
prod?: boolean
|
||||||
|
path?: string
|
||||||
|
params?: URLSearchParams | Record<string, string>
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (prod) process.env.NODE_ENV = "production"
|
if (prod) process.env.NODE_ENV = "production"
|
||||||
@@ -27,7 +27,11 @@ export async function handler(
|
|||||||
params ?? {}
|
params ?? {}
|
||||||
)}`
|
)}`
|
||||||
)
|
)
|
||||||
const req = new Request(url, { headers: { host: "" }, ...requestInit })
|
const req = new Request(url, {
|
||||||
|
headers: {
|
||||||
|
host: "",
|
||||||
|
},
|
||||||
|
})
|
||||||
const logger = mockLogger()
|
const logger = mockLogger()
|
||||||
const response = await NextAuthHandler({
|
const response = await NextAuthHandler({
|
||||||
req,
|
req,
|
||||||
@@ -45,14 +49,3 @@ export async function handler(
|
|||||||
log: logger,
|
log: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCSRF() {
|
|
||||||
const secret = "secret"
|
|
||||||
const value = "csrf"
|
|
||||||
const token = createHash("sha256").update(`${value}${secret}`).digest("hex")
|
|
||||||
|
|
||||||
return {
|
|
||||||
secret,
|
|
||||||
csrf: { value, token, cookie: `next-auth.csrf-token=${value}|${token}` },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
21
pnpm-lock.yaml
generated
21
pnpm-lock.yaml
generated
@@ -88,9 +88,9 @@ importers:
|
|||||||
'@docusaurus/core': ^2.0.0-beta.21
|
'@docusaurus/core': ^2.0.0-beta.21
|
||||||
'@docusaurus/module-type-aliases': 2.0.0-beta.20
|
'@docusaurus/module-type-aliases': 2.0.0-beta.20
|
||||||
'@docusaurus/preset-classic': ^2.0.0-beta.21
|
'@docusaurus/preset-classic': ^2.0.0-beta.21
|
||||||
|
'@docusaurus/remark-plugin-npm2yarn': ^2.0.0-beta.21
|
||||||
'@docusaurus/theme-common': 2.0.0-beta.21
|
'@docusaurus/theme-common': 2.0.0-beta.21
|
||||||
'@mdx-js/react': 1.6.22
|
'@mdx-js/react': 1.6.22
|
||||||
'@sapphire/docusaurus-plugin-npm2yarn2pnpm': ^1.1.0
|
|
||||||
classnames: ^2.3.1
|
classnames: ^2.3.1
|
||||||
mdx-mermaid: ^1.2.2
|
mdx-mermaid: ^1.2.2
|
||||||
mermaid: ^9.0.1
|
mermaid: ^9.0.1
|
||||||
@@ -104,9 +104,9 @@ importers:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@docusaurus/core': 2.0.0-beta.21_biqbaboplfbrettd7655fr4n2y
|
'@docusaurus/core': 2.0.0-beta.21_biqbaboplfbrettd7655fr4n2y
|
||||||
'@docusaurus/preset-classic': 2.0.0-beta.21_biqbaboplfbrettd7655fr4n2y
|
'@docusaurus/preset-classic': 2.0.0-beta.21_biqbaboplfbrettd7655fr4n2y
|
||||||
|
'@docusaurus/remark-plugin-npm2yarn': 2.0.0-beta.21
|
||||||
'@docusaurus/theme-common': 2.0.0-beta.21_biqbaboplfbrettd7655fr4n2y
|
'@docusaurus/theme-common': 2.0.0-beta.21_biqbaboplfbrettd7655fr4n2y
|
||||||
'@mdx-js/react': 1.6.22_react@18.2.0
|
'@mdx-js/react': 1.6.22_react@18.2.0
|
||||||
'@sapphire/docusaurus-plugin-npm2yarn2pnpm': 1.1.0
|
|
||||||
classnames: 2.3.1
|
classnames: 2.3.1
|
||||||
mdx-mermaid: 1.2.3_mermaid@9.1.2+react@18.2.0
|
mdx-mermaid: 1.2.3_mermaid@9.1.2+react@18.2.0
|
||||||
mermaid: 9.1.2
|
mermaid: 9.1.2
|
||||||
@@ -4155,6 +4155,15 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@docusaurus/remark-plugin-npm2yarn/2.0.0-beta.21:
|
||||||
|
resolution: {integrity: sha512-CqvmoFEj05NzaQBKxnsfI90aM8KHJZWyCzED/Qg5odUD9VtR9zNQJ1Nu/X1ctqCN7FBIxBYk2tz1Xb1+zCP8gg==}
|
||||||
|
engines: {node: '>=16.14'}
|
||||||
|
dependencies:
|
||||||
|
npm-to-yarn: 1.0.1
|
||||||
|
tslib: 2.4.0
|
||||||
|
unist-util-visit: 2.0.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@docusaurus/theme-classic/2.0.0-beta.21_biqbaboplfbrettd7655fr4n2y:
|
/@docusaurus/theme-classic/2.0.0-beta.21_biqbaboplfbrettd7655fr4n2y:
|
||||||
resolution: {integrity: sha512-Ge0WNdTefD0VDQfaIMRRWa8tWMG9+8/OlBRd5MK88/TZfqdBq7b/gnCSaalQlvZwwkj6notkKhHx72+MKwWUJA==}
|
resolution: {integrity: sha512-Ge0WNdTefD0VDQfaIMRRWa8tWMG9+8/OlBRd5MK88/TZfqdBq7b/gnCSaalQlvZwwkj6notkKhHx72+MKwWUJA==}
|
||||||
engines: {node: '>=16.14'}
|
engines: {node: '>=16.14'}
|
||||||
@@ -5798,14 +5807,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
|
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@sapphire/docusaurus-plugin-npm2yarn2pnpm/1.1.0:
|
|
||||||
resolution: {integrity: sha512-AQGsbaxxJEFGWbLfzoXndT5waPNSQ708qvOz31aKwNilsftV22aBW1NMizxjLCQBooRr4dEWWBLxTGl9y4vKLg==}
|
|
||||||
engines: {node: '>=v16.6.0', npm: '>=7.0.0'}
|
|
||||||
dependencies:
|
|
||||||
npm-to-yarn: 1.0.1
|
|
||||||
unist-util-visit: 2.0.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@shelf/jest-dynamodb/2.2.4_qsruu6yolbxs4rh6ixjhkibvwu:
|
/@shelf/jest-dynamodb/2.2.4_qsruu6yolbxs4rh6ixjhkibvwu:
|
||||||
resolution: {integrity: sha512-OAnkP5sPcIoqL+q/tpp54psuK1gssm+nZLOHRy0S1eyAZGmuqiYAUzyAvmH5AhyqvDPSEHFkIkfbqlp1+KpHgw==}
|
resolution: {integrity: sha512-OAnkP5sPcIoqL+q/tpp54psuK1gssm+nZLOHRy0S1eyAZGmuqiYAUzyAvmH5AhyqvDPSEHFkIkfbqlp1+KpHgw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|||||||
Reference in New Issue
Block a user