Как мне добавить базовую аутентификацию на сервер узла Nextjs?

#node.js #next.js

#node.js #next.js

Вопрос:

Я не хочу добавлять аутентификацию к какому-либо внешнему запросу или OAuth. Просто базовая аутентификация на стороне сервера для сервера узла.

На момент написания Next.js похоже, это не подтверждается: https://github.com/vercel/next.js/discussions/17719

Он поддерживает аутентификацию, но я предполагаю, что ее нужно будет добавлять к каждому отдельному маршруту приложения: https://nextjs.org/docs/authentication

Комментарии:

1. Вы действительно имеете в виду «(Basic Auth) [ developer.mozilla.org/en-US/docs/Web/HTTP/Authentication ] «? Это буквально просто HTTP-заголовок Authorization , представляющий собой версию user и pw на base64, и 401 ответ, если он отсутствует, который должен запрашивать браузер пользователя. @Su-AuHwangHas отличный пример того, как это сделать.

Ответ №1:

— ОБНОВЛЕНО для NextJS 12.2 —

Текущая версия Nextjs позволяет запускать have add custom middleware, просто имея middleware.js/ts файл в вашем root каталоге (рядом с папкой pages). Там вы можете просто проверить Auth-заголовки и решить либо вызвать next , чтобы разрешить это, либо вернуть код состояния 401, чтобы запретить это. Вот пример, скопированный из примера Nextjs (typescript)

 import { NextRequest, NextResponse } from 'next/server'

export const config = {
  matcher: '/',
}

export function middleware(req: NextRequest) {
  const basicAuth = req.headers.get('authorization')
  const url = req.nextUrl

  if (basicAuth) {
    const authValue = basicAuth.split(' ')[1]
    const [user, pwd] = atob(authValue).split(':')

    if (user === '4dmin' amp;amp; pwd === 'testpwd123') {
      return NextResponse.next()
    }
  }
  url.pathname = '/api/auth'

  return NextResponse.rewrite(url)
}
  

вот ссылка на полный пример проекта:
https://github.com/vercel/examples/tree/main/edge-functions/basic-auth-password

— ДЛЯ БОЛЕЕ СТАРЫХ ВЕРСИЙ (до 12.2)—

Текущая версия Nextjs позволяет запускать have add custom middleware, просто имея _middleware.js/ts файл в вашем pages каталоге. Там вы можете просто проверить Auth-заголовки и решить либо вызвать next , чтобы разрешить это, либо вернуть код состояния 401, чтобы запретить это. Вот пример, скопированный из примера Nextjs (typescript)

 import { NextRequest, NextResponse } from 'next/server'

export function middleware(req: NextRequest) {
  const basicAuth = req.headers.get('authorization')

  if (basicAuth) {
    const auth = basicAuth.split(' ')[1]
    const [user, pwd] = Buffer.from(auth, 'base64').toString().split(':')

    if (user === '4dmin' amp;amp; pwd === 'testpwd123') {
      return NextResponse.next()
    }
  }

  return new Response('Auth required', {
    status: 401,
    headers: {
      'WWW-Authenticate': 'Basic realm="Secure Area"',
    },
  })
}
  

вот ссылка на полный пример проекта:
https://github.com/vercel/examples/tree/main/edge-functions/basic-auth-password

Комментарии:

1. Мне пришлось просто вызвать файл middleware.ts (без _ ) и поместить его в корень моего Next.js приложение (не pages включено), но затем оно сработало (как показано здесь: github.com/vercel/examples/tree/main/edge-functions /… ).

2. @BennyNeugebauer ах да, спасибо, что указали на это, NextJS-промежуточное программное обеспечение вышло из бета-версии, и, к счастью, они отошли от маршрутизации на основе файлов, теперь вместо этого используется сопоставитель.

Ответ №2:

Редактировать: приведенное ниже решение ExpressJS не будет работать, если вы хотите разместить его, например, на Vercel, который стоит немного, но предлагает лучшее Next.js хостинг со встроенным пограничным кэшированием, масштабированием и оптимизацией изображений. Итак, если вы хотите разместить его на Vercel, чем добавлять аутентификацию к каждому отдельному маршруту, используя: https://nextjs.org/docs/authentication (может быть, не маленькие ножки, если не сделано с самого начала проекта).

Если вы, однако, намерены разместить его где-нибудь еще, который поддерживает Node.js и Express.js хостинг (и не предлагает ничего особенного для Next.js ) тогда приведенное ниже — отличное решение.

Решение ExpressJS:

Этот пост не распространяется на хранение базовых учетных данных аутентификации или использование нескольких учетных данных. Это скорее отправная точка, потому что я вообще ничего не смог найти в Google.

Мы будем использовать Express.js для размещения NextJS (в рабочем режиме!) в обоих примерах ниже.

Для запуска сервера в обоих примерах: node index.mjs (в настоящее время используется node -v 14.9.0)

Сначала: создайте index.mjs файл в корне.

(Вариант 1) Использование последней версии NextJS:

  1. Используйте последнюю версию (9.5 на момент написания) NextJS Express.js сервер узловпоследней версии nextjs исправлены проблемы (я не проверял) с CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP )
  2. Внутри index.mjs добавьте следующий код (установите необходимые модули):
     import next from 'next'
    import express from 'express'

    import auth from 'basic-auth'

    const dev = process.env.NODE_ENV === 'development'
    const app = next({ dev })
    const handle = app.getRequestHandler()

    app.prepare()
      .then(() => {
        const server = express()

        server.use(function (req, res, next) {
          const credentials = auth(req)

          if (!credentials || credentials.name !== '[change_me_username]' || credentials.pass !== '[change_me_password]') {
            res.status(401)
            res.header('WWW-Authenticate', 'Basic realm="example"')
            res.send('Access denied')
          } else {
            next()
          }
        });

        server.get('*', (req, res) => {
          return handle(req, res)
        })

        const port = 80;
        server.listen(port, (err) => {
          if (err) {
            throw err
          }
          console.log(`Ready on http://localhost:${port}`)
        })
      })
      .catch((ex) => {
        console.error(ex.stack)
        process.exit(1)
      })
  

(Вариант 2) Использование NextJS 9.4 (если вы не можете обновить)

Этот ответ не касается потенциальных последствий от потенциальных целостностей безопасности CSP, лучший и самый простой способ справиться с этим — перейти на период NextJS 9.5.

  1. Внутри index.mjs добавьте следующий код (установите необходимые модули):
     import next from 'next'
    import express from 'express'

    import auth from 'basic-auth'

    const dev = process.env.NODE_ENV === 'development'
    const app = next({ dev })
    const handle = app.getRequestHandler()

    app.prepare()
      .then(() => {
        const server = express()

        server.use(function (req, res, next) {
          const credentials = auth(req)

          if (!credentials || credentials.name !== '[change_me_username]' || credentials.pass !== '[change_me_password]') {
            res.status(401)
            res.header('WWW-Authenticate', 'Basic realm="example"')
            res.send('Access denied')
          } else {
            // this is different from above NextJS 9.5, 9.4 requires inline js.
            res.setHeader(
              'Content-Security-Policy',
              "default-src * 'self' data: 'unsafe-inline' *"
            );
            next()
          }
        });

        server.get('*', (req, res) => {
          return handle(req, res)
        })

        const port = 80;
        server.listen(port, (err) => {
          if (err) {
            throw err
          }
          console.log(`Ready on http://localhost:${port}`)
        })
      })
      .catch((ex) => {
        console.error(ex.stack)
        process.exit(1)
      })
  
  1. Внутри next.config.js , если вы используете next-images , в противном случае просто поместите объект: module.exports = { // ... }
 const withImages = require('next-images')
module.exports = withImages({
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            // // https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
            key: 'Content-Security-Policy',
            value: "default-src * 'self' data: 'unsafe-inline' *"
          }
        ]
      }
    ]
  }
})