-
Notifications
You must be signed in to change notification settings - Fork 4k
feat(middleware): introduce withAuth Next.js method
#3657
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
Changes from all commits
b496866
13ce4de
d1337a5
f9be36b
061e7ba
f4b7733
ea16ed8
96d07ab
6c27a82
a7ca9e3
9c44d13
e32e782
e767e0e
c3d58cb
2976116
4fb5cf7
eb36ed5
cb0ba9e
12002aa
1a3aac5
5db7cc7
f28ad9f
0f23b3d
f51be7c
1b99fba
817b2cf
4a95e1c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| export { default } from "next-auth/middleware" | ||
|
|
||
| // Other ways to use this middleware | ||
|
|
||
| // import withAuth from "next-auth/middleware" | ||
| // import { withAuth } from "next-auth/middleware" | ||
|
|
||
| // export function middleware(req, ev) { | ||
| // return withAuth(req) | ||
| // } | ||
|
|
||
| // export function middleware(req, ev) { | ||
| // return withAuth(req, ev) | ||
| // } | ||
|
|
||
| // export function middleware(req, ev) { | ||
| // return withAuth(req, { | ||
| // callbacks: { | ||
| // authorized: ({ token }) => !!token, | ||
| // }, | ||
| // }) | ||
| // } | ||
|
|
||
| // export default withAuth(function middleware(req, ev) { | ||
| // console.log(req.nextauth.token) | ||
| // }) | ||
|
|
||
| // export default withAuth( | ||
| // function middleware(req, ev) { | ||
| // console.log(req, ev) | ||
| // return undefined // NOTE: `NextMiddleware` should allow returning `void` | ||
| // }, | ||
| // { | ||
| // callbacks: { | ||
| // authorized: ({ token }) => token.name === "Balázs Orbán", | ||
| // } | ||
| // } | ||
| // ) | ||
|
|
||
| // export default withAuth({ | ||
| // callbacks: { | ||
| // authorized: ({ token }) => !!token, | ||
| // }, | ||
| // }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import Layout from "components/layout" | ||
|
|
||
| export default function Page() { | ||
| return ( | ||
| <Layout> | ||
| <h1>Page protected by Middleware</h1> | ||
| </Layout> | ||
| ) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export { default } from "./next/middleware" | ||
| export * from "./next/middleware" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| import type { NextMiddleware, NextFetchEvent } from "next/server" | ||
| import type { Awaitable, NextAuthOptions } from ".." | ||
| import type { JWT } from "../jwt" | ||
|
|
||
| import { NextResponse, NextRequest } from "next/server" | ||
|
|
||
| import { getToken } from "../jwt" | ||
| import parseUrl from "../lib/parse-url" | ||
|
|
||
| type AuthorizedCallback = (params: { | ||
| token: JWT | null | ||
| req: NextRequest | ||
| }) => Awaitable<boolean> | ||
|
|
||
| export interface NextAuthMiddlewareOptions { | ||
| /** | ||
| * Where to redirect the user in case of an error if they weren't logged in. | ||
| * Similar to `pages` in `NextAuth`. | ||
| * | ||
| * --- | ||
| * [Documentation](https://next-auth.js.org/configuration/pages) | ||
| */ | ||
| pages?: NextAuthOptions["pages"] | ||
| callbacks?: { | ||
| /** | ||
| * Callback that receives the user's JWT payload | ||
| * and returns `true` to allow the user to continue. | ||
| * | ||
| * This is similar to the `signIn` callback in `NextAuthOptions`. | ||
| * | ||
| * If it returns `false`, the user is redirected to the sign-in page instead | ||
| * | ||
| * The default is to let the user continue if they have a valid JWT (basic authentication). | ||
| * | ||
| * How to restrict a page and all of it's subpages for admins-only: | ||
| * @example | ||
| * | ||
| * ```js | ||
| * // `pages/admin/_middleware.js` | ||
| * import { withAuth } from "next-auth/middleware" | ||
| * | ||
| * export default withAuth({ | ||
| * callbacks: { | ||
| * authorized: ({ token }) => token?.user.isAdmin | ||
| * } | ||
| * }) | ||
| * ``` | ||
| * | ||
| * --- | ||
| * [Documentation](https://next-auth.js.org/getting-started/nextjs/middleware#api) | [`signIn` callback](configuration/callbacks#sign-in-callback) | ||
| */ | ||
| authorized?: AuthorizedCallback | ||
| } | ||
| } | ||
|
|
||
| async function handleMiddleware( | ||
| req: NextRequest, | ||
| options: NextAuthMiddlewareOptions | undefined, | ||
| onSuccess?: (token: JWT | null) => Promise<any> | ||
| ) { | ||
| const signInPage = options?.pages?.signIn ?? "/api/auth/signin" | ||
| const errorPage = options?.pages?.error ?? "/api/auth/error" | ||
| const basePath = parseUrl(process.env.NEXTAUTH_URL).path | ||
| // Avoid infinite redirect loop | ||
| if ( | ||
| req.nextUrl.pathname.startsWith(basePath) || | ||
| [signInPage, errorPage].includes(req.nextUrl.pathname) | ||
| ) { | ||
| return | ||
| } | ||
|
|
||
| if (!process.env.NEXTAUTH_SECRET) { | ||
| console.error( | ||
| `[next-auth][error][NO_SECRET]`, | ||
| `\nhttps://next-auth.js.org/errors#no_secret` | ||
| ) | ||
|
|
||
| return { | ||
| redirect: NextResponse.redirect(`${errorPage}?error=Configuration`), | ||
| } | ||
| } | ||
|
|
||
| const token = await getToken({ req: req as any }) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @balazsorban44 Does this middleware will work with I have similar middleware, but with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Only supports the "jwt" session strategy. We need to wait until databases at the Edge become mature enough to ensure a fast experience. (If you know of an Edge-compatible database, we would like if you proposed a new Adapter)" mind sharing your sessions middleware function? 👀 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it is pretty simple export async function middleware(request: NextRequest) {
const cookie = request.headers.get('cookie')
const session = cookie
? await getSession({ req: { headers: { cookie } } as any })
: null
if (!session) {
return NextResponse.redirect(new URL('/api/auth/signin', request.url))
}
} |
||
|
|
||
| const isAuthorized = | ||
| (await options?.callbacks?.authorized?.({ req, token })) ?? !!token | ||
|
|
||
| // the user is authorized, let the middleware handle the rest | ||
| if (isAuthorized) return await onSuccess?.(token) | ||
|
|
||
| // the user is not logged in, re-direct to the sign-in page | ||
| return NextResponse.redirect( | ||
| `${signInPage}?${new URLSearchParams({ callbackUrl: req.url })}` | ||
| ) | ||
| } | ||
|
|
||
| export type WithAuthArgs = | ||
| | [NextRequest] | ||
| | [NextRequest, NextFetchEvent] | ||
| | [NextRequest, NextAuthMiddlewareOptions] | ||
| | [NextMiddleware] | ||
| | [NextMiddleware, NextAuthMiddlewareOptions] | ||
| | [NextAuthMiddlewareOptions] | ||
|
|
||
| /** | ||
| * Middleware that checks if the user is authenticated/authorized. | ||
| * If if they aren't, they will be redirected to the login page. | ||
| * Otherwise, continue. | ||
| * | ||
| * @example | ||
| * | ||
| * ```js | ||
| * // `pages/_middleware.js` | ||
| * export { default } from "next-auth/middleware" | ||
| * ``` | ||
| * | ||
| * --- | ||
| * [Documentation](https://next-auth.js.org/getting-started/middleware) | ||
| */ | ||
| export function withAuth(...args: WithAuthArgs) { | ||
| if (args[0] instanceof NextRequest) { | ||
| // @ts-expect-error | ||
| return handleMiddleware(...args) | ||
| } | ||
|
|
||
| if (typeof args[0] === "function") { | ||
| const middleware = args[0] | ||
| const options = args[1] as NextAuthMiddlewareOptions | undefined | ||
| return async (...args: Parameters<NextMiddleware>) => | ||
| await handleMiddleware(args[0], options, async (token) => { | ||
| ;(args[0] as any).nextauth = { token } | ||
| return await middleware(...args) | ||
| }) | ||
| } | ||
|
|
||
| const options = args[0] | ||
| return async (...args: Parameters<NextMiddleware>) => | ||
| await handleMiddleware(args[0], options) | ||
| } | ||
balazsorban44 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| export default withAuth | ||
Uh oh!
There was an error while loading. Please reload this page.