简体   繁体   中英

nextjs route middleware for authentication

I'm trying to figure out an appropriate way of doing authentication, which I know is a touchy subject on the GitHub issue page .

My authentication is simple. I store a JWT token in the session. I send it to a different server for approval. If I get back true, we keep going, if I get back false, it clears the session and puts sends them to the main page.

In my server.js file I have the following (note- I am using the example from nextjs learn and just adding isAuthenticated ):

function isAuthenticated(req, res, next) {
  //checks go here

  //if (req.user.authenticated)
   // return next();

  // IF A USER ISN'T LOGGED IN, THEN REDIRECT THEM SOMEWHERE
  res.redirect('/');
}

server.get('/p/:id', isAuthenticated, (req, res) => {
  const actualPage = '/post'
  const queryParams = { id: req.params.id }
  app.render(req, res, actualPage, queryParams)
})

This works as designed. If I refresh the page /p/123 , it will redirect to the / . However, if I go there via a next/link href, it doesn't. Which I believe is because it's not using express at this point but next's custom routing.

Is there a way I can bake in a check for every single next/link that doesn't go through express so that I can make sure the user is logged in?

Tim from the next chat helped me solve this. Solution can be found here but I will quote him so you all can see:

I've also created an example skeleton template you can take a look at.

--

EDIT July 2021 - WARNING: This is an outdated solution and has not been confirmed to work with the latest versions of next.js. Use skeleton template at your own risk.

没有middleware在没有API的路线NextJS ,但也有肝卵圆细胞,它可以用来连接数据库-选择用户,等: https://hoangvvo.com/blog/nextjs-middleware

With the release of Next.js 12, there's now beta support for middleware using Vercel Edge Functions.

https://nextjs.org/blog/next-12#introducing-middleware

Middleware uses a strict runtime that supports standard Web APIs like fetch. > This works out of the box using next start, as well as on Edge platforms like Vercel, which use Edge Functions.

To use Middleware in Next.js, you can create a file pages/_middleware.js. In this example, we use the standard Web API Response (MDN):

// pages/_middleware.js

export function middleware(req, ev) {
  return new Response('Hello, world!')
}

JWT Authentication example

in next.config.js :

const withTM = require('@vercel/edge-functions-ui/transpile')()

module.exports = withTM()

in pages/_middleware.js :

import { NextRequest, NextResponse } from 'next/server'
import { setUserCookie } from '@lib/auth'

export function middleware(req: NextRequest) {
  // Add the user token to the response
  return setUserCookie(req, NextResponse.next())
}

in pages/api/_middleware.js :

import type { NextRequest } from 'next/server'
import { nanoid } from 'nanoid'
import { verifyAuth } from '@lib/auth'
import { jsonResponse } from '@lib/utils'

export async function middleware(req: NextRequest) {
  const url = req.nextUrl

  if (url.searchParams.has('edge')) {
    const resOrPayload = await verifyAuth(req)

    return resOrPayload instanceof Response
      ? resOrPayload
      : jsonResponse(200, { nanoid: nanoid(), jwtID: resOrPayload.jti })
  }
}

in pages/api/index.js :

import type { NextApiRequest, NextApiResponse } from 'next'
import { verify, JwtPayload } from 'jsonwebtoken'
import { nanoid } from 'nanoid'
import { USER_TOKEN, JWT_SECRET_KEY } from '@lib/constants'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== 'GET') {
    return res.status(405).json({
      error: { message: 'Method not allowed' },
    })
  }
  try {
    const token = req.cookies[USER_TOKEN]
    const payload = verify(token, JWT_SECRET_KEY) as JwtPayload
    res.status(200).json({ nanoid: nanoid(), jwtID: payload.jti })
  } catch (err) {
    res.status(401).json({ error: { message: 'Your token has expired.' } })
  }
}

I created mine by creating a wrapper component leveraging NextAuth.js:

const AUTH_FLOW = '/api/auth'

/**
 * Provider to wrap the components in to check the user is authenticated before displaying a page.
 */
const Auth: React.FC<React.PropsWithChildren> = ({ children }) => {
    const { data: session, status } = useSession()
    const router = useRouter();
    React.useEffect(() => {
        const current_url = router.asPath
        const inAuthFlow = current_url.startsWith(AUTH_FLOW)
        // Delegate access to auth pages to NextAuth.js
        if (inAuthFlow) {
            return;
        }
        if (status == 'unauthenticated') {
            signIn();
        }
    }, [session])
    if (status === "authenticated") {
        return <>{children}</>
    }
    return <div>Loading...</div>
}

/**
 * Application initializer.
 */
const App = ({
    Component,
    pageProps: { session, ...pageProps }
}: AppProps): JSX.Element => {
    return (
        <SessionProvider session={session}>
            <Auth>
                <Layout>
                    <Component {...pageProps} />
                </Layout>
            </Auth>
        </SessionProvider>
    )
}

If a user is not on the auth pages and is not logged in, they'll be sent to the login screen.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM