mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
8 Commits
next-auth@
...
next-auth@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
163d8c66e2 | ||
|
|
5319dca583 | ||
|
|
cd6ccfde89 | ||
|
|
89d91ea282 | ||
|
|
ca3165bd5a | ||
|
|
aa527b37bf | ||
|
|
f3233641d0 | ||
|
|
4bee970775 |
@@ -93,7 +93,7 @@ The most simple usage is when you want to require authentication for your entire
|
||||
export { default } from "next-auth/middleware"
|
||||
```
|
||||
|
||||
That's it! Your application is not secured. 🎉
|
||||
That's it! Your application is now secured. 🎉
|
||||
|
||||
If you only want to secure certain pages, export a `config` object with a `matcher`:
|
||||
|
||||
|
||||
@@ -165,6 +165,12 @@ 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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -58,7 +58,7 @@ 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 need to add this to every server rendered page you want to protect.
|
||||
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`.
|
||||
|
||||
```js title="pages/server-side-example.js"
|
||||
import { useSession, unstable_getServerSession } from "next-auth/next"
|
||||
@@ -82,7 +82,11 @@ export default function Page() {
|
||||
export async function getServerSideProps(context) {
|
||||
return {
|
||||
props: {
|
||||
session: await unstable_getServerSession(context.req, context.res, authOptions),
|
||||
session: await unstable_getServerSession(
|
||||
context.req,
|
||||
context.res,
|
||||
authOptions
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +166,10 @@ 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
|
||||
|
||||
[NextAuth.js is made possible thanks to all of its contributors.](https://next-auth.js.org/contributors)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "next-auth",
|
||||
"version": "4.6.1",
|
||||
"version": "4.8.0",
|
||||
"description": "Authentication for Next.js",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth.git",
|
||||
|
||||
@@ -9,6 +9,7 @@ import { SessionStore } from "./lib/cookie"
|
||||
import type { NextAuthAction, NextAuthOptions } from "./types"
|
||||
import type { Cookie } from "./lib/cookie"
|
||||
import type { ErrorType } from "./pages/error"
|
||||
import { parse as parseCookie } from "cookie"
|
||||
|
||||
export interface RequestInternal {
|
||||
/** @default "http://localhost:3000" */
|
||||
@@ -68,7 +69,7 @@ async function toInternalRequest(
|
||||
method: req.method,
|
||||
headers,
|
||||
body: await getBody(req),
|
||||
cookies: {},
|
||||
cookies: parseCookie(req.headers.get("cookie") ?? ""),
|
||||
providerId: nextauth[1],
|
||||
error: url.searchParams.get("error") ?? nextauth[1],
|
||||
host: detectHost(headers["x-forwarded-host"] ?? headers.host),
|
||||
|
||||
@@ -30,12 +30,25 @@ export default async function signin(params: {
|
||||
return { redirect: `${url}/error?error=OAuthSignin` }
|
||||
}
|
||||
} else if (provider.type === "email") {
|
||||
// Note: Technically the part of the email address local mailbox element
|
||||
// (everything before the @ symbol) should be treated as 'case sensitive'
|
||||
// according to RFC 2821, but in practice this causes more problems than
|
||||
// it solves. We treat email addresses as all lower case. If anyone
|
||||
// complains about this we can make strict RFC 2821 compliance an option.
|
||||
const email = body?.email?.toLowerCase() ?? null
|
||||
/**
|
||||
* @note Technically the part of the email address local mailbox element
|
||||
* (everything before the @ symbol) should be treated as 'case sensitive'
|
||||
* according to RFC 2821, but in practice this causes more problems than
|
||||
* it solves. We treat email addresses as all lower case. If anyone
|
||||
* complains about this we can make strict RFC 2821 compliance an option.
|
||||
*/
|
||||
let email = body?.email?.toLowerCase()
|
||||
|
||||
if (!email) return { redirect: `${url}/error?error=EmailSignin` }
|
||||
|
||||
email = email
|
||||
.split(",")[0]
|
||||
.trim()
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'")
|
||||
|
||||
// Verified in `assertConfig`
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
|
||||
59
packages/next-auth/tests/email.test.ts
Normal file
59
packages/next-auth/tests/email.test.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { createCSRF, handler } from "./lib"
|
||||
import EmailProvider from "../src/providers/email"
|
||||
|
||||
const originalEmail = "balazs@email.com"
|
||||
|
||||
test.each([
|
||||
[originalEmail, `,<a href="example.com">Click here!</a>`],
|
||||
[originalEmail, ""],
|
||||
])("Sanitize email", async (emailOriginal, emailCompromised) => {
|
||||
const sendEmail = jest.fn()
|
||||
|
||||
const { secret, csrf } = createCSRF()
|
||||
|
||||
const email = {
|
||||
original: emailOriginal,
|
||||
compromised: `${emailOriginal}${emailCompromised}`,
|
||||
}
|
||||
|
||||
const { res } = await handler(
|
||||
{
|
||||
providers: [EmailProvider({ sendVerificationRequest: sendEmail })],
|
||||
adapter: {
|
||||
getUserByEmail: (email) => ({ id: "1", email, emailVerified: null }),
|
||||
createVerificationToken: (token) => token,
|
||||
} as any,
|
||||
secret,
|
||||
},
|
||||
{
|
||||
prod: true,
|
||||
path: "signin/email",
|
||||
requestInit: {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
email: email.compromised,
|
||||
csrfToken: csrf.value,
|
||||
}),
|
||||
headers: { "Content-Type": "application/json", Cookie: csrf.cookie },
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if (!emailCompromised) {
|
||||
expect(res.redirect).toBe(
|
||||
"http://localhost:3000/api/auth/verify-request?provider=email&type=email"
|
||||
)
|
||||
expect(sendEmail).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
identifier: email.original,
|
||||
token: expect.any(String),
|
||||
})
|
||||
)
|
||||
} else {
|
||||
expect(res.redirect).not.toContain("error=EmailSignin")
|
||||
|
||||
const emailTo = sendEmail.mock.calls[0][0].identifier
|
||||
expect(emailTo).not.toBe(email.compromised)
|
||||
expect(emailTo).toBe(email.original)
|
||||
}
|
||||
})
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createHash } from "crypto"
|
||||
import type { LoggerInstance, NextAuthOptions } from "../src"
|
||||
import { NextAuthHandler } from "../src/core"
|
||||
|
||||
@@ -7,17 +8,16 @@ export const mockLogger: () => LoggerInstance = () => ({
|
||||
debug: jest.fn(() => {}),
|
||||
})
|
||||
|
||||
interface HandlerOptions {
|
||||
prod?: boolean
|
||||
path?: string
|
||||
params?: URLSearchParams | Record<string, string>
|
||||
requestInit?: RequestInit
|
||||
}
|
||||
|
||||
export async function handler(
|
||||
options: NextAuthOptions,
|
||||
{
|
||||
prod,
|
||||
path,
|
||||
params,
|
||||
}: {
|
||||
prod?: boolean
|
||||
path?: string
|
||||
params?: URLSearchParams | Record<string, string>
|
||||
}
|
||||
{ prod, path, params, requestInit }: HandlerOptions
|
||||
) {
|
||||
// @ts-ignore
|
||||
if (prod) process.env.NODE_ENV = "production"
|
||||
@@ -27,11 +27,7 @@ export async function handler(
|
||||
params ?? {}
|
||||
)}`
|
||||
)
|
||||
const req = new Request(url, {
|
||||
headers: {
|
||||
host: "",
|
||||
},
|
||||
})
|
||||
const req = new Request(url, { headers: { host: "" }, ...requestInit })
|
||||
const logger = mockLogger()
|
||||
const response = await NextAuthHandler({
|
||||
req,
|
||||
@@ -49,3 +45,14 @@ export async function handler(
|
||||
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}` },
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user