Next.js: Добавить пример входа / аутентификации

Созданный на 29 окт. 2016  ·  208Комментарии  ·  Источник: vercel/next.js

С участием:

  • многоразовый помощник аутентификации на разных страницах
  • синхронизация сеанса между вкладками
  • простой сервер электронной почты без пароля, размещенный на now.sh

Я думаю, что это будет очень полезно для многих новичков.

Самый полезный комментарий

Так что у меня есть авторизация, которая работает без проблем. Как уже упоминалось в другом месте, это только на стороне клиента, что, в конечном счете, только полдела.

"Довольно-безопасный"

Как и php, атомарной единицей Next является страница. Одна из самых крутых функций заключается в том, что он лениво загружает каждую страницу только по запросу. С аутентификацией только на стороне клиента, но с серверным рендерингом, js для этой защищенной страницы фактически загружается браузером. Надеемся, что в будущем, когда Next добавит серверные рабочие процессы, вы сможете заблокировать рендеринг и перенаправление на сервере, чтобы полностью предотвратить это. Для этого потребуются файлы cookie, сеансы и хранилища сеансов AFAIK, но это всего лишь стоимость создания подобных гибридных приложений.

Пример аутентификации

Предположим, у вас есть защищенный JWT API с двумя интересующими конечными точками: /token и /me . /token принимает учетные данные электронной почты / пароля и возвращает подписанный JWT ( id_token ), а /me возвращает информацию профиля, относящуюся к пользователю, прошедшему аутентификацию JWT. Я адаптировал следующий AuthService.js из блокировки Auth0 (удаление эмиттера событий, хотя это не худшая идея). Он извлекает почти всю обработку токена JWT, поэтому его можно использовать на странице входа в систему, а также в компоненте более высокого порядка (подробнее об этом позже).

// utils/AuthService.js
export default class AuthService {
  constructor(domain) {
    this.domain = domain || 'http://localhost:5000'
    this.fetch = this.fetch.bind(this)
    this.login = this.login.bind(this)
    this.getProfile = this.getProfile.bind(this)
  }

  login(email, password) {
    // Get a token
    return this.fetch(`${this.domain}/token`, {
      method: 'POST',
      body: JSON.stringify({
        email,
        password
      })
    }).then(res => {
      this.setToken(res.id_token)
      return this.fetch(`${this.domain}/user`, {
        method: 'GET'
      })
    }).then(res => {
      this.setProfile(res)
      return Promise.resolve(res)
    })
  }

  loggedIn(){
    // Checks if there is a saved token and it's still valid
    const token = this.getToken()
    return !!token && !isTokenExpired(token) // handwaiving here
  }

  setProfile(profile){
    // Saves profile data to localStorage
    localStorage.setItem('profile', JSON.stringify(profile))
  }

  getProfile(){
    // Retrieves the profile data from localStorage
    const profile = localStorage.getItem('profile')
    return profile ? JSON.parse(localStorage.profile) : {}
  }

  setToken(idToken){
    // Saves user token to localStorage
    localStorage.setItem('id_token', idToken)
  }

  getToken(){
    // Retrieves the user token from localStorage
    return localStorage.getItem('id_token')
  }

  logout(){
    // Clear user token and profile data from localStorage
    localStorage.removeItem('id_token');
    localStorage.removeItem('profile');
  }

  _checkStatus(response) {
    // raises an error in case response status is not a success
    if (response.status >= 200 && response.status < 300) {
      return response
    } else {
      var error = new Error(response.statusText)
      error.response = response
      throw error
    }
  }

  fetch(url, options){
    // performs api calls sending the required authentication headers
    const headers = {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    }

    if (this.loggedIn()){
      headers['Authorization'] = 'Bearer ' + this.getToken()
    }

    return fetch(url, {
      headers,
      ...options
    })
    .then(this._checkStatus)
    .then(response => response.json())
  }
}

Далее идет HOC, чтобы упростить защиту страниц. Чтобы предотвратить нежелательную вспышку конфиденциальной информации, страница будет обрабатывать сервер Loading... при первом рендеринге, в то время как реакция загружает / считывает токен из localStorage. Это означает, что защищенные страницы не будут использовать SEO, что, вероятно, на данный момент нормально, но определенно не оптимально.

// utils/withAuth.js - a HOC for protected pages
import React, {Component} from 'react'
import AuthService from './auth'

export default function withAuth(AuthComponent) {
    const Auth = new AuthService('http://localhost:5000')
    return class Authenticated extends Component {
      constructor(props) {
        super(props)
        this.state = {
          isLoading: true
        };
      }

      componentDidMount () {
        if (!Auth.loggedIn()) {
          this.props.url.replaceTo('/')
        }
        this.setState({ isLoading: false })
      }

      render() {
        return (
          <div>
          {this.state.isLoading ? (
              <div>LOADING....</div>
            ) : (
              <AuthComponent {...this.props}  auth={Auth} />
            )}
          </div>
        )
      }
    }
}
// ./pages/dashboard.js
// example of a protected page
import React from 'react'
import withAuth from  '../utils/withAuth'

class Dashboard extends Component {
   render() {
     const user = this.props.auth.getProfile()
     return (   
         <div>Current user: {user.email}</div>
     )
   }
}

export default withAuth(Dashboard) 

Страница входа в систему не может использовать HOC в ее нынешнем виде, потому что вход в систему должен быть общедоступным. Таким образом, он просто создает экземпляр AuthService напрямую. Вы бы сделали нечто подобное и для страницы регистрации.

// ./pages/login.js
import React, {Component} from 'react'
import AuthService from '../utils/AuthService'

const auth = new AuthService('http://localhost:5000')

class Login extends Component {
  constructor(props) {
    super(props)
    this.handleSubmit = this.handleSubmit.bind(this)
  }

  componentDidMount () {
    if (auth.loggedIn()) {
      this.props.url.replaceTo('/admin')   // redirect if you're already logged in
    }
  }

  handleSubmit (e) {
    e.preventDefault()
    // yay uncontrolled forms!
    auth.login(this.refs.email.value, this.refs.password.value)
      .then(res => {
        console.log(res)
        this.props.url.replaceTo('/admin')
      })
      .catch(e => console.log(e))  // you would show/hide error messages with component state here 
  }

  render () {
    return (
      <div>
         Login
          <form onSubmit={this.handleSubmit} >
            <input type="text" ref="email"/>
            <input type="password" ref="password"/>
            <input type="submit" value="Submit"/>
          </form>
      </div>
    )
  }
}

export default Login

Вдохновленный реакцией со стилями Airbnb, я также начал работать над библиотекой next-with-auth lib, которая будет функцией, возвращающей HOC для использования на страницах. Я также играл с объединением AuthService и этого HOC. Одно из решений могло бы заключаться в том, чтобы этот HOC принимал функцию уровня разрешений в качестве аргумента в дополнение к компоненту, например, redux connect. В любом случае, на мой взгляд, вы бы использовали next-with-auth вот так:

// ./utils/withAuth.js
import nextAuth from 'next/auth'
import parseScopes from './parseScopes'

const Loading = () => <div>Loading...</div>

export default nextAuth({
  url: 'http://localhost:5000',
  tokenEndpoint: '/api/token',
  profileEndpoint: '/api/me',
  getTokenFromResponse: (res) => res.id_token,
  getProfileFromResponse: (res) => res,
  parseScopes,
})

Выполнение всего этого с помощью Redux казалось излишне сложным, но в основном вы можете следовать примеру вики, но переместите AuthService в Действия (вход и выход) и получите User Reducer. Однако вы можете вызывать эти действия только на клиенте, поскольку на сервере нет localStorage, поэтому вам нужно проверить это в своих действиях. В конце концов, redux store все равно помещается на window . Таким образом, вы можете просто хорошо кэшировать пользователя в window самостоятельно вместо использования контекста. Если вы не хотите редукции, вы также можете попробовать react-broadcast .

Наконец, при условии, что next/server отправляется согласно пункту 25. next-with-auth может отвлечь от разработчика сложное локальное хранилище и файлы cookie с помощью промежуточного программного обеспечения + HOC. Он также может обрабатывать обновление токена.

Все 208 Комментарий

Предложение: используйте Redux и JWT для выполнения примера

Я работаю над примером для этого. В настоящее время возникают проблемы с запуском componentWillReceiveProps для моего компонента высокого уровня (где я планирую проверить, аутентифицирован ли пользователь, и перенаправить на страницу входа, если нет)

Так что у меня есть авторизация, которая работает без проблем. Как уже упоминалось в другом месте, это только на стороне клиента, что, в конечном счете, только полдела.

"Довольно-безопасный"

Как и php, атомарной единицей Next является страница. Одна из самых крутых функций заключается в том, что он лениво загружает каждую страницу только по запросу. С аутентификацией только на стороне клиента, но с серверным рендерингом, js для этой защищенной страницы фактически загружается браузером. Надеемся, что в будущем, когда Next добавит серверные рабочие процессы, вы сможете заблокировать рендеринг и перенаправление на сервере, чтобы полностью предотвратить это. Для этого потребуются файлы cookie, сеансы и хранилища сеансов AFAIK, но это всего лишь стоимость создания подобных гибридных приложений.

Пример аутентификации

Предположим, у вас есть защищенный JWT API с двумя интересующими конечными точками: /token и /me . /token принимает учетные данные электронной почты / пароля и возвращает подписанный JWT ( id_token ), а /me возвращает информацию профиля, относящуюся к пользователю, прошедшему аутентификацию JWT. Я адаптировал следующий AuthService.js из блокировки Auth0 (удаление эмиттера событий, хотя это не худшая идея). Он извлекает почти всю обработку токена JWT, поэтому его можно использовать на странице входа в систему, а также в компоненте более высокого порядка (подробнее об этом позже).

// utils/AuthService.js
export default class AuthService {
  constructor(domain) {
    this.domain = domain || 'http://localhost:5000'
    this.fetch = this.fetch.bind(this)
    this.login = this.login.bind(this)
    this.getProfile = this.getProfile.bind(this)
  }

  login(email, password) {
    // Get a token
    return this.fetch(`${this.domain}/token`, {
      method: 'POST',
      body: JSON.stringify({
        email,
        password
      })
    }).then(res => {
      this.setToken(res.id_token)
      return this.fetch(`${this.domain}/user`, {
        method: 'GET'
      })
    }).then(res => {
      this.setProfile(res)
      return Promise.resolve(res)
    })
  }

  loggedIn(){
    // Checks if there is a saved token and it's still valid
    const token = this.getToken()
    return !!token && !isTokenExpired(token) // handwaiving here
  }

  setProfile(profile){
    // Saves profile data to localStorage
    localStorage.setItem('profile', JSON.stringify(profile))
  }

  getProfile(){
    // Retrieves the profile data from localStorage
    const profile = localStorage.getItem('profile')
    return profile ? JSON.parse(localStorage.profile) : {}
  }

  setToken(idToken){
    // Saves user token to localStorage
    localStorage.setItem('id_token', idToken)
  }

  getToken(){
    // Retrieves the user token from localStorage
    return localStorage.getItem('id_token')
  }

  logout(){
    // Clear user token and profile data from localStorage
    localStorage.removeItem('id_token');
    localStorage.removeItem('profile');
  }

  _checkStatus(response) {
    // raises an error in case response status is not a success
    if (response.status >= 200 && response.status < 300) {
      return response
    } else {
      var error = new Error(response.statusText)
      error.response = response
      throw error
    }
  }

  fetch(url, options){
    // performs api calls sending the required authentication headers
    const headers = {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    }

    if (this.loggedIn()){
      headers['Authorization'] = 'Bearer ' + this.getToken()
    }

    return fetch(url, {
      headers,
      ...options
    })
    .then(this._checkStatus)
    .then(response => response.json())
  }
}

Далее идет HOC, чтобы упростить защиту страниц. Чтобы предотвратить нежелательную вспышку конфиденциальной информации, страница будет обрабатывать сервер Loading... при первом рендеринге, в то время как реакция загружает / считывает токен из localStorage. Это означает, что защищенные страницы не будут использовать SEO, что, вероятно, на данный момент нормально, но определенно не оптимально.

// utils/withAuth.js - a HOC for protected pages
import React, {Component} from 'react'
import AuthService from './auth'

export default function withAuth(AuthComponent) {
    const Auth = new AuthService('http://localhost:5000')
    return class Authenticated extends Component {
      constructor(props) {
        super(props)
        this.state = {
          isLoading: true
        };
      }

      componentDidMount () {
        if (!Auth.loggedIn()) {
          this.props.url.replaceTo('/')
        }
        this.setState({ isLoading: false })
      }

      render() {
        return (
          <div>
          {this.state.isLoading ? (
              <div>LOADING....</div>
            ) : (
              <AuthComponent {...this.props}  auth={Auth} />
            )}
          </div>
        )
      }
    }
}
// ./pages/dashboard.js
// example of a protected page
import React from 'react'
import withAuth from  '../utils/withAuth'

class Dashboard extends Component {
   render() {
     const user = this.props.auth.getProfile()
     return (   
         <div>Current user: {user.email}</div>
     )
   }
}

export default withAuth(Dashboard) 

Страница входа в систему не может использовать HOC в ее нынешнем виде, потому что вход в систему должен быть общедоступным. Таким образом, он просто создает экземпляр AuthService напрямую. Вы бы сделали нечто подобное и для страницы регистрации.

// ./pages/login.js
import React, {Component} from 'react'
import AuthService from '../utils/AuthService'

const auth = new AuthService('http://localhost:5000')

class Login extends Component {
  constructor(props) {
    super(props)
    this.handleSubmit = this.handleSubmit.bind(this)
  }

  componentDidMount () {
    if (auth.loggedIn()) {
      this.props.url.replaceTo('/admin')   // redirect if you're already logged in
    }
  }

  handleSubmit (e) {
    e.preventDefault()
    // yay uncontrolled forms!
    auth.login(this.refs.email.value, this.refs.password.value)
      .then(res => {
        console.log(res)
        this.props.url.replaceTo('/admin')
      })
      .catch(e => console.log(e))  // you would show/hide error messages with component state here 
  }

  render () {
    return (
      <div>
         Login
          <form onSubmit={this.handleSubmit} >
            <input type="text" ref="email"/>
            <input type="password" ref="password"/>
            <input type="submit" value="Submit"/>
          </form>
      </div>
    )
  }
}

export default Login

Вдохновленный реакцией со стилями Airbnb, я также начал работать над библиотекой next-with-auth lib, которая будет функцией, возвращающей HOC для использования на страницах. Я также играл с объединением AuthService и этого HOC. Одно из решений могло бы заключаться в том, чтобы этот HOC принимал функцию уровня разрешений в качестве аргумента в дополнение к компоненту, например, redux connect. В любом случае, на мой взгляд, вы бы использовали next-with-auth вот так:

// ./utils/withAuth.js
import nextAuth from 'next/auth'
import parseScopes from './parseScopes'

const Loading = () => <div>Loading...</div>

export default nextAuth({
  url: 'http://localhost:5000',
  tokenEndpoint: '/api/token',
  profileEndpoint: '/api/me',
  getTokenFromResponse: (res) => res.id_token,
  getProfileFromResponse: (res) => res,
  parseScopes,
})

Выполнение всего этого с помощью Redux казалось излишне сложным, но в основном вы можете следовать примеру вики, но переместите AuthService в Действия (вход и выход) и получите User Reducer. Однако вы можете вызывать эти действия только на клиенте, поскольку на сервере нет localStorage, поэтому вам нужно проверить это в своих действиях. В конце концов, redux store все равно помещается на window . Таким образом, вы можете просто хорошо кэшировать пользователя в window самостоятельно вместо использования контекста. Если вы не хотите редукции, вы также можете попробовать react-broadcast .

Наконец, при условии, что next/server отправляется согласно пункту 25. next-with-auth может отвлечь от разработчика сложное локальное хранилище и файлы cookie с помощью промежуточного программного обеспечения + HOC. Он также может обрабатывать обновление токена.

Рад попробовать это! Спасибо за баребонную реализацию :)

@jaredpalmer Я работаю над чем-то похожим. Как ваш AuthService работает, когда компонент отображается на стороне сервера? Серверу потребуется доступ к JWT, но он не может прочитать его из локального хранилища.

@amccloud Это не так. В этом вся проблема. HOC отображает <div>Loading..</div> на защищенных маршрутах и ​​должен прочитать токен и решить, следует ли перенаправлять в componentDidMount . Чтобы он работал так, как вы хотите, и отображал на стороне сервера, Next требуется # 25 или, по крайней мере, возможность установить cookie со значением JWT AFAIK.

Я использовал cookie-js, чтобы установить cookie, но это немного похоже на взлом ...
Дело в том, что если вы не отправляете cookie, вы теряете все преимущества nextjs и рендеринга на стороне сервера в аутентифицированных маршрутах.

@jaredpalmer, это здорово! Спасибо за попытку. Я постараюсь завершить реализацию вашего примера (или помочь вам в этом, если хотите) в следующие дни.

Эй! Я опубликовал пример с nextjs и auth0 здесь: https://github.com/luisrudge/next.js-auth0
Он имеет концепцию основного макета, а также «защищенных страниц», которые загружаются только после аутентификации пользователя.
Дай мне знать, что ты думаешь 🎉

@luisrudge потрясающе. Я клонирую и вношу некоторые изменения, но выглядит отлично

Прохладный! Как вы думаете, чего не хватает? Какие изменения вы думаете?

В воскресенье, 6 ноября 2016 г., в 13:12 -0200, "Дэн Зайдбанд" < [email protected] [email protected] > написал:

@luisr udgehttps: //github.com/luisrudge потрясающе. Я клонирую и вношу некоторые изменения, но выглядит отлично

Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на Gi tHubhttps: //github.com/zeit/next.js/issues/153#issuecomment -258687108 или отключите его чтение https://github.com/notifications/unsubscribe-auth/ AA5cE8NIsvQ_ITjc1gArTFgNXzEda4TSks5q7e5NgaJpZM4KkJmi.

1) Использование standard для линтинга (так что это согласуется со всем, что мы создаем дальше)
2) Добавление поддержки нескольких вкладок по запросу @rauchg
3) Часть css можно упростить

Я пришлю тебе пиар :)

Что вы имеете в виду под поддержкой нескольких вкладок?

В воскресенье, 6 ноября 2016 г., в 13:16 -0200, "Дэн Зайдбанд" < [email protected] [email protected] > написал:

1) Использование стандарта для линтинга (чтобы он соответствовал всему, что мы будем строить дальше)
2) Добавление поддержки нескольких вкладок по запросу @rauchgh https://github.com/rauchg
3) Часть css можно упростить

Я пришлю тебе пиар :)

Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на Gi tHubhttps: //github.com/zeit/next.js/issues/153#issuecomment -258687373 или отключите его чтение https://github.com/notifications/unsubscribe-auth/ AA5cE1A6jq4KZc9_ynukTCI4mU-rdsNaks5q7e81gaJpZM4KkJmi.

У вас есть 2 открытых вкладки, на одной вы выйдете, на другой автоматически выйдет.

Ах. Это супер круто!

В воскресенье, 6 ноября 2016 г., в 13:21 -0200, "Дэн Зайдбанд" < [email protected] [email protected] > написал:

У вас есть 2 открытых вкладки, выход из 1, автоматический выход из других

Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на Gi tHubhttps: //github.com/zeit/next.js/issues/153#issuecomment -258687707 или отключите его чтение https://github.com/notifications/unsubscribe-auth/ AA5cE9e2DA4_GgNQIVTMp0hx74G-6RmUks5q7fBfgaJpZM4KkJmi.

Привет @luisrudge, я отправил вам PR с изменениями https://github.com/luisrudge/next.js-auth0/pull/2

большое спасибо за это <3

Кстати, вот результат:

2016-11-06 11 14 31

@impronunciable @luisrudge Фантастическая реализация! Если вы хотите использовать его без Auth0, похоже, вам нужно будет только изменить файлы в каталоге ./utils, возможно, даже просто lock.js . Я скоро попробую это сделать. Кстати, многопользовательская вкладка выглядит потрясающе 💯

@ugiacoman Я начал внедрять небольшой сервер с passwordless.net, дайте мне знать, если вы хотите получить мой код в качестве отправной точки

@impronunciable Было бы здорово! На самом деле я собирался сделать что-то подобное с Twitter Fabric's Digits.

@impronuncible Я предлагаю не использовать пароль less.net, вместо этого вы можете просто использовать локальный паспорт и просто отправить пользователям ссылку с их адресом электронной почты и токеном в строке запроса.

Спасибо @impronunciable ❤️

@ugiacoman да, довольно легко удалить зависимость auth0. Я использовал его, потому что не хотел иметь отдельный api для обработки аутентификации.

@jaredpalmer, насколько я знаю, иметь номер 25 было бы здорово, но не блокирует? Я имею в виду, что у нас есть доступ к req на стороне сервера в getInitialProps так что ничто не мешает применить к нему cookie-parser ? Серверная аутентификация и управление сеансами - для меня все в новинку 😬

Кстати, учитывая, что localStorage нельзя использовать на стороне сервера, являются ли файлы cookie единственным способом проведения сеансов на стороне сервера? Я смутно помню, что это может быть не самое безопасное? Но есть ли другой вариант?

@sedubois

Подход с использованием файлов cookie может быть очень безопасным, если все сделано правильно. Сделать следующее довольно тривиально:

  • использовать флаг httpOnly (предотвращает доступ JavaScript к cookie)
  • использовать безопасный флаг (устанавливать cookie только для запросов https)
  • Подписанные файлы cookie (проверьте источник cookie)

Также есть очень значительное преимущество в задержке, когда вы можете получить доступ к информации аутентификации непосредственно на сервере.

Мы должны переместить этот пример в examples/ Я посмотрю, что я могу придумать.

Мне удалось использовать react-cookie для изоморфных файлов cookie, обернув nextjs на настраиваемом экспресс-сервере следующим образом:

const express = require('express')
const next = require('next')
const cookie = require('react-cookie')
const cookieParser = require('cookie-parser')

const app = next({ dev: true, dir: process.cwd() })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express()
  server.use(cookieParser())       // <---- this line

  server.get('*', (req, res) => {
    cookie.plugToRequest(req, res) // <---- this line
    return handle(req, res)
  })

  server.listen(3000, (err) => {
    if (err) throw err
    console.log('> Ready on http://localhost:3000')
  })
})

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

С точки зрения человека, который многому этому научился. Я думаю, было бы лучше, если бы примеры не полагались на сторонние сервисы, такие как auth0. Новичкам было бы более полезно увидеть более простой пример с формами входа / регистрации и с использованием Redux и JWT.

Пример, который мы планируем объединить, будет основан на API-интерфейсах сервера Node.js с открытым исходным кодом.

Я добавил пример аутентификации на основе электронной почты в пример стартового проекта на https://github.com/iaincollins/nextjs-starter.

Он имеет поддержку сеансов (с экспресс-сеансами на бэкэнде и браузером sessionStorage API для кеширования их на переднем конце), httpOnly cookies, проекция CSRF, использует встроенный SMTP для отправки электронных писем, легко изменить бэкэнд, который по умолчанию использует SQL Lite. . Для его запуска не требуется никакой настройки.

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

Я согласен с @iamjacks, что пример с JWT кажется хорошей идеей.

Я рад улучшить обработку ошибок и добавить такие функции, как простая страница профиля, которую пользователи могут редактировать, и интеграция паспорта с примерами для oAuth для Facebook, Google и Twitter, если это будет полезно для людей. Если у людей есть хорошие идеи о лучших способах распространения / предоставления информации о сеансе компонентам, мне это очень интересно.

Example screenshot showing what to expect

Это невероятно @iaincollins. Мы обязательно укажем это в примечаниях к выпуску 2.0 :)

@rauchg Спасибо! :)

Думаю, мне следует явно добавить, что в этом примере сеансы основаны как на клиенте, так и на сервере, т. Е. Работают с JavaScript и без него (и в системах без sessionStorage), и один и тот же сеанс совместно используется обоими.

Достижение этого становится немного сложным в компоненте сеанса, который вникает в переменные с такими именами, как req.connection._httpMessage.locals._csrf, чтобы получить токен CSRF из заголовков сервера - как объект 'req', передаваемый на страницы в getInitialProps () Любопытно, что он немного отличается от объекта req, представленного в Express, поскольку я обычно обращаюсь к нему через req.locals._csrf (однако req.session одинаков в обоих).

localStorage небезопасен. как лучше всего сделать его более безопасным. любой может украсть данные localStorage и снова поместить их в свой браузер и войти в систему как пользователь-жертва !!

@Chathula , это неправда. Этот аргумент неверен.
Если у кого-то есть физический доступ к браузеру, он может делать все, что угодно.

Это верно и для файлов cookie.

Использование localStorage для аутентификации в некотором роде безопасно, потому что мы можем избавиться от проблем, связанных с файлами cookie.
Но с другой стороны, это влияет на SSR.

@arunoda Я создал логин с помощью Laravel API и Next.js Client. Я храню authUser access_token внутри localStorage. затем проверьте, вошел ли пользователь в систему или нет, путем аутентификации. но это небезопасно. если кто-то украл данные localStorage. он / она может использовать это.

если кто-то украл данные localStorage.

Как? По сути, он должен иметь физический доступ к браузеру. Тогда этот человек мог делать все, что угодно.
Так что не стоит об этом беспокоиться.

нельзя ли использовать какое-либо шифрование, чтобы сделать его более безопасным?

@Chathula, это не по теме. Это не совсем актуально для Next.js, и мы хотим понять, как обычно работают веб-материалы.

@Chathula может быть, вы могли бы начать новую тему в вышеупомянутом стартовом проекте.

@arunoda хахха !! Спасибо за информацию! : D

@Chathula Я счастлив обсудить это подробнее в выпуске стартового проекта, если у вас есть особые опасения.

Я хотел бы исправить любое заблуждение, чтобы люди не волновались излишне.

API веб-хранилища (то есть localStorage и sessionStorage), как и файлы cookie без httpOnly, ограничен одной и той же политикой происхождения (протокол, имя хоста, номер порта), неверно, что «любой может украсть [его]»; но да, если кто-то может выполнить произвольный JavaScript на вашем сайте через уязвимость межсайтового скриптинга в вашем приложении, тогда они также могут получить доступ к магазину, поэтому вам не следует хранить в нем идентификаторы сеансов.

Вот почему вы увидите, что сам токен сеанса не хранится в localStorage / sessionStorage и не читается в JavaScript, он передается только через cookie только HTTP (вот почему класс сеанса использует XMLHttpRequest (), а не fetch () - как описано в документации класса).

Это означает, что даже если кто-то может использовать уязвимость межсайтового скриптинга в вашем приложении и выполнить произвольный JavaScript в вашем веб-приложении, он все равно не сможет прочитать или экспортировать токен сеанса пользователя.

Возможно, это важное различие, о котором стоит проработать в документации.

Примечание. Дополнительное шифрование пользовательских данных здесь бесполезно, потому что приложение всегда должно иметь возможность читать данные, чтобы их можно было отобразить (так что вам также необходимо сохранить ключ описания в приложении, который будет отображать шифрование данные пользователя довольно спорны).

_UPDATE: на прошлой неделе или две в примере был проведен рефакторинг, чтобы использовать localStorage вместо sessionStorage, поскольку sessionStorage не используется совместно между вкладками, а обмен несущественными данными таким образом сокращает количество ненужных проверок аутентификации и поддерживает согласованность статуса сеанса между вкладками.

Может быть, полезно для некоторых людей, которые я создал во время экспериментов:

https://github.com/possabilities/next.js-with-auth

При поддержке этого игрушечного бэкэнда:

https://github.com/possabilities/micro-auth

Развернуты здесь:

https://next-with-auth.now.sh/

Бэкэнд здесь:

https://micro-auth.now.sh/

@ Возможности Спасибо, Майк! А также предоставление отдельного микросервиса, который показывает хороший способ обработки защищенных страниц, который, как я думал, может быть хорошей прагмой и может вдохновить. У меня есть несколько идей, которые будут обсуждаться в следующем репозитории .js-with-auth.

Поразмыслив еще немного, я бы, наверное, не стал рассматривать приведенные выше усилия в качестве отличного примера. Я бы, вероятно, переключился на то, чтобы регистрация / вход была полностью отправлена ​​в стиле web 1.0, чтобы мы могли установить файл cookie только для HTTP на сервере (устраняя доступность JWT через XSS), а затем прикрепить объект пользователя к req а чем весь токен. Это оставляет возможность уязвимостей CSRF, но я думаю, что это проще устранить, чем XSS (нет?). Приятным побочным эффектом этого является то, что клиентское приложение может использовать почти идентичный поток при входе через службу Oath.

Оглядываясь назад, я также вижу, что могу избежать «промежуточного программного обеспечения» (и, следовательно, необходимости в настраиваемом сервере), анализируя файл cookie в Page HoC getInitialProps .

@possabilities Поскольку «секретная» страница - это просто страница, связанная с веб-пакетом, не будет ли она предоставлена ​​браузеру, если я перейду в / secret?

Да, я полагаю, это должно быть связано с перенаправлением на стороне сервера.

Кстати, я снова отвлекся на свой первоначальный проект входа в систему с помощью github. Процесс аналогичен с большей заботой о безопасности (а именно, не раскрывать какие-либо секреты на клиенте, чтобы токен oauth не был открыт через XSS). Он привязан к подходящему приложению, но если есть какой-то интерес, я могу разбить его на что-то, что может быть полезно для потока oauth в целом.

@ Возможности Я думаю, что это было бы

@sedubois У меня есть https://github.com/zeit/next.js/pull/646, но мы переместим сервер аутентификации в другое место

как насчет использования graphql?

Apollo приводит пример аутентификации graphql:

https://dev-blog.apollodata.com/a-guide-to-authentication-in-graphql-e002a4039d1

Здесь мы аутентифицируем запрос graphql, но его можно адаптировать для нашего случая.

Плюс graphql может абстрагироваться от реализации и логики. Его можно использовать с без пароля, с auth0 или с чем-то еще, что мы предпочтем.

@impronunciable FYI с вашим примером Я еще не знаю, как обрабатывать сеансы, так как я хочу избавиться от без пароля. Я попробую адаптировать пример @iaincollins в своем приложении.

Мои требования:

  • безопасный
  • проверка подлинности на стороне сервера, затем на стороне клиента
  • поддержка как Facebook, так и логина / пароля
  • провайдеры аутентификации должны быть легко изменены
  • создать пользователя в Graphcool
  • аутентифицировать последующие запросы GraphQL к Graphcool

Если кому-то это поможет, вот мое приложение с серверной аутентификацией 🙂

Авторизация должна быть отделена от сервера Next.js, но я жду, что кто-то еще подскажет по этому поводу ... Также я не уверен, что он должным образом защищен от CSRF.

Вот что я сделал:

  • когда пользователь входит в систему, клиентский javascript устанавливает файл cookie в браузере, содержащий токен носителя аутентификации
  • при выполнении аутентифицированных запросов код на стороне сервера (next.js) считывает токен-носитель в заголовках запроса браузера и использует его для связи с сервером api от имени клиента

Это уязвимо для XSS (даже если реакция делает многое для его предотвращения) и CSRF-атак, но это просто и работает с SSR.

Просто чтобы добавить к запросам

graph.cool + apollo + jwt + auth0 + next.js, первые 4 части уже выполнены на https://github.com/graphcool-examples/react-apollo-auth0-example

@balupton в вашем примере, что происходит, когда токен истекает, пока пользователь все еще подключен, сеанс только что закончился или он каким-то образом возобновляется?

@nmaro не знаю, написано не мной - лучше спросить там

Для моего собственного приложения у меня есть auth с next.js и auth0, а затем с сервером zeit / micro API, который проверяет токены-носители.

Должен появиться открытый исходный код где-то в феврале.

Следуя той же философии, которой следует next.js (делайте одно и делайте это хорошо), я разработал каркас расширяемой системы учетных записей пользователей для узла. См. Философию здесь: https://medium.com/the-ideal-system/ooth-user-accounts-for-node-js-93cfcd28ed1a#.97kyfg4xg

Проект github находится здесь: https://github.com/nmaro/ooth/

Цель состоит в том, чтобы иметь расширяемую, независимую службу аутентификации и управления пользователями, которая идеально подходит для использования в качестве отдельного микросервиса, структуры приложения, которая будет очень хорошо работать с node.js.

Пример с аутентификацией по электронной почте и паролю находится здесь: https://github.com/nmaro/ooth/tree/master/examples/ooth
Уже есть пакеты для Facebook и Google auth (ooth-facebook и ooth-google), которые должно быть легко реализовать на основе паспорта-facebook и паспорта-google соответственно.

Я опубликую пример интеграции next.js как можно скорее. Не стесняйтесь присоединяться к обсуждению и вносить свой вклад.

Извините за плагин, но это к лучшему - я действительно считаю, что пока нет хорошего решения для node, и это как раз подходящая аудитория людей, которым может понадобиться такая вещь. Мир

Между тем ... вот пример API-интерфейса graphql, который требует аутентификации с помощью JWT-Token только для операций записи. Не стесняйтесь использовать его со своим любимым методом аутентификации :)

https://github.com/nmaro/ooth/tree/master/examples/graphql-api-with-auth

Я обновил https://nextjs-starter.now.sh, чтобы добавить поддержку oAuth.

screen shot 2017-02-10 at 05 03 19

  • Он использует Passport для oAuth вместе с экспресс-сессиями (как и раньше).
  • Есть поддержка Facebook, Google и Twitter + oAuth, и легко добавить больше (см. AUTHENTICATION.md и routes / auth-паспорт.js ).
  • Он использует универсальную систему сеансов клиент / сервер (с токенами CSRF, защиту XSS с помощью файлов cookie только HTTP, уровень ORM, который поддерживает Mongo, SQL DB, Redshift и т. Д.) В качестве входа по электронной почте.
  • Могу сообщить, что опыт настройки oAuth на порталах разработчиков столь же ужасен, как и всегда (случаются странные ошибки, и их сложно отлаживать).

Природа oAuth - db + сеансы + паспорт и обработка ошибок, требующие тесного взаимодействия - и сеансы, требующие работы как клиента, так и сервера, и то, как это работает с универсальным рендерингом, - значит немного много, чтобы попытаться обернуть все сразу, если вы пытаетесь понять, что происходит, но клиентская логика не имеет какой-либо конкретной конфигурации oAuth, поэтому это не слишком беспорядочно.

Я был бы счастлив выделить просто аутентификацию в отдельный пример, хотя подозреваю, что он не будет намного меньше. Если кто-то еще захочет - отлично. Я, вероятно, когда-нибудь добавлю к этому примеру немного больше (например, страницу управления учетной записью). Возможно, было бы неплохо добавить больше ссылок на документацию.

Потрясающая работа! Если бы вы могли разделить аутентификацию и добавить ее в репозиторий next.js, это было бы здорово: heart:

Я тоже могу это сделать

У меня не будет времени в течение следующих 2 недель или около того, так что, если кто-то захочет, это будет здорово.

Я бы хотел его реорганизовать и посмотреть, можно ли его сократить до простого модуля (который предоставляет простые компоненты, такие как кнопки входа и формы входа для встраивания), и черпать вдохновение из действительно приятного более раннего примера только на стороне клиента пользователя @impronunciable.

Обновление: я действительно уезжаю, поэтому я не могу, но когда я вернусь, я счастлив посмотреть, как это сделать!

Я следил за руководством по быстрому запуску auth0 / react с некоторыми изменениями, но когда я вызываю lock.show() , приложение жалуется:

Неперехваченная ошибка: addComponentAsRefTo (...): ссылки могут быть только у ReactOwner. Возможно, вы добавляете ссылку на компонент, который не был создан внутри метода render компонента, или у вас загружено несколько копий React

@iaincollins @timneutkens относительно вашего примера, поправьте меня, если я ошибаюсь.

В примере используется файл cookie httpOnly для хранения токена сеанса, что делает его безопасным от атак Javascript Injection (XSS). Затем возникает проблема с хранением токена csrf в локальном хранилище, чтобы также защитить от атак CSRF.

В основе лежит предположение, что такая комбинация методов делает вещи безопасными, что может ввести пользователя / разработчика в заблуждение. Злоумышленник по-прежнему может внедрить JavaScript на странице (XSS), прочитать токен csrf и использовать его для выполнения аутентифицированных (cookie) запросов к API. Стоит упомянуть в ридми?

Привет @davibe

Увы, в настоящее время нет технически лучшего подхода, чем файл cookie сеанса и отдельный вращающийся токен CSRF, поэтому, думаю, я должен сказать, что в целом я доволен этой моделью, поскольку на самом деле нет другого способа сделать это. (даже если фактическая реализация всегда может быть улучшена).

Мы могли бы добавить отпечаток пальца браузера, возможно, токен сеанса мог бы вращаться чаще (я забыл, если это происходит автоматически прямо сейчас), токен CSRF может быть заголовком вместо параметра, файлы cookie действительно должны быть SSL только в производстве, и мы могли бы добавить дата истечения срока действия токенов входа, но AFAICS имеет довольно ограниченные возможности для улучшения в отношении модели безопасности и ничего, что действительно обеспечивает дополнительную защиту в случае, если кто-то сможет внедрить любой код, который ему нравится, в приложение; но, пожалуйста, не стесняйтесь поднимать эти вопросы как проблемы для улучшения репо.

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

Хранение токенов сеанса в локальном хранилище сделает его значительно менее безопасным и противоречит предполагаемому использованию в спецификации, поэтому лично я не поддерживаю это - хотя я ценю, что это упростило бы ситуацию, поскольку не имеет проекции CSRF, но люди, вероятно, не нужен пример этого, так как это было бы очень просто - я только попытался предоставить его, потому что это очень неудобно. :-)

Я полностью с вами в том, что я тоже ненавижу то, насколько это неизбежно неудобно, и не отказался от попыток превратить это в модуль.

Хм .. Понятно.
Этот выпуск мне очень пригодился, спасибо.

Это точка зрения Auth0 https://auth0.com/blog/cookies-vs-tokens-definitive-guide/
Они в основном предлагают избегать файлов cookie, но я думаю, что это оставит SSR при загрузке первой страницы.

Привет всем!

Я также работаю над аутентификацией с помощью next.js, и комментарии к этой проблеме, в частности @davibe + репо от @iaincollins и PR от @timneutkens, оказали огромную помощь.

Мое решение делает следующее:

  • После успешного входа в систему мой редуктор redux сохраняет токен и данные пользователя в файле cookie с помощью response-cookie .
  • Любая страница / компонент, которому требуется эта информация, упаковывается в компонент более высокого порядка, аналогичный @timneutkens with-session.js . Если SSR, токен будет доступен в ctx.req.headers.cookie, в противном случае просто возьмите его из документа браузера (используйте метод загрузки response-cookie).
  • Получив токен, я могу установить его в заголовках Bearer / Authorization всякий раз, когда я делаю запрос.

Помогает то, что у меня есть собственный микросервис аутентификации токена JWT, работающий в контейнере докеров.
Возможно, было бы проще предоставить простую службу JWT как часть примера with-auth? Таким образом, избегая взлома server.js и потенциально теряя преимущества встроенной горячей перезагрузки next.js, ssr и маршрутизации?

Я также не уверен, безопасен ли этот подход с точки зрения CSRF / XSS. Любые комментарии приветствуются.

Спасибо вам всем за отличную работу, которую вы проделали до сих пор. Я большой поклонник этого проекта!

@jcsmesquita Я работаю над новой версией примера, в которой все отделено от Next.js, подобно тому, как auth реализована на zeit.co.

Это кажется полезным: https://hub.docker.com/r/rabbotio/nap/~/dockerfile/

@subsumo Спасибо за упоминание, к вашему сведению: NAP - мой, аутентификация через Интернет основана на nextjs-starter плюс реагирует на логин собственного клиента с помощью токена.

@timneutkens позволяет ли решение, над которым вы работаете, аутентифицироваться в отдельной службе?

Я смотрел текущий пример запроса на аутентификацию https://github.com/zeit/next.js/pull/1141
Мне кажется, что он позволяет аутентифицироваться только по отношению к серверу next.js, но не изоморфно по отношению к отдельной службе.

Другими словами, предположим, что вы хотите разделить сервер next.js и фактический API приложения (например, REST или GraphQL), тогда вы хотите, чтобы клиент и сервер могли аутентифицироваться по отношению к API. Я не думаю, что это решение действительно поможет вам в этом.

Я подумал о потоке.

Сущности:

  • Клиент (C)
  • Сервер Next.js (S)
  • API (A)

Цель состоит в том, чтобы установить 3 сеанса на основе файлов cookie:

  1. CS
  2. CA
  3. SA

Сеанс 1) означает, что Клиент распознает Сервер, а 2) 3) означает, что и Клиент, и Сервер могут использовать свои соответствующие сеансы для доступа к API независимо.

Это поток:

  1. CS выполняется легко, аутентификация не требуется
  2. CA выполняется с аутентификацией (например, с помощью имени пользователя / пароля). Дополнительно предоставляется JWT
  3. Клиент предоставляет JWT серверу, а затем отбрасывает его
  4. SA выполняется с помощью JWT, который затем отбрасывается

Каково твое мнение? Есть способ попроще? Должны ли мы вместо этого просто сохранить API вместе с сервером next.js, чтобы нужен был только один сеанс (CS)?

@rauchg Мне

@timneutkens позволяет ли решение, над которым вы работаете, аутентифицироваться в отдельной службе?

Это идея да. Был занят исправлением других вещей на Next.js. Вернемся к нему как можно скорее.

Я разбил части своего приложения, относящиеся к github-auth, в довольно удобоваримый пример. может быть интересным для некоторых и хотел бы получить отзывы о коде или потоке: https://github.com/possabilities/next-github-auth-example

ОБНОВЛЕНИЕ: я преобразовал пример приложения в многократно используемый набор компонентов, которые можно «добавить» в следующие приложения.

Продолжение : я написал интеграцию с ooth и GraphQL API.

https://medium.com/the-ideal-system/ooth-user-accounts-for-node-js-93cfcd28ed1a#.ykoj1dhil

он основан на существующих подходах, т.е. аутентификации по отношению к next.js серверу, то есть он предполагает ИПН и сервер аутентификации для выполнения всех в том же самом процессе, так что только одна сессия должна быть создана. Преимущество: в принципе, он может хранить учетные данные для / может быть расширен практически для любой стратегии Passport.js.

@timneutkens есть ли прогресс в этом направлении?

Я реорганизовал свое примерное приложение github auth в набор декораторов и компонент страницы для многократного использования, чтобы «перетащить github auth в» следующие приложения. Отзывы о коде и функциональности приветствуются. https://github.com/possabilities/next-github-auth

Я _ думаю_, что было бы интересно пригвоздить доску здесь, а затем продолжить развивать это в более общую структуру аутентификации для следующего.

@timneutkens
Извините за пинг, но добились ли вы каких-либо успехов в этом вопросе? Я совершенно не понимаю, как мне правильно настроить аутентификацию с помощью next.js.

@kolpav Я поработал над этим, но сейчас завален другими вещами 😥 Безусловно, это довольно высокий приоритет в моем списке приоритетов для Next 😄

Ясный, хорошо документированный и безопасный способ аутентификации с помощью приложения next.js имеет решающее значение для его успеха в качестве фреймворка.

Я не могу вспомнить, когда в последний раз создавал сеть без аутентификации.
Как и большинство людей, которых я себе представляю, я также хочу выполнять защищенные вызовы для моих резервных данных, поэтому JWT кажется очевидным решением.

Но между вопросами и пиарами так много дискуссий, что я не знаю, с чего начать!

@timneutkens
Круто 👍 Думаю, это будет очень ценно для других.

@camstuart @kolpav Выше есть несколько хороших рабочих примеров, включая поддержку oAuth и аутентификации на основе электронной почты, которая использует как JWT- , так и HTTP-файлы cookie, @luisrudge , @impronunciable , @possabilities и мной.

Чтобы выделить некоторые ссылки, проверяющие работу:

(Пример микро-аутентификации был хорош, но я думаю, что его нужно обновить.)

Есть возможности для дальнейшего улучшения, которые дополнительные комментаторы прокомментировали выше - включая компонент хранилища сеансов и разделение логики сервера; и пример, который еще больше упрощает вещи, над которыми работал Тим.

Простота аутентификации - сложная область для универсальных приложений, но вы должны иметь возможность запустить приведенные выше примеры - или просто опробовать демонстрационные версии напрямую - и увидеть, как они работают, без особых хлопот.

@iaincollins Это отличные примеры. Но как я могу использовать (например) стартовый проект? Итак, если я хочу создать свое приложение. Мне нужно клонировать это репо? Или мне нужно «скопировать-вставить» код из фрагмента начального проекта фрагментом в свой собственный код?

Если стартовый проект будет обновлен - что делать?

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

Согласен с @kolpav , собственная безопасность - непростое дело. Лучше оставить экспертам

Я создал для этого стек, который может легко обрабатывать аутентификацию с помощью GraphQL: https://github.com/thebillkidy/MERGE-Stack

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

@kolpav @camstuart Эта ветка содержит действительно обширное обсуждение различных моделей безопасности, включая развенчание некоторых распространенных заблуждений и компромиссов. Особо отмечу моменты, касающиеся файлов cookie только HTTP и токенов CSRF (и дополнительной защиты, которую они обеспечивают от XSS и CSRF при использовании JWT и / или API веб-хранилища для токенов сеанса). Это действительно стоит прочитать.

@iaincollins Вы хотели что-то связать? :улыбка:

Привет ребята :)

У меня вопрос, я читал дальше, не могу получить токены для аутентификации с помощью jwt из localstorage.

Если у меня есть сервер для рендеринга только первого заряда моего сайта со следующим. И у меня есть еще один сервер для моего api. Это получает пользователя / пропуск от клиента и дает jwt. В каких случаях мне нужно получить токен на сервере ??

Зачем мне нужен токен на сервере рендеринга (следующий)?

Если клиент не отправляет токен на сервер api, api не предоставляет данные, и пользователь не может получить личную информацию. Я не понимаю, зачем мне отправлять токен на сервер рендеринга.

@kamilml

Это может помочь. В общем, у вас есть два варианта:

  1. Дайте JWT следующему серверу (возможно, через cookie). Это позволит серверу Next выполнять вызовы API от имени клиента. Вы бы хотели этого, если для вас важен полный рендеринг на стороне сервера.

  2. Храните JWT в локальном хранилище клиента и не давайте ему доступ к следующему серверу. В этом случае вы можете просто обойти серверную часть вызовов API и отложить полную визуализацию до завершения загрузки на стороне клиента.

Приносим извинения за повторное открытие, но я подумал, что добавлю свои 2 цента в эту ветку, и то, как мои первоначальные исследования и разработки развиваются в этой области. Меньше примеров кода, больше потока высокого уровня.

Во-первых, для контекста, большая часть нашего приложения уже построена на Symfony 3 (PHP) и использует Vue для гибридного взаимодействия. Сервер отображает страницу-оболочку и назначает данные приложения __INITIAL_STATE__ чтобы приложение могло их забрать. Это привело к принятию решения между рендерингом маркетинговых страниц в Symfony (для SEO) и выбором UX / UI вместо SEO в других областях путем извлечения данных через JS и обеспечения большего ощущения SPA. Также стоит отметить, что не все страницы являются общедоступными / частными двоичными (как я видел в некоторых примерах). Некоторые страницы по умолчанию являются общедоступными, а затем отображаются по-другому, если они аутентифицированы. Мы рассматривали возможность использования SPA для частей сайта, но во многих практических отношениях это был худший UX / UI (более медленный TTI, индикаторы выполнения и т. Д.). Кроме того, это не решит проблему SEO, если мы не введем FOUC и дважды отрендерим текст (один раз через Symfony, еще раз как компоненты JS) и т. Д.

Введите SSR / Universal JS ...

В моем случае я имитировал логику PHPSESSID , создав файл cookie HttpOnly UJSSESSID (придумал имя) и установил значение для пользователя. JWT. Приложение Symfony передает это в каждом запросе страницы, когда пользователь перемещается по сайту. Когда пользователь попадает на страницы UJS, серверная часть этих приложений получает файлы cookie в запросе (в соответствии со встроенным поведением браузера). Если установлен файл cookie UJSSESSID , приложение вызывает API для получения информации о пользователе (например, /api/v1/users/me с передачей токена через заголовок Authentication ). Остальные вызовы выполняются через API с использованием того же токена. Механизм выхода из Symfony очищает файл cookie UJSSESSID . В следующий раз, когда приложение UJS загрузится, оно отобразит страницы в анонимном пользовательском режиме. К вашему сведению, маршрутизация страниц Symfony и UJS выполняется через Apache ProxyPass . LocalStorage не используется.

Конечным результатом является цельный UX, где пользователь находится на некоторых страницах, написанных на PHP с клиентским JS, а на некоторых - на UJS. Это позволяет нам проводить A / B-тесты и итеративно обновлять сайт - не каждый может начать с нуля :)

Хотя это немного сложнее из-за симбиоза PHP / UJS, тот же принцип можно использовать в полном решении UJS с API или промежуточным программным обеспечением сервера Node.js (например, Express, Adonis и т. Д.). Вместо того, чтобы устанавливать файл cookie UJSSESSID через запрос страницы PHP (флаг HttpOnly ), попросите пользователя войти в систему через ваш SPA / UJS и установить там файл cookie. Чего вам НЕ СЛЕДУЕТ делать, так это использовать свое приложение для декодирования JWT или выполнять сторонние вызовы, требующие client_secret . Используйте для этого промежуточное ПО, которое остается на сервере.

Надеюсь, это кому-то поможет. Другие примеры, которые я видел, были для меня слишком чашечками Петри.

@jaredpalmer Привет, спасибо за эту реализацию, я попробовал, просто скопировал весь ваш код, заменил ваш dashboard.js на мой index.js, который выглядит так:

const index = () =>
  <div>
    <span>WoooHoooo</span>
  </div>

export default withAuth(index)

и в withAuth hoc я изменил его, чтобы перенаправить на страницу входа.
Но перед перенаправлением на страницу входа содержимое индексной страницы еще некоторое время мигает. : S

Каков статус этой проблемы? 😇

Это немного утомляет новичков, читающих всю дискуссию. Я решил реализовать здесь самую простую аутентификацию. Всего 2 страницы (индекс, логин) и собственный сервер
https://github.com/trandainhan/next.js-example-authentication-with-jwt

По сути, у нас есть промежуточное ПО для аутентификации на сервере, чтобы проверять токен в заголовке каждого запроса. Токен jwt будет храниться в файлах cookie. Я считаю, что это очень просто, понятно и очень хорошо работает.

@trandainhan Не могли бы вы добавить конечную точку POST, которая использует секретный токен для предотвращения атак CSRF?

@sbking Обновленный исходный код с примером конечной точки, защищенной от атак CSRF

Это готово к использованию 😬?

Кто-нибудь пробовал аутентификацию с помощью redux-auth-wrapper ?

Привет всем! За последние месяцы я создал, а теперь усовершенствовал

  • Универсальная библиотека учетных записей пользователей для node.js под названием ooth: https://github.com/nmaro/ooth
  • Начальная библиотека на основе next и ooth вместе с минимальным шаблоном: https://github.com/nmaro/staart

Особенности:

  • Регистрация по электронной почте и паролю
  • Войдите в систему с помощью электронной почты или имени пользователя и пароля
  • Страница учетной записи, где вы можете указать свое имя пользователя, изменить пароль и повторно отправить письмо с подтверждением.
  • Забыли пароль / сбросить пароль страницы
  • Подтвердите страницу электронной почты
  • Базовый API GraphQL, связанный с MongoDB (1 файл, можно легко удалить)
  • Минимальный шаблон (как можно больше логики инкапсулировано в библиотеках)

Посмотрите живую демонстрацию здесь: http://staart.nmr.io/

Я делал это в основном для себя для быстрого создания прототипа, чтобы быстро начать работу с приложением с простой, работающей системой учетных записей, которая не полагается на внешние службы. Результатом я вполне доволен. Я намерен продолжать использовать и поддерживать эти библиотеки, поэтому попробуйте, если вы чувствуете, что установленная система учетных записей + пользовательский интерфейс для узла все еще отсутствуют.

@trandainhan Спасибо, это действительно отличный пример, намного проще для многих, и он будет работать во многих сценариях.

Я собираюсь подумать о том, могу ли / как я могу адаптировать текущую логику в nextjs-starter, чтобы вместо этого использовать что-то вроде этого, но безопасно, при этом сохраняя совместимость с логикой экспресс-сеанса для реальных случаев использования, которые у меня есть (например, использование такие вещи, как API Google oAuth, где мне нужен сервер для хранения и отслеживания токенов, предоставленных при первом входе в систему).

Я еще не понял, возможно ли это, но людям было бы намного проще, если бы это было возможно.

Если нет, то, по крайней мере, стоит написать где-нибудь, чтобы объяснить людям различные варианты.

@trandainhan : Если я добавлю <Link href="/">Home</Link> в login.js, а затем щелкну созданную ссылку, я смогу получить доступ к index.js без входа в систему. Как вы предлагаете исправить это в своем примере?

@iaincollins Я вижу, что большинство решений здесь аутентифицируются против службы oauth. Есть ли хорошее решение для аутентификации по API, основанному на JWT?

@paulwehner Я думаю, что это происходит потому, что @trandainhan обрабатывает только маршрутизацию аутентификации на стороне сервера. Я до сих пор не понимаю, как маршрутизация на стороне клиента работает в next.js, потому что все обрабатывается внутренним компонентом next / Link.

@ carlos-peru Под капотом Link фактически использует next / router для добавления нового пути в историю браузера. Кажется, что большинство вещей обрабатывается браузером, здесь нечего делать для промежуточного программного обеспечения на стороне сервера. Пока я могу думать только о создании нашего собственного компонента Link и делать другие вещи всякий раз, когда мы меняем URL-адрес.

@ carlos-peru Вы всегда можете использовать собственный сервер, чтобы иметь полный контроль над маршрутизацией.

Спасибо @trandainhan и @kolpav! Я должен лучше понять, проведя некоторое время на выходных. Мне также удалось реализовать решение, основанное на HOC, которое хранит токен jwt в файле cookie, поэтому и сервер, и клиент могут использовать api.

@nmaro У меня есть Backend с аутентификацией JWT для веб-сайта в next.js и приложения (response-native).

Так. Я думаю, что в следующей модели: клиент получает токен Facebook (пользователь принимает логин facebook), а затем клиент отправляет бэкэнду токен для проверки и подтверждения токена пользователя (паспорт-facebook-токен в бэкэнде node-js) . Затем, если токен исправен, серверная часть отправляет клиенту JWT, сгенерированный в ней.

Эта проблема? Ваш инструмент может получить ключ от facebook и google? Я использую hello.js, но не совместим с next.js, и весь мир использует паспорт-facebook. Я считаю, что это большая ошибка, потому что сервер api должен быть совместим с веб-клиентом и мобильным приложением.

Спасибо

PS: Я не могу получить приглашение на вашем слабом канале.

@hmontes здесь - приглашение на канал Slack для проекта

@nmaro Я хочу помочь тебе с твоим проектом. У меня есть бэкэнд с Graphql с паспортом-facebok и паспортом-google-id-token !!!

Но. Вы знаете совместимый пакет для подключения клиента facebook / google в next.js?

На стороне клиента вы можете использовать аналогичный шаблон, который я использовал здесь: https://github.com/nmaro/staart/blob/master/packages/staart/src/components/login-facebook.js https: // github. com / nmaro / staart / blob / master / packages / staart / src / components / login-google.js
(вместо oothClient.authenticate просто отправьте почтовый запрос на ваш маршрут аутентификации).

@timneutkens Будет ли приветствоваться пиар с минимальным примером с ooth?

@nmaro в вашем примере. Почему вы используете componentDidMount вместо componentWillMount?

Я делаю компонент HoC (поставщик) для входа в Google.

Компонент componentWillMount вызывается только на клиенте? Тогда все должно быть хорошо.

Окей. Большое спасибо за ваши примеры. Наконец, я могу реализовать аутентификацию в социальных сетях.

Последний вопрос. Я создал access_token и refresh_token, и я хочу добавить localStorage для сохранения данных.

Где я могу поместить это в next.js, чтобы проверить логин при обновлении страницы? В приложении create-response-app я помещаю это в index.js

...
import { loginUser } from './actions'
...
let accessToken = localStorage.getItem('access_token')
let refreshToken = localStorage.getItem('refresh_token')

if (accessToken && refreshToken) store.dispatch(loginUser({accessToken, refreshToken}))

ReactDOM.render(
  <ApolloProvider store={store} client={client}>
....

Спасибо

С ooth я не храню токены facebook, я использую их только один раз с паспортом, а затем создаю обычный сеанс пользователя на основе файлов cookie.

Ах. Окей. Я использую JWT вместо сессий. Итак, я отправляю access_token на бэкэнд, он проверяет, действителен ли токен в службе (google, facebook), а затем, если пользователь существует в моем приложении, и отправляет мне обратно JWT.

Спасибо :)

Я должен добавить, что хранить JWT в локальном хранилище опасно (из-за XSS), лучше отправлять их как файлы cookie, если все они находятся на одном сервере / домене, поэтому клиентский код javascript не может получить JWT, но браузер отправит JWT автоматически как файлы cookie. JWT сложны (см. Длинные обсуждения выше), поэтому обычно я использую сеансы.

Я использую JWT только в том случае, если ooth используется в качестве микросервиса внешней аутентификации (потому что файлы cookie работают только в одном домене), и даже тогда я использую его ровно один раз, чтобы затем создать сеанс с сервером, поэтому он не хранится нигде на клиенте .

Для увеличения безопасности JWT вы можете использовать токен обновления (взято из oauth2).

Мобильное приложение может обрабатывать файлы cookie?

@nmaro Okey. Я понимаю тебя. Извините за мою ошибку.

У вас есть руководство или код для сохранения токена JWT в файл cookie? Я ищу https://github.com/zeit/next.js/blob/master/examples/with-firebase-authentication (моя система аутентификации с graphql)

Нет, извини, я этого не делал.

@hmontes @nmaro
Я написал это для себя, но может помочь:
https://github.com/malixsys/mobazoo

Привет, @malixsys, спасибо, что поделился. Что-то я не понимаю из вашего кода: похоже, вы бы настроили API_BASE_URL на что-то внешнее, правильно? Если вы аутентифицируетесь в этом API, я думаю, он запустит сеанс на основе файлов cookie, верно? Но тогда как передать этот файл cookie на сервер next.js, если сервер next.js находится в другом домене?

Ах нет, я вижу, вы устанавливаете / auth / signin в том же процессе. Хорошо, тогда это в основном тот же подход, который я использовал для ooth с next.js / staart.

На самом деле нет, я все еще в замешательстве: в / auth / signin вы возвращаете JWT, но вы никогда ничего не делаете с этим на стороне клиента. Позже вы «getUserFromCookie», но я не вижу, где вы установили cookie.

@nmaro Я просто хотел, чтобы изначально был базовый логин для работы с универсальным. Я установил на данный момент и jwt, и куки. Файл cookie используется для универсального использования. Мне еще предстоит использовать jwt в другом вызове axios в этом репо. Я использую частную вилку, где API_BASE_URL указывает на другой домен ...
Это все в моем списке TODO вместе с PWA.
Не стесняйтесь открывать вопрос или пинговать меня здесь ...

@nmaro cookie устанавливается в saveUser() здесь: https://github.com/malixsys/mobazoo/blob/master/utils/auth.js

Я знаю, что этот вопрос закрыт, но хочу показать свое решение.
https://next-auth.now.sh/
Я думаю, это немного похоже на сайт zeit.co

9 апреля

Я поработал над этим, но сейчас завален другими вещами 😥 Безусловно, это довольно высокий приоритет в моем списке приоритетов для Next.

22 сен # 2974

Мы планируем вскоре выпустить официальный пример аутентификации, не могли бы вы выпустить его как отдельный репозиторий? Thaaaanks!

@timneutkens

Привет, извините, что снова ошибаюсь, но не могли бы вы поделиться, как статус официального примера авторизации? Это определенно то, что я хотел бы увидеть, и, судя по количеству комментариев в этом выпуске, тоже. Итак, насколько высоко он стоит в списке приоритетов и стоит ли нам надеяться? 😄

Привет, @kolpav , они официально объявили об этом как о дорожной карте Next.js 5.

Наконец, мы добавляем некоторые очень востребованные примеры (например, аутентификацию пользователя), улучшенную документацию по внутреннему устройству Next.js, а также более мелкие функции и исправления.

https://zeit.co/blog/next-canary#the -roadmap

@babenzele Это отличные новости. Я, должно быть, пропустил это 😅

Где мы можем оставаться в курсе развития Next.js 5? В данный момент я интегрирую auth0, но было бы здорово иметь официальный путь / пример nextjs auth для подражания

Кажется, Next.js отчаянно нуждается в официальном примере локальной аутентификации с использованием JWT / cookies / localStorage (или их комбинации, если это безопасно и защищено от XSS / CSRF) ... Я потратил несколько недель, пытаясь придумать это, используя отдельный сервер Express API с локальной стратегией Passport.js. Я попробовал JWT в файле cookie / localStorage для запросов без сохранения состояния, а также попробовал обычный файл cookie с идентификатором сеанса из промежуточного программного обеспечения экспресс-сеанса, как только я окончательно отказался от JWT и безгражданства. Я столкнулся с проблемами, когда на сервере Express API создавались дополнительные сеансы из-за того, как работает экспресс-сеанс (пробовал с saveUninitialized: false). Я рассматривал возможность переноса кода Express в server.js в моем приложении Next.js, но я бы предпочел, чтобы это был отдельный сервер. Я также уверен, что моя реализация небезопасна от XSS / CSRF или перехвата файлов cookie. Нам нужен официальный пример, который охватывает лучшие практики для локальной аутентификации / входа в систему, или, возможно, официальный модуль как часть Next.js, который справится со всеми сложностями за нас!

Пока мы ждем Next.js 5.x и других примеров, вы можете взглянуть на https://nextjs-starter.now.sh, который все еще активно поддерживается и использует Next.js с Express, Express Sessions, CSRF ( CRSF Tokens), XSS (HTTP-файлы cookie только для токенов сеанса) и использует Passport JS для поддержки oAuth и электронной почты.

На самом деле я занимаюсь рефакторингом кода аутентификации в модуль, что упрощает добавление аутентификации в проекты Next.js простым в использовании способом. Модуль должен позволить вам легко использовать его с любой базой данных, которая вам нравится, без необходимости копировать кучу кода из примера Starter Project. Я надеюсь на этой неделе покончить с этим.

Для справки: метод «Double Submit Cookie» предоставляет простой способ добавить защиту CSRF, если вы ищете простой, но безопасный подход.

Проблема с JWT в localStorage заключается в том, что он всегда доступен для чтения из JavaScript на стороне клиента, поэтому вектор для перехвата сеанса через XSS, если у вас есть ненадежный контент (например, контент, отправленный пользователем или рекламные объявления).

Если вы используете файлы cookie только HTTP для токенов сеанса - или что-то вроде JWT со значением токена в HTTP Only (или зашифровываете весь JWT и расшифровываете его с помощью токена только HTTP на сервере) - тогда сеансы защищены настолько, насколько это возможно. быть. Идея заключается в том, что в идеале токены сеанса не должны читаться через клиентский JavaScript.

Конечно, многие сайты не используют файлы cookie только HTTP, потому что это проблема для одностраничных приложений и неизменно требует наличия некоторой логики аутентификации во внешнем интерфейсе, но это все еще идеальный подход.

Еще один вариант - https://github.com/nmaro/ooth, который активно поддерживается, уже поставляется в виде пакетов и используется в нескольких производственных приложениях.

@iaincollins Я скачал Next.js Starter Project и начал его просматривать. Мне нужно будет выделить важные части, связанные с безопасностью (XSS / CSRF), и попытаться интегрировать их в свое приложение или дождаться завершения отдельного модуля. Могу ли я где-нибудь следить за развитием этого модуля?

Привет @ kelleg1!

Отдельный модуль теперь опубликован как модуль next-auth, чтобы упростить его использование в других проектах. Он включает в себя пример проекта, который показывает, как его использовать.

Для справки : проект

Это все еще несколько сложно, поэтому, если у вас есть существующее приложение, вам может быть проще использовать его в качестве справки, но если да, я надеюсь, что это поможет

NB: CSRF в настоящее время все еще довольно тесно связан с ним. Он использует lusca, поэтому предполагается, что res.locals._csrf установлен, но разные библиотеки CSRF используют разные частные vars.

Я понимаю, что это все еще сложнее в использовании, чем кому-либо хотелось бы, но, по крайней мере, теперь код аутентификации наконец-то выделен в модуль, поэтому я могу начать рефакторинг. Я надеюсь со временем упростить его использование (с обработчиками по умолчанию для разных баз данных и более простой настройкой oAuth).

@iaincollins похоже, что единственная зависимость от next.js - это https://github.com/iaincollins/next-auth/blob/master/index.js#L342 ? Если это так, было бы здорово сделать библиотеку next.js-агностической.

@sedubois Полностью согласен!

Хотя это, вероятно, хорошее обсуждение проблем с репозиторием GitHub, а не здесь. 🙂 Если вы хотите предложить способы его улучшения, упрощения и придания большей универсальности, с удовольствием сотрудничаете.

(Иметь что-то максимально простое в использовании со следующим, насколько это возможно, по-прежнему является основной целью, но я не думаю, что это должно быть эксклюзивным, даже если оно заканчивается также со следующими конкретными сфокусированными параметрами.)

@timneutkens Поздравляю с выпуском nextjs 5. В ближайшем будущем мы увидим добавление официального примера локальной аутентификации в папку примеров? Или это что-то еще в разработке для более позднего выпуска?

Теперь у Ooth есть исчерпывающая документация, включая особенности аутентификации next.js.

@jaredpalmer Мне нравится ваш подход, я скопировал части вашей реализации. Однако безопасен ли метод getUser ? Что, если кто-то вручную изменит строку в локальном хранилище, будет ли разумно для приложения полагаться на это? Было бы разумнее превратить его в метод, который каждый раз декодирует публичную часть токена JWT и считывает оттуда состояние пользователя? Таким образом, мы можем больше доверять этому состоянию из-за природы вещей JWT. Каково твое мнение?

Вы должны сохранить его в файле cookie. Я писал, что до того, как в Next была поддержка кастомных серверов.

@jaredpalmer Можете рассказать мне об этом поподробнее? Должны ли мы хранить все в файлах cookie, а не в локальном хранилище? Ваш HOC тоже был бы другим? Означает ли это, что теперь мы можем использовать метод getInitialProps для рендеринга защищенных веб-сайтов на стороне сервера?

Просто предупредите, если речь идет о хранении конфиденциальных данных в локальном хранилище / веб-хранилище_:

«Никогда не храните конфиденциальные данные с помощью веб-хранилища: веб-хранилище не является безопасным хранилищем. Оно не« безопаснее », чем файлы cookie, потому что не передается по сети. Оно не зашифровано. Нет флага безопасности или только HTTP, поэтому это не место для хранения токенов сеанса или других токенов безопасности ".

Установите токен в cookie после входа в систему. используйте cookie-js. Затем используйте экспресс-анализатор файлов cookie на сервере, чтобы вы могли проверить получение req.headers.cookies.myToken или эквивалент. В getInitialProps hoc проверьте, существует ли req, затем возьмите токен из req.cookies, в противном случае получите его на клиенте из Cookies.get ('mytoken'). На этом этапе у вас будет доступ к вашему токену на клиенте и сервере. Затем вы хотите создать оболочку / экземпляр fetch / axios и объединить ее с следующим ctx в getInitialProps, чтобы все ваши страницы могли выполнять проверенные изоморфные запросы. Вы также можете захотеть просто подключить своего пользователя к сети. Так что не нужно повторять это везде. Затем вы можете сделать больше hocs, если вам нужно для общих сущностей, таких как withUser (withTeam (Page))

getUser - плохая идея, вам следует хранить только токен.

@jaredpalmer Я разработал почти такой же подход, и все работает нормально. Проблема, которую я сейчас пытаюсь решить, - как обновить токены. API, с которым я работаю, имеет относительно недолговечные токены (2 часа), и я пытаюсь разобраться в какой-то системе, чтобы пользователь находился в системе во время использования приложения.
У вас есть какие-нибудь мнения по этому поводу?

Вы также можете сохранять запрос при каждом переходе страницы, не передавая данные для вашего пользователя через next.js. Для этого вы должны прочитать токен из файла cookie в server.js и попытаться получить пользователя и передать его следующему обработчику запроса. В документе получите его из props и сохраните в JSON, например, в window.USER. Тогда в вашем hoc вы просто читаете это из окна, когда находитесь на земле клиента. Наконец, вы должны использовать axios с перехватчиком ответа, который немедленно стирает ваш cookie после получения кода 403 и перезагружает страницу.

@pbrandone Хотя и повторяющееся, но самое простое и предсказуемое решение - отправлять ваш токен с каждым запросом, получая только что обновленный токен в заголовке / теле ответа. Ваш клиент обновляет свое известное значение в каждом цикле запроса / ответа, эффективно предоставляя одноразовые токены, которые никогда не устаревают и не могут быть использованы неправильно.

Как правило, при таком подходе /token/refresh используется только для начального «пробуждения» приложения (подумайте о componentWillMount hook для <App/> ), проверяя, есть ли последний сохраненный токен по-прежнему действителен и может использоваться.

Никогда не приходилось иметь дело с токенами обновления, но я бы попытался сделать это в перехватчике axios. Придется подумать об этом больше, но теоретически вы могли бы перехватить неверный запрос, затем использовать токен обновления, чтобы получить новый токен, установить файл cookie с новым токеном и снова воспроизвести первый запрос с новым токеном. Перехватчики Axios действительно мощные, потому что они позволяют вам держать такие вещи абстрагированными и вне поля зрения вашего кода продукта. Возможно, вам потребуется написать перехватчик запросов и ответов и / или сохранить какой-то объект / карту с отслеживанием состояния, что может стать волосатым. Надеюсь, это поможет

Или сделай то, что сказал Люк. Намного легче.

Если вы хотите обновить токены, вам необходимо перехватить файл cookie (если вы используете файлы cookie) с помощью настраиваемой настройки сервера. Я сделал это с помощью экспресса. В противном случае вы можете сделать все это на стороне клиента, используя токены JWT. Поскольку я делал свое через файлы cookie, у меня было 2 варианта использования - обработка запросов навигации на стороне клиента и запросы страниц на стороне сервера (каждый раз, когда кто-то вручную вводит URL-адрес или обновляет страницу). Но в основном это один и тот же поток. просто выполняется по-разному, так как один на стороне клиента, а другой на стороне сервера. На стороне сервера, поскольку я использовал экспресс, я просто создал промежуточное ПО для обработки файла cookie, декодирования JWT внутри него и проверки срока действия. Если срок его действия истек, сделайте запрос на получение нового токена и продолжите передачу декодированного пользователя в redux на клиенте. На клиенте у меня есть оболочка HOC для безопасных ссылок, которая постоянно проверяет пользователя, каждый раз, когда вы перемещаетесь куда-то на клиенте. HOC получит ваш файл cookie или JWT (в моем случае мой JWT находится в файле cookie) и быстро проверит, действительно ли он по-прежнему действителен. Если он недействителен, он попытается получить обновленный JWT / Cookie и продолжить, иначе он выйдет из системы. Да, сложнее, но и надежнее. Надеюсь, это поможет! Не стесняйтесь задавать вопросы, если вам интересно - мне потребовалось некоторое время, чтобы во всем разобраться. Мне лично интересно, как люди входят в систему с помощью Github или FB и т. Д. - возможно, это нормально для многих случаев, но в некоторых случаях это просто не профессионально. Я еще не видел банк, медицинское учреждение, какие-либо оплачиваемые мной счета и т. Д. - войдите в мою учетную запись FB. Хотя это может быть лишь вопросом времени;)

Что касается обновления токенов доступа;
Я не уверен, что это правильный способ сделать это, но мои токены доступа очень недолговечные (около минуты). Чтобы справиться с проблемами истечения срока действия токена на стороне клиента, я использовал этот подход;
Каждый раз, когда страница загружается, она проверяет, не истек ли токен, если да, то обновляет его. Однако, если он все еще действителен, он вызывает метод, который проверяет, сколько времени до истечения срока, и устанавливает тайм-аут, который обновит токен доступа по истечении срока. А затем повторяет этот процесс, пока посетитель находится на сайте.

callRefreshAuthenticationLater(){
    clearTimeout(this.accessTokenRefreshmentTimeout)
    this.accessTokenRefreshmentTimeout = setTimeout(() => {
      this.refreshAuthentication()
    }, this.authService.howMuchUntilExpiration(this.authService.getToken('access_token')))
  }

Это возвращает миллисекунды до истечения срока:

howMuchUntilExpiration(token){
    return normalizeTimestamp(jwtDecode(token).exp) - Date.now()
  }

Любые отзывы об этом подходе будут очень приветствоваться.

@kunokdev - Думаю, это хорошее начало. Я переместил аналогичную функцию, мне нужно было вычислить оставшееся время, в функцию, которая просто вернула логическое значение в зависимости от того, истек срок его действия или нет, и это сделало мой код немного более читаемым и простым, плюс мне не нужно было беспокоиться о сбросе таймеров. Затем я просто проверяю, истек ли срок действия пользователя по любому запросу, требующему аутентификации. Если бы у меня был таймер обратного отсчета или что-то вроде того, что было видно клиенту или отладке, то ваша функция была бы идеальной. Это мои 2 цента 👍

Разве пример авторизации / входа не должен быть самым простым из возможных? т.е. хранилище памяти и никаких JWT или странных токенов. Обычно, когда вы думаете о ком-то, кто «вошел в систему», это означает, что у него есть куки / активная сессия.

У меня есть клиентская и серверная стороны HOC для входа в систему. Он использует JWT и бэкэнд (может быть node, django, что угодно).

Проверьте это, и мы будем благодарны за любые отзывы

https://github.com/hugotox/AppStarter

Соответствующий код здесь https://github.com/hugotox/AppStarter/blob/master/src/components/auth/login-required.js

@hugotox Мне нравится идея использовать хранилище Redux для хранения данных аутентификации, но когда вы вызываете store.dispatch из getInitialProps несколько раз, будьте готовы к нежелательным побочным эффектам, когда украшенный компонент будет отображаться, даже если процесс аутентификации еще не закончено. Возьмем пример использования из вашего кода:

export default withRedux(initStore, mapStateToProps)(
   loginRequired([PUBLIC])(MyPage)
)

В getInitialProps вы вызываете verifyToken перед verificationOk , поэтому MyPage.mapStateToProps будет вызываться 2 раза (если он прослушивает store.auth.user) и первый раз store.auth.user будет нулевым даже для страницы, на которой требуется авторизованный пользователь.

Вчера я также выпустил первую WIP-версию собственного стартового набора Next.js, но я начал с Docker, Flow и fast-redux: https://github.com/dogada/microchain

Я использую выделенные образы Docker для сервера API и веб-приложения, которые могут работать в разных доменах, поэтому я все еще ищу рабочее решение, которое позволит регистрировать реальных пользователей, а также, например, выпускать токены носителя OAuth для мобильных клиентов.

@spencersmb, ура! Это в значительной степени именно то, как я собирался это сделать. Что касается рендеринга на стороне сервера для URL-адреса при первом посещении (никогда не входил в систему), вы просто перехватываете его, видя, что нет cookie и, например, перенаправляете на страницу типа pages / login.js?

@ james-ff Спасибо, что опубликовали это, даже если это похоже на крик в пустоту.

В этой ветке было сказано несколько раз, но все, по-видимому, все еще игнорируют, что они не должны хранить токены сеанса в JWT с помощью localStorage или в файлах cookie, доступных для JavaScript, и, похоже, намереваются попытаться повторно изобрести сеансы и аутентификацию менее безопасным способом. (и для этого требуется JS на стороне клиента). 🙃🤦🏻‍♂️

Автор этого поста отлично описал, что с этим не так, в 2016 году, но, к сожалению, ситуация не улучшилась:

К сожалению, кажется, я нашел верхний предел длины статьи, прежде чем люди перестанут читать - многие комментаторы Reddit и Hacker News снова и снова предлагали одни и те же «решения», полностью игнорируя то, что они уже были рассмотрены и сочтены непрактичными. в самой статье.

Тем не менее, как исходная статья, так и следующая диаграмма довольно хороши.

JWT @iaincollins и приложения / аутентификация без сеанса имеют свои преимущества, и многие люди / компании (включая Google) все еще хотят их использовать. Прочитав дело против JWT , я все еще думаю, что должно быть два официальных примера / библиотеки next-auth, одна с использованием сеансов (например, next-auth), а другая с использованием JWT, или, возможно, только одна, которая позволяет использовать их. Преимущества, недостатки и предостережения должны быть четко объяснены для каждого из них на официальных страницах руководств по Next.js и / или в документации по модулям. Пока кто-нибудь не изобретет лучший шаблон / библиотеку, я, вероятно, буду продолжать использовать nextjs-starter и next-auth, поскольку они лучшее, что я нашел.

Я лишь случайно следил за этим, но мой опыт с универсальной аутентификацией JS заключается в том, чтобы хранить JWT в файле cookie HttpOnly . На самом деле нет причин использовать localStorage если у вас есть возможность использовать серверную часть приложения для хранения cookie. Если вам нужен доступ к JWT для вызовов API из разных источников в браузере, вы можете передать JWT в сценарии типа __INITIAL_STATE__ (помните об уязвимостях XSS, но, по крайней мере, вы не «храните» это на стороне клиента). Для доступа к одному и тому же источнику cookie будет передаваться туда и обратно (при условии, что вы используете withCredentials для axios или credentials: 'include' для выборки), что исключает необходимость помещать JWT в JS. . Вы можете использовать прокси-сервер на стороне сервера приложения, чтобы получить JWT из HttpOnly cookie, _ затем_ также сделать вызовы API между разными источниками. Вы также отменяете предполетный вызов в этом сценарии. Каждому свое, но я лично не думаю, что localStorage нужен для универсальных приложений.

@bjunc Да, из всех вариантов того, где должен храниться JWT (localStorage против HttpOnly cookie против Redux, или что у вас есть), я могу ошибаться, но я думаю, что ответ в основном всегда должен быть файлом cookie HttpOnly. Кажется, об этом говорилось бесчисленное количество раз в многочисленных блогах и форумах. (Я не уверен в токенах обновления - возможно, они также должны храниться в файле cookie HttpOnly?) Я бы подумал, что место хранения JWT должно быть решенным вопросом, который отличается от остальных преимуществ / недостатков JWT.

Я пытался сделать более или менее точно то, что вы сказали в моем проекте, прежде чем я начал применять nextjs-starter и next-auth. Прошло некоторое время, но если я правильно помню, я думаю, что проблема, с которой я столкнулся, заключалась в том, что мой сервер Express API, на котором я проходил аутентификацию (который использовал экспресс-сеанс), не инициализировал / не загружал сеанс правильно. Я бы получил странное поведение, когда сеансы инициализировались несколько раз. Я намерен продолжить делать более или менее то, что вы описали, если я смогу это исправить. Я также продолжу работать с nextjs-starter и next-auth, поскольку сеансы устраняют многие другие проблемы, которые ставят JWT, например, как сделать токены недействительными.

Опять же, официальный пример (или примеры) были бы полезны. Ключевое слово: официальный, то есть авторы Next.js учли все возможности и включили в него лучшие идеи и практики. В идеале использовать современные функции ES6 / ES7 / ES8, такие как обещания и async / await.

@ kelleg1 похоже, что ваши проблемы связаны со спецификой создания файлов cookie. Например, вы можете случайно создать несколько файлов cookie с одним и тем же именем, используя разные настройки ( HttpOnly , домен, путь, срок действия и т. Д.); которые могут вызвать странные побочные эффекты, подобные описываемым вами. Один из способов отладки - использовать инструменты разработчика Application-> Cookies. Если вы видите несколько файлов cookie с одинаковыми именами, это может указать вам правильное направление.

В любом случае, я не в основной команде, поэтому я не могу помочь с «официальным» вкладом (и на самом деле я использую Nuxt.js, а не Next.js), но принципы те же. Я экспериментировал с несколькими разными способами сделать это (взвешивая плюсы и минусы JWT, файлов cookie, CSFR, websocket CSWSH, localStorage и т. Д.). В конечном итоге я пришел к выводу, что универсальный характер Next / Nuxt хорошо подходит для использования файлов cookie HttpOnly JWT. Возможно, другие придут к другому выводу, но я лично не принадлежу к лагерю «о боже, не используйте JWT, разве вы не читали ту статью, в которой говорится, что JWT вызывает рак !?».

@iaincollins, извините, что возвращаю это снова, но каждый учебник в Интернете использует localStorage для сохранения токена, а вы говорите, что это небезопасно. Если да, то где мы должны хранить токен?

ПЕЧЕНЬЕ! : D
мы используем что-то вроде этого:

// auth.js
async signIn(res, info) {
    const { email, password } = info;
    const result = await this.service.signIn(email, password);
    if (!result) {
      res.status(HttpStatus.BAD_REQUEST).json({ success: false, message: 'Wrong email and/or password' });
      return;
    }
  const payload = {
    scope: 'appUser',
    userId: user.email,
    language: user.language,
    iat: moment().unix(),
    exp: moment().add(7, 'days').unix(),
    type: user.type,
    status: user.status
  };
    const token = jwt.sign(payload, ...);

    res.cookie('token', token, { domain: 'yourdomain.com', path: '/', secure: true, httpOnly: true, maxAge: 24 * 60 * 60 * 1000 * 7, signed: true });

    res.status(HttpStatus.OK).json({});
  }

// pages/index.js

class Index extends PureComponent {
  render() {
    return (
      <Layout title="home">
        <Home />
      </Layout>
    );
  }
}

Index.getInitialProps = async ({ req, res }) => {
  const auth = req ? getServerSideToken(req) : getClientSideToken();
  return { auth };
}
export default Index;

// utils/auth.js
const decode = ({ token }) => {
  const decoded = jwt.decode(token);
  const { userId, type = 'anonymous', status, language = 'en' } = decoded || {};
  return { user: { email: userId, type, status, language } };
};

export const getServerSideToken = (req) => {
  const { signedCookies } = req;

  if (!signedCookies) {
    return {};
  }
  try {
    return decode(signedCookies);
  } catch (parseError) {
    console.error(parseError, signedCookies);
    return {};
  }
};

export const getClientSideToken = () => {
  if (typeof window !== 'undefined') {
    const user = window.__USER__ || {};
    return { user };
  }
  return { user: {} };
};

// pages/_document.js
export default class extends Document {
  static async getInitialProps(ctx) {
    const props = await Document.getInitialProps(ctx);
    const info = getServerSideToken(ctx.req);
    return { ...props, ...info };
  }

  render() {
    const { user = {} } = this.props;
    const json = JSON.stringify(user);
    return (
      <html lang="en">
        <Head>
          <meta name="viewport" content="width=device-width, initial-scale=1" />
          <meta charSet="utf-8" />
          <link href="//fonts.googleapis.com/css?family=Kadwa:400,700|Work+Sans:100,300,600" rel="stylesheet" />
          <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.14/semantic.min.css" />
          <link rel="stylesheet" href="/static/site.css" />
        </Head>
        <body>
          <Main />
          <script
            dangerouslySetInnerHTML={{ __html: `__USER__ = ${json};` }}
          />
          <NextScript />
        </body>
      </html>
    );
  }
}

// somewhere.js
import axios from 'axios';
axios.defaults.withCredentials = true;
axios.post(`${apiBaseUrl}/some/path`, data)...

// somemiddleware.js
      const { signedCookies = {} } = req;
      const { token = '' } = signedCookies;
      if (token) {
        try {
          const info = jwt.verify(token, ...);
          req.user = await this.getUser(info.userId);
          if (req.user) {
            next();
            return;
          }
        } catch (err) {
          console.error({ permissionError: err });
          return res.status(500).json({...});
        }
      }
      return res.status(403).json({...});

Немного беспорядочно, но довольно безопасно, поскольку javascript на стороне клиента никогда не видит токен, а файл cookie - httpOnly, безопасен, подписан и автоматически отправляется axios или другим ...

Привет всем, только что создал пример аутентификации с файлами cookie и redux. См. Здесь https://github.com/zeit/next.js/pull/4011

Подождите, а где же эта проблема? Я так и не смог найти в официальном репо пример Express auth.

Я часто слышу разговоры о файлах cookie и сеансах, но как это работает для моего собственного мобильного приложения, когда ему нужно обратиться к API?

@jackjwilliams не должны ли HTTP-библиотеки мобильных приложений поддерживать файлы cookie?
https://stackoverflow.com/questions/1660927/iphone-make-post-request-handle-cookie
https://stackoverflow.com/questions/678630/how-do-i-make-an-http-request-using-cookies-on-android

Это можно сделать, но для этого нужен JWT: p

Я только что подробно прочитал всю эту ветку и чувствую себя обязанным подытожить свои выводы.

Похоже, есть два жизнеспособных стартовых проекта с выделенными твердыми модулями аутентификации:

Отличная работа @iaincollins и @nmaro !

Спасибо @curran :)

Я задокументировал все изменения кода, которые я сделал для устранения посторонних аспектов nextjs-starter, в этом запросе на слияние https://github.com/iaincollins/nextjs-starter/pull/86.

Возможно, это приближается к надежному примеру простой аутентификации для репозитория Next.js.

Недавно у меня возникла потребность реализовать OAuth в Office 365, поэтому я решил поделиться очень простым примером, который я здесь собрал. Он требует доработки (и далеко не так развит, как некоторые из приведенных выше примеров), но я думаю, что в конечном итоге его можно было бы обобщить для использования различных клиентов OAuth. Он использует несколько примеров, уже показанных в репозитории примеров Next.js, а также поток защищенных маршрутов здесь . В любом случае, просто подумал, что поделюсь, если кому-то понадобится быстрый пример того, как (возможно) сделать это с Microsoft.

Для всех, кто интересуется простой токенизированной аутентификацией JWT, которая работает на стороне клиента и сервера, я получил некоторую помощь в чате Spectrum и решил поделиться ею со всеми вами. Любые отзывы всегда приветствуются.

https://github.com/bgold0/nextjs-auth-skeleton

Привет, народ!
Вот еще один пример аутентификации с помощью next.js, который я построил пару месяцев назад, может быть, кому-то это пригодится:

https://github.com/alan2207/nextjs-jwt-authentication

Ooth 2.0 вышел с новыми и лучшими документами

Ooth - это система управления идентификацией пользователей , созданная для node.js (с учетом next.js).

Поддерживаемые в настоящее время стратегии:

  • Первичный: имя пользователя / пароль (включая подтверждение адреса электронной почты, забытый пароль и т. Д.), Гость, facebook, google
  • Вторичный: сеансы на основе файлов cookie, JWT

Посмотрите этот живой пример, который объединяет все вместе с next.js ( исходный код ).

Многие из примеров аутентификации здесь (и в папке примеров) возвращают пользовательский сеанс / токен / и т. Д. Из getInitialProps . Насколько я понимаю, когда страница отображается на стороне сервера, это означает, что информация о пользовательском сеансе будет отправлена ​​как часть ответа HTML-страницы (как часть NEXT_DATA ) в браузер.

Я вижу две проблемы безопасности с этим шаблоном, когда getInitialProps запускается на стороне сервера:
1) Пользовательский сеанс передается по сети от сервера к браузеру. Если в любых таких запросах используется http:// а не https:// , токен пользователя и т. Д. Будет доступен по сети в виде обычного текста.

2) Сеанс пользователя возвращается с сервера в браузер как часть HTML-страницы (в теге NEXT_DATA script). Размещение токена пользователя и т. Д. Непосредственно на HTML-странице кажется рискованным, особенно после анализа страницы, ее отображения в браузере и запуска других сторонних скриптов.

Эти вопросы уже решены? Есть ли какие-либо меры по устранению этих угроз?

Вот почему я использую файлы cookie. См. Мой пример здесь https://github.com/hugotox/nextjs-starter/blob/master/pages/_app.js

Наконец-то! Здесь вы можете найти полностью dockerized пример аутентификации next.js со следующими контейнерами:

  • next.js
  • микросервис авторизации (ooth)
  • api (graphql)
  • хранилище сеансов (Redis)
  • небольшой обратный прокси

Все собрано вместе с docker-compose.

Краткое примечание о JWT. JWT имеют преимущества в том, что они не имеют состояния, подходят для мобильных устройств и хороши, если вам нужно передавать учетные данные из одного домена в другой. Основным недостатком является то, что они открывают доступ к браузеру XSS. В этом примере я выбрал решение, основанное исключительно на файлах cookie. Мне все же удалось разделить вещи на микросервисы благодаря обратному прокси (все микросервисы работают в одном домене) и общему хранилищу сеансов.

https://github.com/nmaro/staart/tree/master/examples/staart
Живой пример, как обычно: https://staart.nmr.io

Я думаю, что было бы разумно уточнить, что использование JWT по сути не "подвергает вас воздействию XSS". Скорее, если ваш сайт имеет уязвимость XSS (не из-за самого JWT), JWT может быть скомпрометирован (вместе с любой другой доступной для скрипта информацией); тогда как файлы cookie httpOnly будут недоступны. Не важно, что вы можете использовать JWT в качестве значения httpOnly cookie!

Решения только для файлов cookie могут хорошо работать для связи в одном домене, но если у вас есть безголовый API (например, example.com вызывающий api.example.com ), то это не совсем решение, если вы не хотите использовать прокси-браузер. запросы от example.com к api.example.com , выполняя вызовы API к example.com и перенаправляя их с файлом cookie, добавленным к запросу (который имеет свой собственный набор плюсов и минусов) .

Я лично считаю, что минусы JWT сильно преувеличены, и их довольно легко уменьшить с помощью множества обычно реализуемых средств защиты. Не в последнюю очередь, черный список токенов, ссылающийся на требование jti (например, UUID версии 4) в случае, если токен был скомпрометирован до истечения срока его действия.

Привет, @bjunc, да, спасибо за разъяснения. Правильно, JWT сам по себе не предоставляет вам доступ к XSS. Что касается других ваших наблюдений, я хотел бы добавить, что у каждого из них есть свои подводные камни.

Не обращайте внимания на то, что вы можете использовать JWT в качестве значения файла cookie httpOnly!

Да, я просто хотел бы добавить, что это устраняет одно преимущество JWT по передаче учетных данных между доменами.

черный список токенов

Это и другая распространенная практика токена обновления устраняет другое преимущество JWT, заключающееся в том, что он действительно не имеет состояния.

Это мое понимание ситуации:

  • Если вам нужна аутентификация с перекрестным расположением, используйте JWT <- но, если возможно, избегайте из-за безопасности
  • Если вам нужно выполнить аутентификацию с любого клиента, который не является браузером и не имеет проблемы с XSS (например, настольное или мобильное приложение), используйте JWT
  • В противном случае используйте сеансы на основе файлов cookie, потому что решение на основе JWT будет либо менее безопасным (XSS), либо не будет действительно без сохранения состояния (черные списки), либо потребует от вас использования прокси-сервера, чтобы все равно хранить все в одном домене.

Я написал статью на Medium об аутентификации / учетных записях пользователей Next.js.
Это обширный учебник и мой мозговой удар после почти двух лет разработки и размышлений в свободное время (мой первый комментарий по этому вопросу сделан в феврале 2017 года).

https://medium.com/the-ideal-system/user-accounts-with-next-js-an-extensive-tutorial-6831cdaed16b

Вы продолжаете соотносить JWT с меньшей безопасностью. JWT не делает ваш сайт менее безопасным. Сама по себе эта уязвимость не является XSS-уязвимостью. Если у вас есть эксплойт XSS, у вас все равно будут проблемы. Даже с файлом cookie httpOnly злоумышленник не сможет прочитать значение файла cookie, но это не имеет значения, поскольку он может запускать произвольный код - например, запросы XHR, которые автоматически передают файлы cookie сеанса (по сути, действуя как CSRF-атака). Если ваш API является междоменным и вы выполняете запросы от браузера к серверу, то JWT все равно находится где-то в коде (будь то начальное состояние или localStorage). Если вы используете Axios, вы могли бы даже установить глобальные настройки по умолчанию, когда злоумышленнику нужно только сделать запрос Axios и даже не беспокоиться об авторизации.

Кроме того, вы не можете говорить о XSS, не говоря также о CSRF; где используются файлы cookie аутентификации. Даже при установке обратного прокси-сервера CSRF-атака позволит злоумышленнику выполнить аутентифицированные запросы к вашему API.

Кстати, только потому, что вы поместили JWT в файл cookie (например, зная, вошел ли пользователь в систему), это не означает, что вам запрещено использовать значение cookie (например, JWT) для междоменного сервера-to- Доступ к API-серверу при загрузке страницы (что также работает для сценария обратного прокси). Вам также не запрещается передавать JWT в исходном состоянии для запросов от браузера к серверу API. Они не исключают друг друга.

Кстати, я считаю, что идея JWT без сохранения состояния переоценена и ограничена в применении для большинства случаев использования. Например:

  • Ресурсные / динамические разрешения (например, не просто « can edit Post », а скорее « can edit Post:1634 »).
  • Что делать, если учетная запись пользователя была заблокирована / удалена?
  • Не оплатили ежемесячную подписку; какие функции дросселируются?
  • Внесен токен в черный список (как указано выше)?

Вы не запекаете все это в JWT, а это означает, что вам нужно погрузиться в уровень домена (то есть в базу данных), чтобы узнать. Вы только что загрузили ресурс, поэтому можете поместить его там, где остальная часть приложения сможет получить к нему доступ, и теперь у вас есть состояние. Я считаю действительно глупым думать, что все, что вам нужно знать о предмете, будет записано в жетон; в то же время сохраняя его в основном анонимным и легким. Не вдаваясь в подробности, есть аргумент в пользу запросов "без сохранения состояния" в межсервисном взаимодействии, но даже это я считаю непрактичным (по крайней мере, в том, что касается концепции запекания того, что вам нужно знать о предмете в JWT). .

Доступные стратегии аутентификации Ooth (новые выделены жирным шрифтом):

  • Локальный (имя пользователя / адрес электронной почты / пароль)
  • Facebook
  • Google
  • Гость
  • Патреон
  • Твиттер
  • Authy (Twilio) - без пароля через SMS

@jaredpalmer ты написал
Как и php, атомарной единицей Next является страница. Одна из самых крутых функций заключается в том, что он лениво загружает каждую страницу только по запросу. С аутентификацией только на стороне клиента, но с серверным рендерингом, js для этой защищенной страницы фактически загружается браузером. Надеемся, что в будущем, когда Next добавит серверные рабочие процессы, вы сможете заблокировать рендеринг и перенаправление на сервере, чтобы полностью предотвратить это. Для этого потребуются файлы cookie, сеансы и хранилища сеансов AFAIK, но это всего лишь стоимость создания подобных гибридных приложений.

Мы спустя 2 года. Есть ли рабочий процесс сервера для предотвращения загрузки js для защищенных страниц?
@timneutkens может
Как я могу полностью предотвратить доступ к защищенному контенту?

@lishine У вас есть ServerResponse в getInitialProps вашей страницы - вы можете легко перенаправить кого-то непривилегированного.

Есть ли пример аутентификации с редукцией?

Есть ли пример аутентификации с редукцией?

Вы можете попробовать этот пример, в котором используется redux, и проверить, работает ли он для вас ...
Вы можете найти его где-нибудь в этой теме, но если вы не можете его найти, вот он:
https://github.com/alan2207/nextjs-jwt-authentication

Я думаю, что это более сложная проблема при использовании результатов вызова API на стороне сервера getInitialProps, потому что Virtual DOM использует старые результаты после действия LOGOUT-LOGIN. Я думаю о решении

_EDITED_
и вот мой ответ с наблюдаемым сокращением

| Сторона | Auth | TODO |
| --- | --- | --- |
| Server | true | Получить начальные данные (с помощью прокси-сервера cookie).
| Сервер | false | Показывать страницу входа и получать данные после входа.
| Client | true | Получить исходные данные.
| Клиент | false | Показывать страницу входа и получать данные после входа. (Это происходит только по истечении срока сеанса при перемещении страницы на страницу) |

  static async getInitialProps({ store, query: { rowsPerPage, pageIndex }, req, auth }) {
    store.dispatch(TemporaryStoryActions.initPageState());

    const isAuthenticated = () => req ? req.isAuthenticated()
      : store.getState().auth.isAuthenticated;

    if (isAuthenticated()) {
      // fetch initial data
      const TemporaryStoryApiProxy = withCookieProxy(req, TemporaryStoryApi);
      await TemporaryStoryApiProxy.fetchTemporaryStories({
        rowsPerPage: rowsPerPage || 15,
        pageIndex: pageIndex || 0,                                                                                                                                  }).then(json => {
        store.dispatch(TemporaryStoryActions.loadTemporaryStories(
          json.rowsPerPage, json.pageIndex, json.count, json.rows));
      }).catch(error => {
        if (error.response && error.response.status === 403) {
          store.dispatch(AuthActions.initState(false, null));
          return;
        }
        throw error;
      });
    }

    if (!isAuthenticated()) {
      // => if client side fetch failed with 403, isAuthenticated() turns off to false
      // register logined action for client side login succeeded
      const reloadAction = TemporaryStoryActions.fetchTemporaryStories({
        rowsPerPage: rowsPerPage || 15,
        pageIndex: pageIndex || 0,
      });
      store.dispatch(AuthActions.addLoginedAction(reloadAction));
    }

    return {
      ...store.getState(),
    }                                                                                                                                                           }                                                                                             }
export const withLogin = Page => class SecurePage extends React.Component {
  static async getInitialProps (ctx) {
    if (ctx.req && ctx.store) {
      // server side
      const isAuthenticated = ctx.req.isAuthenticated();
      const { user } = ctx.req;
      ctx.store.dispatch(AuthActions.initState(isAuthenticated, user));
    }
    return Page.getInitialProps && await Page.getInitialProps(ctx)
  }

  render () {
    const { auth } = this.props;
    return auth.isAuthenticated ? <Page {...this.props} /> : <LoginPage />
  }                                                                                                                                                           }
// when [front-end server] => [api server]
// on Server Side Rendering,
// needs to proxy Cookies which sent to Next.js page request
export const withCookieProxy = (req, targetApi) => {
  if (!req) {
    return targetApi;
  }
  targetApi.client.interceptors.request.use(config => {
    const cookieString = Object.keys(req.cookies).map(key => `${key}=${req.cookies[key]}`).join('; ');
    const headers = {
      ...(config.headers || {}),
      Cookie: cookieString,
    };
    return {
      ...config,
      headers: headers,
    };
  }, error => {
    return Promise.reject(error);
  });
  return targetApi;
};
const loginEpic = (action$, state$) => action$.pipe(
  ofType(AuthActionTypes.login),
  mergeMap(action => {
    const email = action.payload.email;
    const password = action.payload.password;
    return from(AuthApi.login(email, password))
      .mergeMap(json => {
        const user = json.user;
        const loginedActions = state$.value.auth.loginedActions;
        const successActions = [
          AuthActions.removeAllLoginedActions(),
          ...loginedActions,
          AuthActions.loginSuccess(user.id, user.name, user.last_login_date),
        ];
        return from(successActions);
      }).pipe(catchError(error => {
        return of$(AuthActions.loginFail(error));
      }));
  }));

Кажется сложным, когда подойдет что-то простое, например:

export const withAuthSync = WrappedComponent => class extends Component {
  componentDidMount() {
    window.addEventListener('storage', this.syncLogout);
  }

  componentWillUnmount() {
    window.removeEventListener('storage', this.syncLogout);
    window.localStorage.removeItem('logout');
  }

    syncLogout = (event) => {
      if (event.key === 'logout') {
        console.log('logged out from storage!');
        window.location.reload();
      }
    };

    render() {
      return <WrappedComponent {...this.props} />;
    }
};

Этот пример был объединен как часть Next.js 8
https://github.com/zeit/next.js/tree/canary/examples/with-cookie-auth

@timneutkens спасибо за ссылку.

глядя на https://github.com/zeit/next.js/blob/canary/examples/with-cookie-auth/www/utils/auth.js#L26 -L34 ... не должно быть чек после вызова auth() ?

Тестирование примера без cookie приводит к вызову Profile.getInitialProps() , тогда как я думал, что перенаправление произойдет еще до попытки получить больше "начальных свойств" ...

Я сделал здесь пример с предварительным рендерингом на стороне сервера + аутентификацией с apollo.

https://github.com/HorizonShadow/apollo-next-now-test

Имейте в виду, что руководство по безопасности OWASP рекомендует не хранить токен JWT в локальном хранилище, т. Е. «Для кражи всех данных в этих объектах можно использовать один межсайтовый скриптинг, поэтому снова рекомендуется не хранить конфиденциальную информацию в локальном хранилище».

Вот Auth0: Где хранить токены и Том Эбботт: Где хранить ваши JWT - файлы cookie против веб-хранилища HTML5 .

Вот пример с прокси-сервером Nuxt.js + Express.js + бэкэндом Django. Если сервер Express используется для проксирования запроса аутентификации на фактический бэкэнд и обрабатывает защиту CSRF при использовании токена JWT, хранящегося в файле cookie (налагает некоторые ограничения на длину токена / сколько информации может храниться в токене JWT): https: / /github.com/danjac/nuxt-python-secure-example

@timneutkens Мне нужны документы о том, как отправить токен из cookie 🍪 в настраиваемое промежуточное ПО Redux SSR. Я получаю файлы cookie внутри _app.js. Но как передать это customApimiddleware. Где я написал запросы на выборку. Спасибо

Я написал статью на Medium об аутентификации / учетных записях пользователей Next.js.
Это обширный учебник и мой мозговой удар после почти двух лет разработки и размышлений в свободное время (мой первый комментарий по этому вопросу сделан в феврале 2017 года).

https://medium.com/the-ideal-system/user-accounts-with-next-js-an-extensive-tutorial-6831cdaed16b

Я думаю, что это один из лучших руководств по аутентификации в приложении nextj.js. Я видел такие вещи, как хранение токенов в localStorage (XSS), хранение токенов в файлах cookie (без обработки CSRF) и даже хранение токенов в файлах cookie из браузера (уязвимы как для XSS, так и для CSRF).

Мне очень нравится ваше решение с обратным прокси и обменом информацией о сеансе между разными службами. Я действительно не хотел бы создавать собственный сервер для приложения next.js, но я думаю, что это наиболее простой способ как для обработки сессий, так и для предотвращения csrf (и, возможно, добавления обратного прокси). Я могу даже создать монолитный проект (как для рендеринга приложения, так и для обработки операций с базами данных и т. Д.).

Я видел, что некоторые люди (включая ZEIT) сохраняют API без сохранения состояния и позволяют приложению next.js обрабатывать сеанс. Он передает токены API. Но проведение сессий только делает вещи немного более плотными и менее сложными.

Было бы действительно лучше иметь пример полной аутентификации для next.js. С такими вещами, как аутентификация для внешних API, сохранение сеанса в приложении next.js, совместное использование сеанса между службами или передача им токенов и, возможно, даже обновление токенов, если срок их действия истек. (Многие люди много пишут о JWT и просто используют их в своих руководствах, но в большинстве случаев они даже не устаревают или даже не обновляют их.)

В любом случае, вы написали одно из самых полных руководств по этой теме. Тогда спасибо!

Я очень надеюсь, что будет гораздо больше полных и понятных примеров и документации.

Было бы действительно лучше иметь пример полной аутентификации для next.js. С такими вещами, как аутентификация для внешних API, сохранение сеанса в приложении next.js, совместное использование сеанса между службами или передача им токенов и, возможно, даже обновление токенов, если срок их действия истек. (Многие люди много пишут о JWT и просто используют их в своих руководствах, но в большинстве случаев они даже не устаревают или даже не обновляют их.)

Я тоже не понимаю, какой подход выбрать.
Большое спасибо за ссылку на статью.
На какой из реализаций вы остановились в настоящее время?
Вы нашли исчерпывающий пример авторизации для следующей версии v9.3 +?

Стоит попробовать новый подход Auth0 на основе файлов cookie
(Конечно, это для конкретного поставщика удостоверений, но подход может быть полезным)
https://github.com/auth0/nextjs-auth0

  • Действительно здорово, что вы можете «проксировать» запросы api через маршруты api nextjs (даже через один динамический маршрут)
  • Тогда вам никогда не придется предоставлять токены доступа и т. Д. На стороне клиента (поскольку маршруты nextjs api выполняются только на стороне сервера), сторона сервера может получать токены доступа и т. Д. Через библиотеку auth0 и cookie с секретом
  • Ваш код на стороне клиента будет вызывать ваши маршруты nextjs api, а маршруты api затем будут выполнять настоящий запрос api

Имейте в виду, что в ReadMe говорится, что этот подход является «экспериментальным».

Стоит попробовать новый подход Auth0 на основе файлов cookie
(Конечно, это для конкретного поставщика удостоверений, но подход может быть полезным)
https://github.com/auth0/nextjs-auth0

  • Действительно здорово, что вы можете «проксировать» запросы api через маршруты api nextjs (даже через один динамический маршрут)
  • Тогда вам никогда не придется предоставлять токены доступа и т. Д. На стороне клиента (поскольку маршруты nextjs api выполняются только на стороне сервера), сторона сервера может получать токены доступа и т. Д. Через библиотеку auth0 и cookie с секретом
  • Ваш код на стороне клиента будет вызывать ваши маршруты nextjs api, а маршруты api затем будут выполнять настоящий запрос api

Имейте в виду, что в ReadMe говорится, что этот подход является «экспериментальным».

Эта статья очень полезна и охватывает множество различных архитектур.
https://auth0.com/blog/ultimate-guide-nextjs-authentication-auth0/

Я думаю, что использование маршрутов API в качестве прокси, вход / выход через маршруты API, получение токена из API, установка его как HttpOnly cookie - надежный подход.
Одной из проблем может быть CSRF, но вы можете легко создать какое-то решение с помощью пакета csrf npm (не csurf , но это тоже может сработать).

@onderonur , спасибо за статью auth0.
то есть на данный момент есть надежный или хотя бы минимальный пример реализации на чистом jwt с next.js?
Я не хочу делать расширенный слой с файлами cookie и их настройкой. В приложении csr мы просто сохранили токен в localstorage и отправили его вместе с запросом.

@onderonur , спасибо за статью auth0.
то есть на данный момент есть надежный или хотя бы минимальный пример реализации на чистом jwt с next.js?
Я не хочу делать расширенный слой с файлами cookie и их настройкой. В приложении csr мы просто сохранили токен в localstorage и отправили его вместе с запросом.

Я использовал этот метод для одного из своих репозиториев, но он все еще находится в стадии черновика, поэтому обязательно протестируйте их самостоятельно :)
https://github.com/onderonur/post-gallery
На самом деле «слой печенья» - вещь не продвинутая. Просто вызовите конечную точку входа в свой API через маршрут /api/login API и, если запрос будет успешным, установите токен в файле cookie httpOnly .
Вы можете проверить мое репо на ту же реализацию.

Еще один вариант (если вы хотите иметь почти тот же поток, что и установка токена в локальном хранилище), вы можете использовать пакет js-cookie npm, вызвать конечную точку входа в систему с запросом на стороне клиента, завершить, если он вернет токен, установите его в куки. И когда вы делаете запрос (через перехватчик axios и т. Д.), Прочтите значение cookie и передайте его своему API в качестве заголовка запроса. Я видел множество (и даже несколько популярных) приложений, использующих этот подход. Но это немного небезопасно. Потому что вы не можете установить файлы cookie httpOnly в браузере. Итак, JavaScript сможет прочитать ваш токен cookie. Таким образом, возникнет XSS-уязвимость.

Примите во внимание, что это старый поток (и в целом долгая тема), но для тех, кто ищет дополнительные ссылки или примеры, мы недавно начали работу над NextAuth.js v2. Я упоминаю об этом не столько как о плагине - это проект с открытым исходным кодом, и группа людей ему помогала, - но он очень прост в использовании, а код и подход могут быть полезны в качестве справочника для людей.

Для некоторого фона, например NextAuth v1, он использует файлы cookie с подписью, префиксом и только HTTP, избегая распространенных ошибок безопасности, связанных с использованием токенов на стороне клиента.

NextAuth.js v2 поддерживает вход через Apple, Google, Facebook, Twitter, GitHub, Auth0, Okta, Slack, Discord и других поставщиков OAuth (поддерживает как 1.x, так и 2.x). Вы можете использовать его с MySQL, MariaDB, Postgres, MongoDB или вообще без базы данных (только веб-токены OAuth и JSON для 100% бессерверного решения).

Использование очень простое, есть универсальный статический метод под названием session() и React Hook под названием useSession() вы можете использовать на стороне клиента компонентов:

import { useSession } from 'next-auth/client'

export default () => {
  const [session, loading] = useSession()

  return <>
    {!loading && session && <p>Signed in as {session.user.name || session.user.email}.</p>}
    {!loading && !session && <p><a href="/api/auth/signin">Sign in here</a></p>}
  </>
}

Он создан для Next.js 9.x и Serverless и не имеет таких зависимостей, как Express или PassportJS. Он включает поставщика аутентификации, который вы можете использовать в _app.js для автоматического добавления состояния аутентификации на все страницы; он работает как для клиентского, так и для серверного рендеринга.

Для получения дополнительной информации см. Next-auth.js.org или ознакомьтесь с next-auth @ beta на NPM.

Работа над ним все еще продолжается - мы все еще дорабатываем документацию и модель событий - с целевой датой выпуска ~ начало ~ середина июня.

Примите во внимание, что это старый поток (и в целом долгая тема), но для тех, кто ищет дополнительные ссылки или примеры, мы недавно начали работу над NextAuth.js v2. Я упоминаю об этом не столько как о плагине - это проект с открытым исходным кодом, и группа людей ему помогала, - но он очень прост в использовании, а код и подход могут быть полезны в качестве справочника для людей.

Для некоторого фона, например NextAuth v1, он использует файлы cookie с подписью, префиксом и только HTTP, избегая распространенных ошибок безопасности, связанных с использованием токенов на стороне клиента.

NextAuth.js v2 поддерживает вход через Apple, Google, Facebook, Twitter, GitHub, Auth0, Okta, Slack, Discord и других поставщиков OAuth (поддерживает как 1.x, так и 2.x). Вы можете использовать его с MySQL, MariaDB, Postgres, MongoDB или вообще без базы данных (только веб-токены OAuth и JSON для 100% бессерверного решения).

Использование очень простое, есть универсальный статический метод под названием session() и React Hook под названием useSession() вы можете использовать на стороне клиента компонентов:

import { useSession } from 'next-auth/client'

export default () => {
  const [session, loading] = useSession()

  return <>
    {!loading && session && <p>Signed in as {session.user.name || session.user.email}.</p>}
    {!loading && !session && <p><a href="/api/auth/signin">Sign in here</a></p>}
  </>
}

Он создан для Next.js 9.x и Serverless и не имеет таких зависимостей, как Express или PassportJS. Он включает поставщика аутентификации, который вы можете использовать в _app.js для автоматического добавления состояния аутентификации на все страницы; он работает как для клиентского, так и для серверного рендеринга.

Для получения дополнительной информации см. Next-auth.js.org или ознакомьтесь с next-auth @ beta на NPM.

Работа над ним все еще продолжается - мы все еще дорабатываем документацию и модель событий - с целевой датой выпуска ~ начало ~ середина июня.

Отличная работа!
Можно ли это использовать только на стороне клиента? Например, у меня есть приложение rails API, и я использую следующий JS для клиентской стороны.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги