Next.js: Adicionar exemplo de login / autenticação

Criado em 29 out. 2016  ·  208Comentários  ·  Fonte: vercel/next.js

Com:

  • auxiliar de autenticação reutilizável em todas as páginas
  • sincronização de sessão entre guias
  • back-end de e-mail simples sem senha hospedado em now.sh

Acho que isso será extremamente útil para muitos novatos.

p0

Comentários muito úteis

Então, eu tenho auth funcionando perfeitamente. Conforme mencionado em outro lugar, é apenas do lado do cliente, o que, em última análise, é apenas metade da batalha.

"Bastante seguro"

Como o php, a unidade atômica do Next é a página. Um dos recursos mais legais é que ele carrega lentamente cada página apenas quando solicitado. Com autenticação apenas do lado do cliente, mas com renderização do servidor, o js para aquela página protegida é de fato baixado pelo navegador. No futuro, quando o Next adicionar fluxos de trabalho do servidor, você poderá bloquear a renderização e redirecionar no servidor para evitar isso completamente. Isso exigirá cookies, sessões e armazenamentos de sessão AFAIK, mas esse é apenas o custo de fazer aplicativos híbridos como esses.

Exemplo de autenticação

Suponha que você tenha uma API protegida por JWT com dois pontos de extremidade de interesse: /token e /me . /token aceita credenciais de e-mail / senha e retorna um JWT assinado ( id_token ), enquanto /me retorna informações de perfil relacionadas ao usuário autenticado pelo JWT. Eu adaptei o seguinte AuthService.js do bloqueio do Auth0 (removendo o emissor de evento, embora essa não seja a pior ideia). Ele extrai quase todo o manuseio do token JWT para que possa ser usado na página de login e também em um componente de ordem superior (mais sobre isso posteriormente).

// 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())
  }
}

O próximo passo é um HOC para simplificar a proteção das páginas. Para evitar um flash indesejado de informações confidenciais, a página renderizará Loading... no servidor na primeira renderização enquanto o react inicializa / lê o token de localStorage. Isso significa que as páginas protegidas não farão o SEO, o que provavelmente está bom a partir de agora, mas definitivamente não é o ideal.

// 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) 

A página de login não pode usar o HOC como está agora, porque o login precisa ser público. Portanto, ele apenas cria uma instância de AuthService diretamente. Você faria algo semelhante para uma página de inscrição também.

// ./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

Inspirado pelos estilos de reação do Airbnb, também comecei a trabalhar em um next-with-auth lib, que seria uma função retorna um HOC para ser usado nas páginas. Também joguei com a fusão de AuthService e este HOC. Uma solução pode ser fazer este HOC aceitar uma função de nível de permissão como um argumento além do componente, como redux connect. Independentemente disso, em minha mente, você usaria next-with-auth assim:

// ./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,
})

Fazer tudo isso com o Redux parecia desnecessariamente complicado, mas basicamente você pode seguir o exemplo do wiki, mas mover AuthService para Actions (login e logout) e ter um redutor de usuário. Você só pode chamar essas ações no cliente, já que não há localStorage no servidor, então você precisa verificar isso em suas Ações. Em última análise, a loja redux é colocada em window qualquer maneira. Portanto, você poderia simplesmente armazenar em cache o usuário em window por conta própria em vez de usar o contexto. Se você não quiser redux, também pode experimentar react-broadcast .

Por último, supondo que next/server enviado de acordo com o item # 25. next-with-auth poderia abstrair o localStorage vs. cookie do desenvolvedor com middleware + um HOC. Ele também pode lidar com a atualização de tokens.

Todos 208 comentários

Sugestão: Use Redux e JWT para realizar o exemplo

Estou trabalhando em um exemplo para isso. Atualmente tendo problemas para fazer o componentWillReceiveProps disparar no meu componente de alto nível (onde estou planejando verificar se o usuário está autenticado e redirecionar para a página de login, caso não esteja)

Então, eu tenho auth funcionando perfeitamente. Conforme mencionado em outro lugar, é apenas do lado do cliente, o que, em última análise, é apenas metade da batalha.

"Bastante seguro"

Como o php, a unidade atômica do Next é a página. Um dos recursos mais legais é que ele carrega lentamente cada página apenas quando solicitado. Com autenticação apenas do lado do cliente, mas com renderização do servidor, o js para aquela página protegida é de fato baixado pelo navegador. No futuro, quando o Next adicionar fluxos de trabalho do servidor, você poderá bloquear a renderização e redirecionar no servidor para evitar isso completamente. Isso exigirá cookies, sessões e armazenamentos de sessão AFAIK, mas esse é apenas o custo de fazer aplicativos híbridos como esses.

Exemplo de autenticação

Suponha que você tenha uma API protegida por JWT com dois pontos de extremidade de interesse: /token e /me . /token aceita credenciais de e-mail / senha e retorna um JWT assinado ( id_token ), enquanto /me retorna informações de perfil relacionadas ao usuário autenticado pelo JWT. Eu adaptei o seguinte AuthService.js do bloqueio do Auth0 (removendo o emissor de evento, embora essa não seja a pior ideia). Ele extrai quase todo o manuseio do token JWT para que possa ser usado na página de login e também em um componente de ordem superior (mais sobre isso posteriormente).

// 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())
  }
}

O próximo passo é um HOC para simplificar a proteção das páginas. Para evitar um flash indesejado de informações confidenciais, a página renderizará Loading... no servidor na primeira renderização enquanto o react inicializa / lê o token de localStorage. Isso significa que as páginas protegidas não farão o SEO, o que provavelmente está bom a partir de agora, mas definitivamente não é o ideal.

// 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) 

A página de login não pode usar o HOC como está agora, porque o login precisa ser público. Portanto, ele apenas cria uma instância de AuthService diretamente. Você faria algo semelhante para uma página de inscrição também.

// ./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

Inspirado pelos estilos de reação do Airbnb, também comecei a trabalhar em um next-with-auth lib, que seria uma função retorna um HOC para ser usado nas páginas. Também joguei com a fusão de AuthService e este HOC. Uma solução pode ser fazer este HOC aceitar uma função de nível de permissão como um argumento além do componente, como redux connect. Independentemente disso, em minha mente, você usaria next-with-auth assim:

// ./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,
})

Fazer tudo isso com o Redux parecia desnecessariamente complicado, mas basicamente você pode seguir o exemplo do wiki, mas mover AuthService para Actions (login e logout) e ter um redutor de usuário. Você só pode chamar essas ações no cliente, já que não há localStorage no servidor, então você precisa verificar isso em suas Ações. Em última análise, a loja redux é colocada em window qualquer maneira. Portanto, você poderia simplesmente armazenar em cache o usuário em window por conta própria em vez de usar o contexto. Se você não quiser redux, também pode experimentar react-broadcast .

Por último, supondo que next/server enviado de acordo com o item # 25. next-with-auth poderia abstrair o localStorage vs. cookie do desenvolvedor com middleware + um HOC. Ele também pode lidar com a atualização de tokens.

Estou ansioso para experimentar! Obrigado pela implementação barebones :)

@jaredpalmer Estou trabalhando em algo semelhante. Como seu AuthService funciona quando um componente é renderizado no lado do servidor? O servidor precisaria acessar o JWT, mas não pode lê-lo do armazenamento local.

@amccloud Não. Esse é todo o problema. O HOC renderiza <div>Loading..</div> em rotas protegidas e deve ler o token e decidir se deve ou não redirecionar em componentDidMount . Para que funcione da maneira que você deseja e renderize do lado do servidor, o Next precisa de # 25, ou pelo menos a capacidade de definir um cookie com o valor do JWT AFAIK.

Eu usei o cookie-js para definir um cookie, mas é um pouco hack ..
a questão é: se você não enviar um cookie, você perderá todos os benefícios do nextjs e da renderização do lado do servidor em rotas autenticadas

@jaredpalmer isso é ótimo! obrigado pelo esforço. Vou tentar terminar de implementar seu exemplo (ou ajudá-lo a fazer isso se quiser) nos próximos dias

Ei! Publiquei um exemplo com nextjs e auth0 aqui: https://github.com/luisrudge/next.js-auth0
Tem o conceito de layout principal e também de “páginas seguras” que carregam apenas quando o usuário é autenticado.
Deixe-me saber o que você acha 🎉

@luisrudge incrível. Estou clonando e fazendo algumas alterações, mas parece ótimo

Legal! O que você acha que está faltando? Que mudanças você está pensando?

No domingo, 6 de novembro de 2016 às 13h12 -0200, "Dan Zajdband" < [email protected] [email protected] > escreveu:

@luisr udgehttps: //github.com/luisrudge incrível. Estou clonando e fazendo algumas alterações, mas parece ótimo

Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o em Gi tHubhttps: //github.com/zeit/next.js/issues/153#issuecomment -258687108 ou silencie o th readhttps: //github.com/notifications/unsubscribe-auth/ AA5cE8NIsvQ_ITjc1gArTFgNXzEda4TSks5q7e5NgaJpZM4KkJmi.

1) Usando standard para linting (portanto, é consistente com tudo o que estamos construindo a seguir)
2) Adicionando suporte multi-aba solicitado por @rauchg
3) A parte css pode ser simplificada

Eu vou te enviar um pr :)

O que você quer dizer com suporte a várias guias?

No domingo, 6 de novembro de 2016 às 13h16 -0200, "Dan Zajdband" < [email protected] [email protected] > escreveu:

1) Usar padrão para linting (portanto, é consistente com tudo o que estamos construindo a seguir)
2) Adicionando suporte a várias guias solicitado por @rauchghttps : //github.com/rauchg
3) A parte css pode ser simplificada

Eu vou te enviar um pr :)

Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o em Gi tHubhttps: //github.com/zeit/next.js/issues/153#issuecomment -258687373 ou silencie o th readhttps: //github.com/notifications/unsubscribe-auth/ AA5cE1A6jq4KZc9_ynukTCI4mU-rdsNaks5q7e81gaJpZM4KkJmi.

Você tem 2 guias abertas, logout em 1, efetua logout automaticamente na outra

Ahh. Isso é muito legal!

No domingo, 6 de novembro de 2016 às 13h21 -0200, "Dan Zajdband" < [email protected] [email protected] > escreveu:

Você tem 2 guias abertas, logout em 1, efetua logout automaticamente nas outras

Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o em Gi tHubhttps: //github.com/zeit/next.js/issues/153#issuecomment -258687707 ou silencie o th readhttps: //github.com/notifications/unsubscribe-auth/ AA5cE9e2DA4_GgNQIVTMp0hx74G-6RmUks5q7fBfgaJpZM4KkJmi.

Olá @luisrudge , enviei um PR com as alterações https://github.com/luisrudge/next.js-auth0/pull/2

muito obrigado por fazer isso <3

btw este é o resultado:

2016-11-06 11 14 31

@impronunciable @luisrudge Implementação fantástica! Se você quiser usá-lo sem o Auth0, parece que você só precisa alterar os arquivos no diretório ./utils, talvez apenas lock.js . Eu estarei experimentando isso em breve. A propósito, a multi-aba parece incrível 💯

@ugiacoman comecei a implementar um pequeno servidor com passwordless.net, deixe-me saber se você deseja obter meu código como ponto de partida

@impronunciable Isso seria incrível! Na verdade, eu ia fazer algo semelhante com o Digits do Twitter Fabric.

@impronuncível, eu sugiro não usar a senha less.net, em vez disso, você pode apenas usar o passaporte local e enviar aos usuários um link com seu e-mail e token na string de consulta.

Obrigado @impronunciable ❤️

@ugiacoman sim, é muito fácil remover a dependência auth0. Usei porque não queria ter uma API separada para lidar com autenticação

@jaredpalmer até onde eu sei, ter o número 25 seria ótimo, mas não está bloqueando? Quer dizer, temos acesso ao lado do servidor req em getInitialProps então nada impede a aplicação de cookie-parser nele? A autenticação do lado do servidor e o gerenciamento de sessão são coisas novas para mim 😬

BTW, considerando que localStorage não pode ser usado do lado do servidor, os cookies são a única maneira de ter sessões do lado do servidor? Tenho uma vaga lembrança de que pode não ser o mais seguro? Mas existe alguma outra opção?

@sedubois

A abordagem do cookie pode ser muito segura se feita de maneira adequada. Fazer o seguinte é bastante trivial:

  • usar sinalizador httpOnly (impede o acesso de JavaScript ao cookie)
  • usar sinalizador seguro (definir cookie apenas para solicitações https)
  • Cookies assinados (verifique a origem do cookie)

Também há uma vantagem de latência muito significativa quando você pode acessar as informações de autenticação diretamente no servidor.

Devemos mover este exemplo para examples/ Verei o que posso fazer

Consegui usar react-cookie para cookies isomórficos envolvendo nextjs em um servidor expresso personalizado da seguinte maneira:

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')
  })
})

Isso me permite fazer uma solicitação autenticada do lado do servidor. Isso não resolve nenhum dos problemas originais, mas resolveu o problema de compartilhamento de estado entre cliente e servidor.

Do ponto de vista de quem está aprendendo muito dessas coisas. Acho que seria melhor se os exemplos não dependessem de serviços de terceiros como o auth0. Seria mais benéfico para os recém-chegados ver um exemplo mais básico com formulários de login / inscrição e usando Redux e JWT.

O exemplo que planejamos _bundle_ será baseado em APIs de servidor Node.js de código aberto

Eu adicionei um exemplo de autenticação baseada em e-mail para um projeto inicial de exemplo em https://github.com/iaincollins/nextjs-starter

Tem suporte de sessão (com Express Sessions no backend e a API sessionStorage do navegador para armazená-los em cache no front end), cookies httpOnly, projeção CSRF, usa SMTP embutido para enviar e-mails, um fácil de alterar o backend que é padronizado para SQL Lite . Nenhuma configuração é necessária para executá-lo.

O projeto também possui páginas de layout, rotas personalizadas e inclui o exemplo de relógio do wiki. Não é o exemplo mais extravagante de autenticação, mas pode ser útil para quem procura uma introdução fácil com um projeto simples que seja fácil de entender e brincar.

Eu concordo com @iamjacks que um exemplo com JWT parece uma boa ideia.

Estou feliz em melhorar o tratamento de erros e adicionar recursos como uma página de perfil simples que os usuários podem editar e integração de passaporte com exemplos para oAuth para Facebook, Google e Twitter, se isso for útil para as pessoas. Se as pessoas tiverem boas idéias sobre as melhores maneiras de propagar / expor informações da sessão para componentes, estou muito interessado.

Example screenshot showing what to expect

Isso é incrível @iaincollins. Vamos apresentar isso nas notas de lançamento para 2.0 com certeza :)

@rauchg Obrigado! :)

Acho que devo acrescentar explicitamente que, neste exemplo, as sessões são baseadas em cliente e servidor - ou seja, funcionam com e sem JavaScript (e em sistemas sem sessionStorage), e a mesma sessão é compartilhada por ambos.

Conseguir isso fica um pouco complicado no componente Sessão, que investiga variáveis ​​com nomes como req.connection._httpMessage.locals._csrf para obter o token CSRF dos cabeçalhos do servidor - como o objeto 'req' passado para páginas em getInitialProps () é curiosamente um pouco diferente do objeto req exposto no Express, pois eu normalmente o acessaria por meio de req.locals._csrf (no entanto, req.session é o mesmo em ambos).

localStorage não é seguro. qual é a melhor maneira de torná-lo mais seguro. qualquer um pode roubar dados localStorage e colocá-los novamente em seu navegador e pode ser registrado como o usuário vítima !!

@Chathula isso não é verdade. Este argumento está errado.
Se alguém tiver acesso ao navegador fisicamente, pode fazer qualquer coisa.

Isso também é válido para cookies.

Usar localStorage para autenticação é meio seguro porque podemos nos livrar de problemas de segurança baseados em cookies.
Mas, por outro lado, afeta o SSR.

@arunoda eu criei um login com Laravel API e Next.js Client. eu armazeno authUser access_token dentro de localStorage. em seguida, verifique o usuário conectado ou não autenticando. mas não é seguro. se alguém roubou os dados localStorage. ele / ela pode usá-lo.

se alguém roubou os dados localStorage.

Como? Basicamente, ele deve ter acesso ao navegador fisicamente. Então essa pessoa pode fazer qualquer coisa.
Portanto, não devemos nos preocupar com isso.

não podemos usar qualquer criptografia para torná-lo muito mais seguro?

@Chathula, isso está saindo do assunto. Não é exatamente relevante para Next.js e queremos ver como as coisas da web normalmente funcionam.

@Chathula pode ser que você possa iniciar um novo tópico no projeto inicial acima.

@arunoda hahha !! Obrigado pela informação! : D

@Chathula Fico feliz em discutir isso mais em uma edição do projeto inicial se você tiver preocupações específicas.

Gostaria de corrigir qualquer equívoco, para evitar que as pessoas fiquem desnecessariamente alarmadas.

A API de armazenamento da Web (ou seja, localStorage e sessionStorage) é - como cookies sem httpOnly definido - restrita pela mesma política de origem (protocolo, nome de host, número de porta), não é verdade que "qualquer um pode roubar [it]"; mas sim, se alguém for capaz de executar JavaScript arbitrário em seu site por meio de uma vulnerabilidade de script entre sites em seu aplicativo, ele também poderá acessar o armazenamento, portanto, você não deve armazenar identificadores de sessão nele.

É por isso que você verá que o token de sessão em si não é armazenado em localStorage / sessionStorage e não é legível em JavaScript, ele é transmitido apenas por um cookie HTTP Only (é por isso que a classe de sessão usa XMLHttpRequest () em vez de fetch () - conforme explicado na documentação da classe).

Isso significa que mesmo se alguém for capaz de explorar uma vulnerabilidade de script entre sites em seu aplicativo e executar JavaScript arbitrário em seu aplicativo da web, ainda não será possível ler ou exportar um token de sessão de usuário.

Esta é talvez uma distinção importante que vale a pena trabalhar na documentação.

Observação: a criptografia adicional de dados do usuário não é útil aqui porque o aplicativo sempre precisa ser capaz de ler os dados para que possam ser renderizados (então você também precisa armazenar uma chave de descrição no aplicativo, o que renderizaria a criptografia do dados do usuário bastante discutíveis).

_UPDATE: Na última semana ou duas, o exemplo foi refatorado para usar localStorage em vez de sessionStorage, já que sessionStorage não é compartilhado entre guias e o compartilhamento de dados não sensíveis desta forma reduz o número de verificações de autenticação desnecessárias e mantém o status da sessão consistente entre as guias._

Talvez seja útil para algumas pessoas que fiz este aplicativo de amostra enquanto experimentava:

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

Apoiado por este back-end de brinquedo:

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

Implantado aqui:

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

Backend aqui:

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

@possabilities Obrigado Mike! Além de expor um microsserviço separado que mostra uma boa maneira de lidar com páginas seguras que eu estava pensando que pode ser um bom pragma e pode me inspirar. Tenho algumas ideias que surgirão no repositório next.js-with-auth.

Depois de pensar um pouco mais, provavelmente não consideraria meus esforços um grande exemplo. Eu provavelmente mudaria para ter a inscrição / login totalmente submetida no estilo web 1.0 para que possamos estabelecer um cookie somente HTTP no servidor (eliminando a acessibilidade ao JWT via XSS) e, em seguida, anexar o objeto de usuário a req vez do que o token inteiro. Isso deixa a possibilidade de vulnerabilidades CSRF, mas acho que é mais simples de mitigar do que XSS (não?). Um bom efeito colateral disso é que o aplicativo cliente pode usar um fluxo quase idêntico para ao fazer login por meio de um serviço de juramento.

Também vejo, em retrospectiva, que poderia evitar o "middleware" (e, portanto, a necessidade de um servidor personalizado) analisando o cookie no Page HoC's getInitialProps .

@possibilidades Uma vez que a página 'secreta' é apenas uma página e é agrupada por webpack, ela não seria exibida para o navegador se eu fosse para / secret?

Sim, suponho que deve ser conectado para fazer um redirecionamento do lado do servidor.

Aliás, eu me distraí de volta ao meu projeto inicial de entrar no github. O fluxo é semelhante com mais cuidado com a segurança (ou seja, não expondo nenhum segredo no cliente para evitar que o token oauth seja exposto via XSS). Ele está vinculado a um aplicativo adequado, mas se houver algum interesse, posso dividi-lo em algo que possa ser útil para o fluxo de oauth em geral.

@ possibilidades Eu acho que seria uma ajuda incrível se pudesse haver um PR com um exemplo mínimo (mas adequado) de with-auth 🙂 Estou trabalhando em auth em meu aplicativo (https://github.com/relatenow/ relacionar), mas atualmente é apenas do lado do cliente (localStorage).

@sedubois Eu tenho um pr para isso usando passwordless.net https://github.com/zeit/next.js/pull/646, mas vamos mover o servidor de autenticação para outro lugar

que tal usar o Graphql?

Apollo dá um exemplo de autenticação Graphql:

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

Aqui estamos autenticando uma solicitação graphql, mas ela poderia ser adaptada para o nosso caso.

Além disso, o graphql pode abstrair a implementação e a lógica. Ele pode ser usado sem senha, auth0 ou qualquer outra coisa que possamos preferir.

@impronunciable FYI com seu exemplo, ainda não sei como lidar com as sessões, pois quero me livrar do sem senha. Vou tentar adaptar o exemplo de @iaincollins em meu aplicativo.

Meus requisitos são:

  • seguro
  • autenticar o lado do servidor e depois o lado do cliente
  • suporta Facebook e login / senha
  • os provedores de autenticação devem ser facilmente alterados
  • criar usuário no Graphcool
  • autenticar solicitações GraphQL subsequentes para Graphcool

Caso ajude alguém aqui está meu aplicativo com autenticação do lado do servidor 🙂

A autenticação deve ser separada do servidor Next.js, mas estou esperando que alguém dê inspiração sobre isso ... Também não tenho certeza se está devidamente protegido contra CSRF.

Isso é o que eu fiz:

  • quando o usuário faz login, o javascript do lado do cliente define um cookie no navegador que contém o token do portador de autenticação
  • ao fazer solicitações autenticadas do lado do servidor (next.js), o código lê o token do portador nos cabeçalhos da solicitação do navegador e o usa para entrar em contato com o servidor API em nome do cliente

Isso é vulnerável a XSS (mesmo que reaja muito para evitá-lo) e ataques CSRF, mas é simples e funciona com SSR.

Apenas para adicionar aos pedidos

graph.cool + apollo + jwt + auth0 + next.js, as primeiras 4 partes já foram feitas em https://github.com/graphcool-examples/react-apollo-auth0-example

@balupton em seu exemplo, o que acontece quando o token expira enquanto o usuário ainda está conectado, a sessão acabou ou é renovada de alguma forma?

@nmaro não sei, não foi escrito por mim - melhor perguntar aí

Para meu próprio aplicativo, tenho auth em next.js e auth0 e, em seguida, com um servidor de API zeit / micro que verifica os tokens do portador.

Deve ser capaz de abrir o código em algum momento de fevereiro.

Seguindo a mesma filosofia que next.js tem seguido (faça uma coisa e faça bem), desenvolvi o esqueleto de um sistema de contas de usuário extensível para o nó. Veja a filosofia aqui: https://medium.com/the-ideal-system/ooth-user-accounts-for-node-js-93cfcd28ed1a#.97kyfg4xg

O projeto github está aqui: https://github.com/nmaro/ooth/

O objetivo é ter uma autenticação extensível e independente + serviço de gerenciamento de usuários ideal para ser usado como um microsserviço separado, uma estrutura de aplicativo que funcionaria muito bem com node.js.

Um exemplo com autenticação de e-mail + senha está aqui: https://github.com/nmaro/ooth/tree/master/examples/ooth
Já existem pacotes para autenticação do Facebook e Google (ooth-facebook e ooth-google), que devem ser fáceis de implementar com base no passport-facebook e no passport-google, respectivamente.

Estarei postando um exemplo de integração next.js o mais rápido possível. Sinta-se à vontade para participar da discussão e contribuir.

Desculpe pelo plug, mas é para o bem maior - eu realmente acredito que ainda não existe uma boa solução para o nó, e este é o público certo de pessoas que podem querer tal coisa. Paz

Enquanto isso ... aqui está um exemplo de API graphql que requer autenticação com um JWT-Token apenas para operações de gravação. Sinta-se à vontade para usá-lo com seu método de autenticação favorito :)

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

Atualizei https://nextjs-starter.now.sh para adicionar suporte oAuth.

screen shot 2017-02-10 at 05 03 19

  • Ele usa o Passport for oAuth, junto com sessões expressas (como antes).
  • Há suporte para Facebook, Google e Twitter + oAuth e é fácil adicionar mais (consulte AUTHENTICATION.md e routes / auth-passport.js ).
  • Ele usa o sistema de sessão cliente / servidor universal (com tokens CSRF, proteção XSS via cookies somente HTTP, a camada ORM que suporta Mongo, SQL DBs, Redshift, etc.) como o login de e-mail usa.
  • Pode relatar que a experiência de configuração do oAuth em portais de desenvolvedor é tão terrível como sempre (erros estranhos acontecem e é difícil de depurar).

A natureza do oAuth - db + sessões + passaporte e tratamento de erros precisando trabalhar juntos - e das sessões que precisam funcionar tanto no cliente quanto no servidor e como isso funciona com a renderização universal - significa um pouco demais para tentar resolver de uma vez se você está tentando descobrir o que está acontecendo, mas a lógica do cliente não tem nenhuma configuração específica do oAuth, portanto, não é muito confusa.

Eu ficaria feliz em dividir apenas a autenticação em um exemplo separado, embora eu suspeite que não seja muito menor. Se alguém mais quiser, ótimo. Provavelmente adicionarei um pouco mais ao exemplo em algum momento (como uma página de gerenciamento de conta). Provavelmente, vincular mais à documentação seria bom.

Ótimo trabalho! Se você pudesse dividir a autenticação e adicioná-la ao repositório next.js, seria incrível: coração:

Eu também consigo fazer isso 👍🏻

Não terei tempo nas próximas 2 semanas ou mais, então, se alguém quiser, seria ótimo.

Eu adoraria refatorá-lo e ver se ele poderia ser reduzido a apenas um módulo (que expõe componentes simples como botões de login e formulários de login para incorporar) e me inspirar no exemplo realmente bom do lado do cliente anterior por @impronunciable.

Atualização: Na verdade, estou indo embora, e é por isso que não posso, mas quando volto fico feliz em ver isso!

Eu segui o guia de início rápido auth0 / react com algumas modificações, mas quando ligo para lock.show() , o aplicativo reclama:

Erro não detectado: addComponentAsRefTo (...): Apenas um ReactOwner pode ter refs. Você pode estar adicionando um ref a um componente que não foi criado dentro do método render um componente, ou você tem várias cópias do React carregadas

@iaincollins @timneutkens em relação ao seu exemplo, corrija-me se eu estiver errado.

O exemplo usa um cookie httpOnly para armazenar um token de sessão, tornando-o seguro contra ataques de injeção de javascript (XSS). Em seguida, ele tem o problema de ter um token csrf armazenado no armazenamento local para também proteger contra ataques CSRF.

Existe uma suposição subjacente de que essa combinação de técnicas torna as coisas seguras, o que pode enganar o usuário / desenvolvedor. Um invasor ainda pode injetar javascript na página (XSS), ler o token csrf e usá-lo para executar solicitações autenticadas (cookie) para a apis. Vale a pena mencionar no readme?

Oi @davibe

Infelizmente, não há atualmente uma abordagem tecnicamente melhor do que um cookie de sessão e um token CSRF rotativo separado, então acho que devo dizer que estou muito feliz com o modelo, pois não há realmente outra maneira de fazê-lo (mesmo que a implementação real sempre possa ser melhorada).

Poderíamos adicionar a impressão digital do navegador, talvez o token de sessão pudesse girar com mais frequência (esqueci se isso é automático agora), o token CSRF poderia ser um cabeçalho em vez de param, os cookies deveriam ser SSL apenas em produção e poderíamos adicionar uma data de expiração para os tokens de login, mas AFAICS há um escopo bastante limitado para melhorias no que diz respeito ao modelo de segurança e nada que realmente conceda proteção adicional no caso de alguém ser capaz de injetar qualquer código que desejar em um aplicativo; mas sinta-se à vontade para levantar essas coisas como problemas para melhorias no repo.

Se houvesse alguma opção de modificação de status da conta (que não existe atualmente), poderíamos ter um CAPTCHA antes de executá-lo para tornar mais difícil fazer alterações no servidor sem permissão, mas tudo o que acontece no exemplo é que os usuários podem fazer login e fora de forma que atualmente está fora do escopo.

O armazenamento local de tokens de sessão o tornaria notavelmente menos seguro e iria contra o uso pretendido na especificação, então, pessoalmente, não sou a favor disso - embora eu aprecie que simplificaria as coisas, já que não ter projeção CSRF, mas provavelmente as pessoas não preciso de um exemplo disso, pois seria muito simples de fazer - só tentei fornecer um porque é muito estranho. :-)

Estou totalmente com você no sentido de que também odeio como isso é necessariamente estranho e não desisti de tentar transformá-lo em um módulo.

Hmm, eu entendo.
Esta edição foi muito útil para mim, obrigado.

Este é o ponto de vista de Auth0 https://auth0.com/blog/cookies-vs-tokens-definitive-guide/
Basicamente, eles sugerem evitar cookies, mas isso deixaria de fora o SSR no carregamento da primeira página, eu acho.

Ei pessoal!

Também estou trabalhando na autenticação com next.js e os comentários desse problema, em particular @davibe + o repo de @iaincollins e o PR de @timneutkens foram de grande ajuda.

Minha solução faz o seguinte:

  • Após o login bem-sucedido, meu redutor redux salva o token e os dados do usuário em um cookie usando react-cookie .
  • Qualquer página / componente que precisa dessa informação é empacotado em um componente de ordem superior semelhante a @timneutkens with-session.js . Se for SSR, o token estará disponível em ctx.req.headers.cookie, caso contrário, apenas pegue-o no documento do navegador (use o método de carregamento de cookie react).
  • Assim que tiver o token, posso defini-lo nos cabeçalhos do portador / autorização sempre que fizer uma solicitação.

Ajuda o fato de eu ter meu próprio microsserviço de autenticação de token JWT em execução em um contêiner do docker.
Talvez seja mais fácil fornecer um serviço JWT simples como parte do exemplo with-auth? Assim, evitando hackear o server.js e potencialmente perdendo os benefícios do next.js embutido no hot recarregamento, ssr e roteamento?

Também não tenho certeza se essa abordagem é segura em termos de CSRF / XSS. Quaisquer comentários são bem-vindos.

Obrigado a todos pelo incrível trabalho que fizeram até agora. Sou um grande fã desse projeto!

@jcsmesquita Estou trabalhando em uma nova versão do exemplo com tudo separado de Next.js semelhante a como o auth é implementado em zeit.co.

@subsumo Obrigado pela menção, FYI: NAP é meu, a autenticação via web é baseada em nextjs-starter e reage ao login do cliente nativo com token.

@timneutkens a solução na qual você está trabalhando permite que você se autentique em um serviço separado?

Estive olhando para o exemplo de solicitação de pull de autenticação atual https://github.com/zeit/next.js/pull/1141
Parece-me que só permite a autenticação no servidor next.js, mas não isomorficamente em um serviço separado.

Em outras palavras, suponha que você deseja separar o servidor next.js e a API do aplicativo real (por exemplo, REST ou GraphQL), então o que você deseja fazer é que o cliente e o servidor sejam capazes de se autenticar na API. Não acho que essa solução realmente o ajude nisso.

Eu pensei em um fluxo.

Entidades:

  • Cliente (C)
  • Servidor Next.js (S)
  • API (A)

O objetivo é estabelecer 3 sessões baseadas em cookies:

  1. CS
  2. CA
  3. SA

A Sessão 1) é para que o Cliente reconheça o Servidor e 2) 3) é para que o Cliente e o Servidor possam usar suas respectivas sessões para acessar a API de forma independente.

Este é o fluxo:

  1. CS é feito facilmente, sem necessidade de autenticação
  2. O CA é feito com autenticação (por exemplo, com nome de usuário / senha). Além disso, um JWT é fornecido
  3. O cliente fornece JWT ao servidor e, em seguida, descarta-o
  4. SA é feito com JWT, que é então descartado

Qual a sua opinião? Existe uma maneira mais fácil? Devemos, em vez disso, apenas manter a API acoplada ao servidor next.js, de modo que apenas uma sessão seja necessária (CS)?

@rauchg Não convoco você facilmente, mas acredito que também se trate de que direção o next.js deve seguir - como um "front-end universal", ele deve ser executado separadamente da API que fornece os dados? Se sim, precisamos fazer isso direito.

@timneutkens a solução na qual você está trabalhando permite que você se autentique em um serviço separado?

Essa é a ideia, sim. Estive ocupado consertando outras coisas no Next.js. Voltaremos a ele o mais rápido possível.

Eu dividi partes específicas do github-auth do meu aplicativo em um exemplo bem digerível. pode ser interessante para alguns e adoraria qualquer feedback sobre o código ou fluxo: https://github.com/possabilities/next-github-auth-example

ATUALIZAÇÃO: Refatorei o aplicativo de exemplo em um conjunto reutilizável de componentes que podem ser "colocados em" próximos aplicativos

Follow-up: Eu escrevi uma integração com Ooth e uma API GraphQL.

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

é baseado em abordagens existentes, ou seja, autenticação para o servidor next.js, ou seja, assume que a API e o servidor de autenticação rodam todos no mesmo processo, então apenas uma sessão precisa ser criada. A vantagem: em princípio, ele pode armazenar credenciais para / é extensível a praticamente qualquer estratégia passport.js.

@timneutkens algum progresso nessa frente?

Refatorei meu aplicativo de exemplo do github auth em um conjunto reutilizável de decoradores e componentes de página para “soltar o github auth nos” próximos aplicativos. Comentários sobre código e funcionalidade são bem-vindos. https://github.com/possabilities/next-github-auth

_Acho_ que poderia ser interessante definir as placas aqui e depois continuar a evoluir para uma estrutura de autenticação mais genérica para a próxima.

@timneutkens
Desculpe por enviar um ping, mas você fez algum progresso nisso? Estou completamente perdido em como devo configurar a autenticação com next.js corretamente.

@kolpav Eu trabalhei nisso, atualmente atolado com outras coisas 😥 De qualquer forma, isso está no topo da minha lista de prioridades para o Próximo 😄

Uma maneira clara, bem documentada e segura de obter autenticação com um aplicativo next.js é fundamental para seu sucesso como estrutura.

Não me lembro da última vez que construí uma web que não tivesse autenticação.
Como a maioria das pessoas que imagino, também quero fazer chamadas seguras para meus dados de backup para descanso, então JWT parece a solução óbvia.

Mas há tanta discussão entre as questões e as relações públicas que não tenho certeza por onde começar!

@timneutkens
Legal 👍 Acho que vai ser muito valioso para os outros.

@camstuart @kolpav Existem alguns bons exemplos de trabalho acima, incluindo o suporte a oAuth e autenticação baseada em e-mail que usa cookies JWT e HTTP pelos contribuidores @jaredpalmer , @luisrudge , @impronunciable , @possilities e eu mesmo.

Para destacar alguns links funcionam check-out:

(O exemplo de microautenticação foi bom, mas acho que precisa ser atualizado.)

Há espaço para melhorias adicionais, que comentaristas adicionais comentaram acima - incluindo um componente de armazenamento de sessão e divisão da lógica do servidor; e um exemplo que simplifica ainda mais as coisas nas quais Tim está trabalhando.

Simplicidade para autenticação é uma área desafiadora para aplicativos universais, mas você deve ser capaz de seguir os exemplos acima - ou apenas experimentar as demos diretamente - e ver como funcionam sem muito barulho.

@iaincollins São ótimos exemplos. Mas como posso usar (por exemplo) o projeto inicial? Então, se eu quiser construir meu aplicativo. Eu preciso clonar este repo? Ou preciso "copiar e colar" o código do projeto inicial, pedaço por pedaço, para o meu próprio código?

Se o projeto inicial for atualizado - o que devo fazer?

@iaincollins
Bons exemplos, especialmente os seus.
Mesmo assim, gostaria de ver um exemplo de autenticação com o selo de aprovação zeit 😄 todos os olhos apontariam em uma direção para que quaisquer erros ou erros não passassem despercebidos. Por enquanto, tenho minha própria autenticação de trabalho, mas não tenho certeza de quão seguro é.

Concordo, @kolpav ,

Eu criei uma pilha para isso que pode lidar com autenticação facilmente com GraphQL: https://github.com/thebillkidy/MERGE-Stack

@salmazov Eu descobri uma maneira útil de começar e entender o que está acontecendo em um exemplo ou projeto inicial pode ser bifurcá-lo e, em seguida, remover coisas que não são relevantes até que você fique apenas com o código relacionado à funcionalidade você deseja implementar; e, em seguida, tentar transferir apenas essa funcionalidade para outro projeto.

@kolpav @camstuart Este tópico contém uma discussão realmente extensa de vários modelos de segurança, incluindo desmascarar alguns equívocos comuns e compensações. Eu observaria particularmente os pontos sobre cookies somente HTTP e tokens CSRF (e a proteção adicional que eles fornecem contra XSS e CSRF sobre o uso de JWT e / ou API de armazenamento da Web para tokens de sessão). Realmente vale a pena ler.

@iaincollins Você queria vincular algo? :sorriso:

Ola pessoal :)

Tenho uma pergunta, estava lendo a seguir, não consigo obter os tokens para autenticação com jwt do localstorage.

Se eu tiver um servidor para render apenas a primeira cobrança do meu site com a próxima. E eu tenho outro servidor para minha api. Este recebe o usuário / passe do cliente e dá um jwt. Em quais casos preciso obter o token no servidor ??

Por que eu precisaria de um token no servidor de renderização (a seguir)?

Se o cliente não enviar o token para o servidor api, a api não fornecerá os dados e o usuário não conseguirá obter informações privadas. Não estou entendendo por que preciso enviar o token para o servidor de renderização.

@kamilml

Isso pode ajudar. Em geral, você tem duas opções:

  1. Forneça JWT ao próximo servidor (possivelmente por meio de cookie). Isso permitirá que o próximo servidor faça chamadas de API em nome do cliente. Você desejaria isso se a renderização completa do lado do servidor fosse importante para você.

  2. Armazene o JWT no armazenamento local do cliente e não conceda acesso ao servidor Next. Nesse caso, você pode simplesmente ignorar as chamadas de API do lado do servidor e adiar a renderização completa até que o carregamento do lado do cliente seja concluído.

Peço desculpas por reabrir isso, mas pensei em adicionar meus 2 centavos a este tópico, e como minha P&D inicial está se saindo nesta área. Menos exemplos de código, mais fluxo de alto nível.

Em primeiro lugar, para contextualizar, a maior parte de nosso aplicativo já é construída em Symfony 3 (PHP) e usa Vue para uma experiência híbrida. O servidor renderiza uma página de wrapper e atribui dados do aplicativo a __INITIAL_STATE__ para o aplicativo selecionar. Isso resultou na tomada de decisão entre renderizar páginas de marketing no Symfony (para SEO) e escolher UX / UI em vez de SEO em outras áreas, buscando dados via JS e fornecendo uma sensação mais SPA. Também vale a pena notar que nem todas as páginas são binárias públicas / privadas (como vi em alguns exemplos). Algumas páginas são públicas por padrão e, em seguida, renderizadas de forma diferente se autenticadas. Estávamos pensando em usar um SPA para partes do site, mas de muitas maneiras práticas, era um UX / UI pior (TTI mais lento, barras de progresso etc.). Além disso, isso não resolve o problema de SEO, a menos que introduzamos FOUC e renderizemos o texto duas vezes (uma vez através do Symfony, mais uma vez como componentes JS), etc.

Insira SSR / JS universal ...

No meu caso, o que fiz foi imitar a lógica PHPSESSID , criando um cookie HttpOnly UJSSESSID (criou o nome) e definindo o valor para o usuário JWT. O aplicativo Symfony passa isso adiante em cada solicitação de página à medida que o usuário navega pelo site. Quando o usuário acessa as páginas UJS, o lado do servidor desses aplicativos receberá os cookies na solicitação (de acordo com o comportamento integrado do navegador). Se o cookie UJSSESSID estiver definido, o aplicativo chamará a API para obter as informações do usuário (por exemplo, /api/v1/users/me passando o token via Authentication header). O restante das chamadas é feito por meio da API, usando o mesmo token. O mecanismo de logout do Symfony limpa o cookie UJSSESSID . Na próxima vez que o aplicativo UJS for carregado, ele renderizará as páginas no modo de usuário anônimo. Para sua informação, o roteamento da página Symfony vs. UJS é feito por meio do ProxyPass do Apache. LocalStorage não é usado.

O resultado final é uma UX perfeita em que o usuário está em algumas páginas que são PHP com JS do lado do cliente e algumas páginas que são UJS. Isso nos permite fazer testes A / B e atualizar o site de forma iterativa - nem todo mundo começa do zero :)

Embora isso seja um pouco mais complicado devido ao PHP / UJS simbiótico, o mesmo princípio pode ser usado em uma solução UJS completa com um middleware de servidor API ou Node.js (por exemplo, Express, Adonis, etc.). Em vez de definir o cookie UJSSESSID por meio de uma solicitação de página PHP (sinalizador HttpOnly ), faça com que o usuário faça o login através do seu SPA / UJS e defina o cookie lá. O que você NÃO DEVE fazer é usar seu aplicativo para decodificar o JWT ou fazer chamadas de terceiros que exijam client_secret . Use um middleware que permanece no servidor para isso.

Espero que ajude alguém. Outros exemplos que vi eram placas de Petri um pouco demais para mim.

@jaredpalmer Ei, obrigado por essa implementação, eu tentei, apenas

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

export default withAuth(index)

e em withAuth hoc eu mudei para redirecionar para a página de login.
Mas antes de redirecionar para a página de login, o conteúdo da página de índice ainda pisca um pouco. : S

Qual é a situação desse problema? 😇

Isso é um pouco opressor para as pessoas novas que estão lendo toda a discussão. Decidi implementar a autenticação básica aqui. Apenas 2 páginas (índice, login) e um servidor personalizado
https://github.com/trandainhan/next.js-example-authentication-with-jwt

Basicamente, temos um middleware de autenticação no servidor para verificar o token no cabeçalho de cada solicitação. O jwt-token será armazenado em cookies. Acho isso muito simples, direto e funciona muito bem.

@trandainhan Você poderia adicionar um endpoint POST que usa um token secreto para evitar ataques CSRF?

@sbking Código-fonte atualizado com um endpoint de exemplo protegido por ataques CSRF

Está pronto para usar 😬?

Alguém tentou autenticação com redux-auth-wrapper ?

Olá pessoal! Nos últimos meses, criei e agora refinei

Possui:

  • Cadastro com email e senha
  • Login com e-mail ou nome de usuário e senha
  • Página da conta onde você pode definir seu nome de usuário, alterar sua senha e reenviar um e-mail de verificação
  • Esqueceu a senha / redefiniu as páginas de senha
  • Verificar página de e-mail
  • Uma API GraphQL básica vinculada a um MongoDB (1 arquivo, pode ser facilmente removido)
  • Boilerplate mínimo (o máximo de lógica possível é encapsulado em bibliotecas)

Confira uma demonstração ao vivo aqui: http://staart.nmr.io/

Eu fiz isso principalmente para mim mesmo para prototipagem rápida, para começar rapidamente com um aplicativo com um sistema de contas simples e funcional que não depende de serviços externos. Estou muito feliz com os resultados. Pretendo continuar usando e mantendo essas bibliotecas, então experimente se você sentir que um sistema de contas + IU para o nó ainda está faltando.

@trandainhan Obrigado, esse é um ótimo exemplo e muito mais simples para muitas pessoas e funcionaria em vários cenários.

Vou pensar se / como posso adaptar a lógica atual no nextjs-starter para usar algo assim, mas com segurança, embora ainda seja compatível com a lógica de sessão expressa para casos de uso do mundo real que tenho (como usar coisas como APIs oAuth do Google, onde preciso que o servidor mantenha e rastreie os tokens concedidos no primeiro login).

Ainda não descobri se isso é possível, mas seria muito mais fácil para as pessoas se fosse.

Se não, pelo menos vale a pena escrever em algum lugar para explicar às pessoas as diferentes opções.

@trandainhan : Se eu adicionar <Link href="/">Home</Link> ao login.js e clicar no link gerado, poderei acessar o index.js sem estar logado. Como você sugeriria corrigir isso em seu exemplo?

@iaincollins Vejo a maioria das soluções aqui autenticadas em um serviço oauth. Existe alguma boa solução para autenticação em uma API que depende do JWT?

@paulwehner Acho que isso acontece porque @trandainhan só lidou com o roteamento de autenticação do lado do servidor. Ainda não estou certo de como o roteamento do lado do cliente funciona no next.js porque tudo é tratado internamente pelo próximo componente / Link.

@ carlos-peru Sob o capô, o Link realmente usa next / router para inserir um novo caminho no histórico do navegador. Parece que a maioria das coisas é tratada pelo navegador, não há nada a fazer aqui para um middleware no lado do servidor. Até agora, só consigo pensar em criar nosso próprio componente Link e fazer outras coisas sempre que mudarmos o url.

@ carlos-peru Você sempre pode usar o servidor personalizado para ter controle total sobre o roteamento.

Obrigado @trandainhan e @kolpav! Tive que entender melhor depois de passar algum tempo no fim de semana. Também consegui implementar uma solução que conta com HOC que mantém o token jwt em um cookie, para que tanto o servidor quanto o cliente consumam a api.

@nmaro Eu tenho um back-end com autenticação JWT para um site em next.js e um aplicativo (react-nativo).

Então. Acho que no próximo modelo: O cliente obtém um token do Facebook (o usuário aceita o login do facebook) e, em seguida, o cliente envia para o backend o token para verificar e validar o token do usuário (passport-facebook-token no backend node-js) . Então, se o token estiver bom, o back-end envia ao cliente o JWT gerado no back-end.

O problema? Sua ferramenta pode obter a chave no facebook e no google? Estou usando hello.js, mas não é compatível com next.js e todo o mundo usa passaporte-facebook. Acho que é um grande erro porque o servidor api precisa ser compatível com o cliente web e o aplicativo móvel.

Obrigado

PS: Não consigo obter um convite no seu canal slack.

@hmontes aqui - um convite para o canal de folga para o Ooth projeto (contas de usuário para o nó, em next.js particulares) sinta-se livre para participar. Ooth usa passport-facebook-token (não passport-facebook) e passport-google-id-token que acredito resolver seu problema.

@nmaro eu quero te ajudar com seu projeto. Eu tenho um back-end com Graphql com passport-facebok e passport-google-id-token também !!!

Mas. Você conhece um pacote compatível para conectar cliente facebook / google em next.js?

No lado do cliente, você pode usar um padrão semelhante ao que usei aqui: 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
(em vez de oothClient.authenticate apenas envie uma solicitação de postagem para sua rota de autenticação).

@timneutkens um PR com um mínimo de exemplo seria bem-vindo?

@nmaro em seu exemplo. Por que você usa componentDidMount em vez de componentWillMount?

Estou criando um componente HoC (provedor) para o login do Google.

O componentWillMount é chamado apenas no cliente? Então deve ficar bem.

Ok. Muito obrigado por seus exemplos. Finalmente, posso implementar autenticação com mídia social.

Ultima questão. Fui criado um access_token e um refresh_token e quero colocar em localStorage para salvar os dados.

Onde posso colocar isso em next.js para verificar o login quando a página for atualizada? Em create-react-app eu coloquei isso em 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}>
....

Obrigado

Com ooth, não armazeno os tokens do Facebook, apenas os uso uma vez com o passaporte e, em seguida, crio uma sessão de usuário normal baseada em cookies.

Ah. Ok. Estou usando JWT em vez de sessões. Então, eu envio access_token para o backend, ele verifica se o token é válido no serviço (google, facebook), então se o usuário existe no meu aplicativo e me envia de volta um JWT.

Obrigado :)

Devo acrescentar que é perigoso armazenar JWTs no armazenamento local (por causa do XSS), melhor enviá-los como cookies se estiverem todos em um servidor / domínio, de modo que o código javascript do cliente não possa obter o JWT, mas o navegador enviará o JWT automaticamente como cookies. Os JWTs são complicados (veja as longas discussões acima), é por isso que em ooth eu uso sessões.

Eu uso um JWT apenas se outro for usado como um microsserviço de autenticação externa (porque os cookies só funcionam no mesmo domínio) e, mesmo assim, eu o uso exatamente uma vez para criar uma sessão com o servidor, de modo que não seja armazenado em nenhum lugar do cliente .

Para aumentar a segurança do JWT, você pode usar um token de atualização (obtido de oauth2).

Um aplicativo móvel pode lidar com cookies?

@nmaro Okey. Eu entendo você. Desculpe pelo meu erro.

Você tem um tutorial ou código para salvar o token JWT em um cookie? Estou procurando https://github.com/zeit/next.js/blob/master/examples/with-firebase-authentication (Meu sistema de autenticação é com graphql)

Não, desculpe, eu nunca fiz isso.

@hmontes @nmaro
Eu escrevi isso para mim, mas pode ajudar:
https://github.com/malixsys/mobazoo

Olá @malixsys, obrigado por compartilhar. Algo que não entendi do seu código: parece que você configuraria API_BASE_URL para algo externo, certo? Se você se autenticar nessa API, acho que isso iniciaria uma sessão baseada em cookie, certo? Mas então, como você transfere esse cookie para o servidor next.js, supondo que o servidor next.js esteja em outro domínio?

Ah não, vejo, você define / auth / signin no mesmo processo. Ok, então é basicamente a mesma abordagem que usei para o outro com next.js / staart.

Na verdade não, ainda estou confuso: em / auth / signin você devolve um JWT, mas nunca faz nada com isso no lado do cliente. Mais tarde, você "getUserFromCookie", mas não vejo onde definiu o cookie.

@nmaro Eu só queria um login básico para trabalhar com universal inicialmente. Eu defini um jwt e um cookie por enquanto. O cookie é usado para fazer universal. Ainda não usei o jwt em outra chamada de axios neste repo. Eu faço em uma bifurcação privada onde API_BASE_URL aponta para outro domínio ...
Está tudo na minha lista TODO, com PWA.
Sinta-se à vontade para abrir um problema ou enviar um ping aqui ...

Sei que esse problema está encerrado, mas gostaria de mostrar minha solução.
https://next-auth.now.sh/
Acho que é um pouco semelhante ao site zeit.co

9 de abril

Eu fiz algum trabalho nisso, atualmente atolado com outras coisas 😥 De qualquer forma, isso está no topo da minha lista de prioridades para o Próximo

22 de setembro # 2974

Estamos planejando lançar um exemplo de autenticação oficial em breve. Você poderia lançá-lo como um repositório separado? Thaaaanks!

@timneutkens

Olá, desculpe incomodá-lo novamente, mas você poderia compartilhar como está o status do exemplo de autenticação oficial? É definitivamente algo que eu adoraria ver e a julgar pela contagem de comentários nesta edição também. Então, quão alto está na lista de prioridades e devemos ter esperanças? 😄

Olá @kolpav , eles anunciaram oficialmente isso no roteiro do Next.js 5.

Finalmente, estamos adicionando alguns exemplos altamente solicitados (como autenticação de usuário), documentação aprimorada para internos do Next.js e recursos menores e correções de bugs.

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

@babenzele Essa é uma ótima notícia. Eu devo ter sentido falta disso 😅

Onde podemos ficar atualizados com o desenvolvimento do Next.js 5? Estou integrando o auth0 no momento, mas seria ótimo ter um caminho / exemplo oficial do nextjs auth para seguir

Parece que Next.js precisa desesperadamente de um exemplo de autenticação local oficial usando JWT / cookies / localStorage (ou uma combinação deles, desde que seja seguro e protegido de XSS / CSRF) ... Passei várias semanas tentando criar isso usando um servidor Express API separado com uma estratégia local Passport.js. Tentei o JWT em um cookie / localStorage para solicitações sem estado e também tentei um cookie regular com o ID de sessão do middleware de sessão expressa nele, uma vez que finalmente desisti do JWT e da ausência de estado. Acabei tendo problemas em que sessões extras estavam sendo criadas no servidor Express API devido à maneira como a sessão expressa funciona (tentei com saveUninitialized: false). Considerei mover o código Express para server.js em meu aplicativo Next.js, mas eu realmente prefiro que seja um servidor separado. Também tenho certeza de que minha implementação não é segura contra XSS / CSRF ou sequestro de cookies. Precisamos de um exemplo oficial que cubra as melhores práticas para autenticação / login local, ou talvez um módulo oficial como parte do Next.js que irá lidar com as complexidades para nós!

Enquanto aguardamos Next.js 5.x e outros exemplos, você pode dar uma olhada em https://nextjs-starter.now.sh, que ainda é mantido ativamente e usa Next.js com Express, Express Sessions, CSRF ( CRSF Tokens), XSS (cookies somente HTTP para tokens de sessão) e usa Passport JS para oferecer suporte a oAuth e e-mail.

Na verdade, estou no processo de refatoração do código de autenticação em um módulo, tornando realmente fácil adicionar autenticação a projetos Next.js de uma maneira fácil de usar. O módulo deve permitir que você o use facilmente com qualquer banco de dados de sua preferência, sem ter que copiar um monte de código do exemplo do Starter Project. Espero terminar com isso esta semana.

Para referência, o método "Double Submit Cookie" fornece uma maneira fácil de adicionar proteção CSRF se você estiver procurando por uma abordagem simples, mas segura.

O problema com o JWT no localStorage é que ele sempre pode ser lido no JavaScript do lado do cliente, portanto, um vetor para sequestro de sessão via XSS, se você tiver conteúdo não confiável (por exemplo, conteúdo enviado pelo usuário ou anúncios).

Se você usar cookies Apenas HTTP para tokens de sessão - ou algo como um JWT com um valor de token em Apenas HTTP (ou criptografar todo o JWT e descriptografá-lo usando um token Apenas HTTP no servidor) - então as sessões são tão protegidas quanto podem ser. A ideia é apenas que, idealmente, os tokens de sessão especificamente não devem ser legíveis por meio de JavaScript do lado do cliente.

É claro que muitos sites não usam cookies HTTP somente porque é uma dor para aplicativos de página única e invariavelmente envolve ter alguma lógica de autenticação no front end, mas ainda é a abordagem ideal.

Outra opção ainda é https://github.com/nmaro/ooth, que é mantido ativamente, já vem em pacotes e está sendo usado em alguns aplicativos de produção.

@iaincollins Baixei o Next.js Starter Project e comecei a procurá-lo. Precisarei separar as partes importantes relacionadas à segurança (XSS / CSRF) e tentar integrá-las ao meu aplicativo ou esperar até que você conclua o módulo separado. Existe algum lugar onde eu possa acompanhar o desenvolvimento desse módulo?

Olá @ kelleg1 ,

O módulo separado agora está publicado como o módulo next-auth para torná-lo mais fácil de usar em outros projetos. Inclui um projeto de exemplo que mostra como usá-lo.

Para referência futura, o projeto nextjs-starter.now.sh agora também usa o next-auth, o que simplifica muito o código no projeto inicial - e agora é muito mais fácil adicionar suporte para novos provedores oAuth ou usá-lo com bancos de dados diferentes ( embora o exemplo ainda use Mongo DB).

Ainda é um pouco complicado, então, se você tiver um aplicativo existente, pode achar mais fácil de usar como referência, mas se assim for, espero que ajude

NB: Atualmente, o CSRF ainda está fortemente acoplado a ele. Ele usa lusca, então assume que res.locals._csrf está definido, mas diferentes bibliotecas CSRF usam diferentes vars privados.

Eu aprecio que ainda seja mais complicado de usar do que qualquer um gostaria, mas pelo menos agora o código de autenticação está finalmente separado em um módulo para que eu possa começar a refatorar. Espero torná-lo mais fácil de usar (com manipuladores padrão para bancos de dados diferentes e configuração mais fácil de oAuth) ao longo do tempo.

@iaincollins parece que a única dependência do next.js é https://github.com/iaincollins/next-auth/blob/master/index.js#L342 ? Nesse caso, seria ótimo tornar a lib next.js agnóstica.

@sedubois Concordo totalmente!

Embora essa seja provavelmente uma boa discussão para os problemas de repositório do GitHub, e não aqui. 🙂 Se você gostaria de sugerir maneiras de melhorá-lo, simplificá-lo e torná-lo mais genérico, adoraria colaborar.

(Ter algo que seja tão fácil de usar com o próximo quanto possível ainda é um objetivo principal, mas não vejo que precise ser exclusivo, mesmo que termine com as próximas opções específicas focadas também).

@timneutkens Parabéns pelo lançamento de nextjs 5. Em um futuro próximo, veremos a adição de um exemplo de autenticação local oficial adicionado à pasta de exemplos? Ou isso ainda está em andamento para um lançamento posterior?

Ooth agora tem uma documentação abrangente, incluindo os detalhes da autenticação next.js.

@jaredpalmer Gosto da sua abordagem, copiei partes da sua implementação. No entanto, o método getUser é seguro? E se alguém alterar a string no armazenamento local manualmente, seria inteligente para o aplicativo confiar nisso? Faria mais sentido transformá-lo em um método que decodifica a parte pública JWT do token todas as vezes e lê o estado do usuário a partir daí? Dessa forma, podemos confiar neste estado mais devido à natureza das coisas do JWT. qual e sua OPINIAO?

Você deve armazená-lo em um cookie. Eu escrevi que antes do Next tinha suporte para servidores personalizados.

@jaredpalmer Você pode me falar mais sobre isso? Devemos armazenar tudo em cookies em vez de armazenamento local? O seu HOC também seria diferente? Isso significa que agora podemos usar o método getInitialProps para renderizar sites protegidos do lado do servidor?

Apenas para avisar _se você está falando sobre armazenamento de dados confidenciais em armazenamento local / Web Storage_:

"Nunca armazene dados confidenciais usando o armazenamento da Web: o armazenamento da Web não é um armazenamento seguro. Não é“ mais seguro ”do que os cookies porque não é transmitido pela rede. Não é criptografado. Não há sinalizador Seguro ou somente HTTP, portanto, não é um local para manter a sessão ou outros tokens de segurança. "

Defina o token em um cookie após o login. use cookie-js. Em seguida, use o analisador expresso de cookies no servidor para que você possa verificar se há req.headers.cookies.myToken ou equivalente. Em getInitialProps de um hoc, verifique se req existe e, em seguida, pegue o token de req.cookies, caso contrário, obtenha-o no cliente em Cookies.get ('mytoken'). Neste ponto, você terá acesso ao seu token no cliente e no servidor. Então você deseja fazer um fetch / axios wrapper / instância e mesclá-lo com o próximo ctx em getInitialProps para que todas as suas páginas tenham uma maneira de fazer solicitações isomórficas autenticadas. Você também pode querer apenas colocar seu usuário no hoc. Portanto, você não precisa repetir isso em todos os lugares. Você pode fazer mais hocs se precisar para entidades comuns como withUser (withTeam (Page))

getUser é uma má ideia, você só deve armazenar o token.

@jaredpalmer Eu construí uma abordagem quase exatamente assim e tudo está funcionando bem. O problema que estou tentando resolver agora é como atualizar os tokens. A API com a qual estou trabalhando tem tokens de duração relativamente curta (2 horas) e estou tentando entender algum sistema para manter o usuário conectado enquanto usa o aplicativo.
Você tem alguma opinião sobre isso?

Você também pode salvar uma solicitação em cada transição de página, não passando dados por meio de next.js para seu usuário. Para fazer isso, você deve ler o token do cookie em server.js e tentar buscar o usuário e passá-lo para o próximo manipulador de solicitação. No documento, obtenha-o dos adereços e armazene-o em JSON em window.USER. Então, em seu hoc, você acabou de lê-lo da janela quando estiver no terreno do cliente. Por último, você deve usar axios com um interceptor de resposta que apaga imediatamente o seu cookie ao receber o código 403 e recarrega a página

@pbrandone Embora repetitivo, a solução mais simples e previsível é enviar seu token com cada solicitação, recebendo um token recém-atualizado no cabeçalho / corpo da resposta. Seu cliente atualiza seu valor conhecido em cada ciclo de solicitação / resposta, efetivamente concedendo tokens de uso único que nunca ficam obsoletos e não podem ser usados ​​indevidamente.

Normalmente, com esta abordagem, o /token/refresh só é usado para o "despertar" inicial do aplicativo (pense em componentWillMount gancho para <App/> ), verificando se o último token ainda é válido e utilizável.

Nunca tive que lidar com tokens de atualização, mas eu tentaria fazer isso em um interceptor axios. Teria que pensar mais sobre isso, mas em teoria você interceptaria um pedido incorreto e, em seguida, usaria o token de atualização para obter um novo token, definiria o cookie com o novo token e reproduziria a primeira solicitação com o novo token novamente. Os interceptores Axios são realmente poderosos porque permitem que você mantenha coisas como essa abstratas e fora da vista do código do produto. Você pode precisar escrever um interceptor de solicitação e resposta e / ou manter algum tipo de objeto / mapa com estado em andamento que pode ficar complicado. Espero que ajude

Ou faça o que Luke disse. Muito facil.

Se você quiser atualizar os tokens, precisará interceptar o cookie (se estiver usando cookies) por meio de uma configuração de servidor personalizada. Eu fiz isso usando o expresso. Caso contrário, você pode fazer tudo no lado do cliente usando tokens JWT. Como eu estava fazendo o meu por meio de cookies, tive 2 casos de uso - lidar com solicitações de navegação no lado do cliente e solicitações de página no lado do servidor (sempre que alguém digitar manualmente um url ou acessar a atualização da página). Mas basicamente eles são o mesmo fluxo apenas executado de forma diferente, pois um é do lado do cliente e o outro do lado do servidor. No lado do servidor, como usei o express, acabei de criar um middleware para processar o cookie, decodificar o JWT dentro dele e verificar a validade. Se ele tiver expirado, faça a solicitação para obter um novo token e continue a passar o usuário decodificado para reduxar no cliente. No cliente, tenho um wrapper HOC para links seguros que verifica constantemente o usuário, cada vez que você navega em algum lugar do cliente. O HOC pegará seu cookie ou JWT, (no meu caso meu JWT está em um cookie) e verificará se ele ainda é válido rapidamente. Se não for válido, ele tentará obter um JWT / Cookie atualizado e continuará, caso contrário, fará o seu logout. Sim, mais complicado, mas também mais seguro. Espero que ajude! Sinta-se à vontade para fazer perguntas se estiver interessado - demorei um pouco para descobrir tudo. Eu pessoalmente me pergunto se as pessoas fazem login no Github ou FB etc - provavelmente ok para muitos casos, mas em alguns casos não é profissional. Eu ainda estou para ver um banco, saúde, nenhuma das minhas contas que eu pago, etc. - faça login com minha conta do FB. Pode ser apenas uma questão de tempo;)

Quanto à atualização de tokens de acesso;
Não tenho certeza se essa é uma maneira adequada de fazer isso, mas meus tokens de acesso duram muito pouco (um minuto ou mais). Para lidar com problemas de expiração de tokens do lado do cliente, usei essa abordagem;
Cada vez que uma página é carregada, ela verifica se o token expirou; em caso afirmativo, ele o atualiza. No entanto, se ainda for válido, ele chama o método que verifica quanto tempo até a expiração e define o tempo limite que atualizará o token de acesso na expiração. E então repete esse processo enquanto o visitante estiver no site.

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

Isso retorna milissegundos até a expiração:

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

Qualquer feedback sobre esta abordagem seria muito bem-vindo

@kunokdev - Acho que é um bom começo. Mudei uma função semelhante que tive que calcular o tempo restante para uma função que apenas retornou um booleano dependendo se estava expirado ou não e isso tornou meu código um pouco mais legível e simples, além de não precisar me preocupar em zerar temporizadores. Então, eu apenas verifico se o usuário expirou ou não em algum tipo de solicitação que requer autenticação. Se eu tivesse um cronômetro de contagem regressiva ou algo parecido que fosse visível para o cliente ou para depuração, sua função seria ideal. Esses são meus 2 centavos 👍

O exemplo de autenticação / login não deveria ser o mais básico possível? ou seja, armazenamento de memória e nenhum JWT ou coisas de token estranhas. Normalmente, quando você pensa em alguém "logado", significa que essa pessoa tem um cookie / sessão ativa.

Eu tenho um HOC de login trabalhando no lado do cliente e do servidor. Ele usa um JWT e um backend (pode ser node, django, qualquer que seja).

Confira e qualquer feedback é muito apreciado

https://github.com/hugotox/AppStarter

Código relevante aqui https://github.com/hugotox/AppStarter/blob/master/src/components/auth/login-required.js

@hugotox Eu gosto da ideia de usar Redux store para armazenar dados de autenticação, mas quando você chamar store.dispatch de getInitialProps várias vezes esteja pronto para efeitos colaterais indesejados, quando o componente decorado será renderizado mesmo se o processo de autenticação ainda não terminou. Vamos dar um exemplo de uso do seu código:

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

Em getInitialProps você chama verifyToken antes de verificationOk , então MyPage.mapStateToProps será chamado 2 vezes (se ouvir store.auth.user) e na primeira vez store.auth.user será nulo mesmo para uma página que requer usuário logado.

Ontem eu também lancei a primeira versão WIP do próprio starter kit Next.js, mas comecei com Docker, Flow e fast-redux: https://github.com/dogada/microchain

Eu uso imagens Docker dedicadas para servidor de API e webapp que podem ser executados em diferentes domínios, então ainda estou procurando uma solução de trabalho que permita fazer login de usuários reais, mas também emitir tokens de portador OAuth para clientes móveis, por exemplo.

@spencersmb excelentes elogios! Esta é exatamente a maneira que eu estava fazendo. Em termos de renderização do lado do servidor para uma URL na primeira visita (nunca logado), você está apenas interceptando-a, vendo que não há cookie e redirecionando para uma página do tipo pages / login.js, por exemplo?

@ james-ff Obrigado por postar isso, mesmo que pareça estar gritando no vazio.

Foi dito neste tópico algumas vezes, mas todos aparentemente ainda estão ignorando que não deveriam armazenar tokens de sessão no JWT com localStorage ou em cookies acessíveis em JavaScript e parecem tentar reinventar sessões e autenticação de uma forma menos segura (e isso requer JS do lado do cliente). 🙃🤦🏻‍♂️

O autor deste post escreveu muito bem o que há de errado nisso em 2016, mas infelizmente as coisas não parecem ter melhorado:

Infelizmente, parece que encontrei o limite superior do comprimento do artigo antes que as pessoas parassem de ler - muitos dos comentaristas no Reddit e no Hacker News continuaram sugerindo as mesmas "soluções" repetidamente, ignorando completamente que elas já foram abordadas e consideradas impraticáveis no próprio artigo.

No entanto, tanto o artigo original quanto o diagrama de acompanhamento são muito bons.

@iaincollins JWTs e aplicativos / autenticação sem sessão têm suas vantagens e muitas pessoas / empresas (incluindo o Google) ainda querem usá-los. Depois de ler o caso contra JWTs , ainda acho que deveria haver dois exemplos / bibliotecas oficiais de next-auth, um usando sessões (como next-auth) e um usando JWTs, ou talvez apenas um que permita o uso de qualquer um deles. As vantagens, desvantagens e advertências devem ser explicadas claramente para cada um nas páginas oficiais do tutorial Next.js e / ou na documentação do módulo em algum lugar. Até que alguém invente um padrão / biblioteca melhor, provavelmente continuarei a usar nextjs-starter e next-auth, uma vez que são os melhores que encontrei.

Eu só tenho observado isso casualmente, mas minha experiência com autenticação JS universal é armazenar um JWT em um cookie HttpOnly . Não há realmente nenhuma razão para usar localStorage quando você tem a opção de usar o lado do servidor do aplicativo para armazenar o cookie. Se você precisar acessar o JWT para chamadas de API de origem cruzada no navegador, poderá passar o JWT em um cenário do tipo __INITIAL_STATE__ (esteja ciente das vulnerabilidades XSS, mas pelo menos você não está "armazenando" do lado do cliente). Para acesso de mesma origem, o cookie será passado e para trás (assumindo que você está usando withCredentials para axios, ou credentials: 'include' para buscar), negando a necessidade de colocar o JWT em JS. . Você poderia usar um proxy no lado do servidor do aplicativo para retirar o JWT do cookie HttpOnly , _então_ fazer suas chamadas API de origem cruzada também. Você nega a chamada preflight nesse cenário também. Cada um com o seu, mas pessoalmente não acho que localStorage seja necessário para aplicativos universais.

@bjunc Sim, de todas as opções de onde um JWT deve ser armazenado (localStorage x cookie HttpOnly x Redux ou o que for), posso estar errado, mas acho que a resposta deve ser basicamente sempre um cookie HttpOnly. Isso parece ter sido afirmado inúmeras vezes em vários blogs e fóruns. (Não tenho certeza sobre tokens de atualização - talvez eles devam ser armazenados em um cookie HttpOnly também?) Eu acho que onde os JWTs são armazenados deve ser uma questão resolvida, além do resto das vantagens / desvantagens dos JWTs.

Tentei fazer mais ou menos exatamente o que você disse em um projeto meu antes de começar a adotar o nextjs-starter e o next-auth. Já faz um tempo, mas se bem me lembro, acho que o problema que encontrei foi que meu servidor Express API no qual eu estava autenticando (que estava usando express-session) não estava inicializando / buscando a sessão corretamente. Eu teria um comportamento estranho em que as sessões seriam inicializadas várias vezes. Pretendo voltar a fazer mais ou menos o que você descreveu se eu puder consertar isso. Também continuarei a trabalhar com nextjs-starter e next-auth, uma vez que as sessões eliminam muitas das outras preocupações que os JWTs colocam, como como invalidar tokens.

Novamente, um exemplo oficial (ou exemplos) seria útil. Palavra-chave: oficial, o que significa que os autores do Next.js consideraram todas as possibilidades e incorporaram as melhores idéias e práticas. Idealmente, usando recursos modernos do ES6 / ES7 / ES8, como promises e async / await.

@ kelleg1 , parece que seus problemas estão relacionados às especificidades da criação de cookies. Por exemplo, você pode criar acidentalmente vários cookies com o mesmo nome usando configurações diferentes ( HttpOnly , domínio, caminho, expiração, etc.); o que pode criar efeitos colaterais estranhos como você está descrevendo. Uma maneira de depurar isso é usar as ferramentas de desenvolvimento Aplicativo-> Cookies. Se você vir um monte de cookies com o mesmo nome, isso pode indicar a direção certa.

De qualquer forma, não estou na equipe principal, então não posso ajudar com uma contribuição "oficial" (e, na verdade, estou usando Nuxt.js, não Next.js), mas os princípios são os mesmos. Experimentei algumas maneiras diferentes de fazer isso (pesando os prós / contras de JWT, cookies, CSFR, websocket CSWSH, localStorage, etc.). Por fim, cheguei à conclusão de que a natureza universal do Next / Nuxt se prestava bem ao uso de cookies HttpOnly JWT. Talvez outros cheguem a uma conclusão diferente, mas eu pessoalmente não estou no campo de "oh Deus, não use o JWT, você não leu aquele artigo que diz que o JWT causa câncer !?".

@iaincollins, desculpe trazer isso de volta, mas todos os tutoriais na web usam localStorage para salvar o token e você está dizendo que é inseguro. Em caso afirmativo, onde devemos armazenar o token?

BISCOITOS! : D
usamos algo assim:

// 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({...});

Um pouco confuso, mas muito seguro, pois o javascript do lado do cliente nunca vê um token e o cookie é httpSomente, seguro, assinado e enviado automaticamente por axios ou outro ...

Oi pessoal acabei de criar um exemplo de autenticação com cookies e redux. Veja aqui https://github.com/zeit/next.js/pull/4011

Espere, então onde esse problema foi parar? Nunca consegui encontrar um exemplo de autenticação Express no repositório oficial.

Vejo muitas conversas sobre cookies e sessões, mas como isso funciona para meu aplicativo móvel nativo quando ele precisa acessar a API?

Isso pode ser feito, mas é para isso que serve o JWT: p

Acabei de fazer uma leitura detalhada de todo este tópico e me sinto compelido a resumir minhas descobertas.

Parece que há dois projetos iniciais viáveis ​​com módulos de autenticação sólidos fatorados:

Excelente trabalho de @iaincollins e @nmaro !

Obrigado @curran :)

Documentei todas as alterações de código que fiz para eliminar os aspectos estranhos de nextjs-starter nesta solicitação de pull https://github.com/iaincollins/nextjs-starter/pull/86

Isso pode estar se aproximando de um exemplo sólido de autenticação básica para o repositório Next.js.

Recentemente, tive uma necessidade de implementar o OAuth com o Office 365, então pensei em compartilhar um exemplo muito simples que apresentei aqui . Ele precisa de trabalho (e não está nem perto de ser tão desenvolvido quanto alguns dos exemplos acima), mas acho que poderia ser generalizado eventualmente para usar vários clientes OAuth também. Ele utiliza vários dos exemplos já mostrados no repositório de exemplos Next.js, bem como o encadeamento de rotas protegidas aqui . Em qualquer caso, apenas pensei em compartilhar, caso alguém quisesse um exemplo rápido de como (talvez) fazer isso com a Microsoft.

Para qualquer pessoa interessada em uma autenticação tokenizada JWT simples que funcione no lado do cliente e do servidor, recebi ajuda no bate-papo do Spectrum e decidi compartilhar com todos vocês. Qualquer feedback é sempre apreciado.

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

Oi, pessoal!
Aqui está outro exemplo de autenticação com next.js que criei alguns meses atrás, talvez alguém ache útil:

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

Ooth 2.0 foi lançado com novos e melhores documentos

Ooth é um sistema de gerenciamento de identidade de usuário criado para node.js (com next.js em mente).

Estratégias atualmente suportadas:

  • Principal: nome de usuário / senha (incl. Verificar e-mail, esqueci o pw etc.), convidado, facebook, google
  • Secundário: sessões baseadas em cookies, JWTs

Confira este exemplo ao vivo que coloca tudo junto com next.js ( código-fonte ).

Muitos dos exemplos de autenticação aqui (e na pasta de exemplos) retornam a sessão do usuário / token / etc de getInitialProps . Pelo que entendi, quando a página é renderizada no lado do servidor, isso significa que as informações da sessão do usuário serão enviadas como parte da resposta da página HTML (como parte de NEXT_DATA ) para o navegador.

Vejo dois problemas de segurança com este padrão quando getInitialProps é executado no lado do servidor:
1) A sessão do usuário é transmitida pela rede do servidor para o navegador. Se alguma dessas solicitações estiver usando http:// e não https:// , o token do usuário, etc., seria exposto na rede em texto simples.

2) A sessão do usuário é retornada do servidor para o navegador como parte da página HTML (em uma tag de script NEXT_DATA ). Ter o token / etc do usuário diretamente na página HTML parece arriscado, especialmente depois que a página é analisada, renderizada pelo navegador e outros scripts de terceiros podem agora estar em execução.

Essas questões já foram tratadas? Existem atenuações para essas ameaças?

É por isso que uso cookies. Veja meu exemplo aqui https://github.com/hugotox/nextjs-starter/blob/master/pages/_app.js

Finalmente! Aqui você pode encontrar um exemplo de autenticação next.js totalmente dockerizado com os seguintes contêineres:

  • next.js
  • microsserviço de autenticação (ooth)
  • api (graphql)
  • armazenamento de sessão (redis)
  • pequeno proxy reverso

Tudo é reunido com docker-compose.

Nota rápida sobre JWTs. Os JWTs têm a vantagem de ser sem estado, bons para dispositivos móveis e bons se você precisar passar credenciais de um domínio para outro. A principal desvantagem é que eles expõem o navegador ao XSS. Para este exemplo, optei por uma solução baseada em sessões de cookies puras. Ainda consegui dividir as coisas em microsserviços graças ao proxy reverso (todos os microsserviços rodam no mesmo domínio) e ao armazenamento de sessão compartilhada.

https://github.com/nmaro/staart/tree/master/examples/staart
O exemplo ao vivo é, como de costume: https://staart.nmr.io

Acho que é prudente esclarecer, o uso do JWT não "expõe" intrinsecamente você ao XSS. Em vez disso, se o seu site tiver uma vulnerabilidade XSS (não devido ao próprio JWT), um JWT pode estar comprometido (junto com qualquer outra informação acessível por script); enquanto os cookies httpOnly não estarão acessíveis. Esqueça que você pode usar um JWT como o valor de um cookie httpOnly !

Soluções somente de cookies podem funcionar bem para comunicação no mesmo domínio, mas se você tiver uma API sem interface (por exemplo, example.com chamando api.example.com ), então essa não é realmente uma solução, a menos que você queira usar o proxy do navegador solicitações de example.com a api.example.com fazendo suas chamadas de API para example.com e encaminhá-las com o cookie anexado à solicitação (que vem com seu próprio conjunto de prós / contras) .

Pessoalmente, sinto que os contras do JWT são amplamente exagerados e são facilmente mitigados por meio de uma série de proteções comumente implementadas. Não menos importante, uma lista negra de tokens referenciando a declaração jti (por exemplo, UUID da versão 4) no caso de o token ter sido comprometido antes da expiração.

Ei @bjunc sim, obrigado pelo esclarecimento. Correto, o JWT por si só não o expõe ao XSS. Com relação às suas outras observações, gostaria de acrescentar que cada uma delas tem suas armadilhas.

Esqueça que você pode usar um JWT como o valor de um cookie httpOnly!

Sim, gostaria apenas de acrescentar que isso remove a única vantagem do JWT de transferir credenciais entre domínios.

uma lista negra de tokens

Isso, e a outra prática comum de um token de atualização, remove a outra vantagem do JWT de ser realmente sem estado.

Este é o meu entendimento da situação:

  • Se você precisar de autenticação em vários locais, use JWT <- mas, se possível, evite devido à segurança
  • Se você precisar se autenticar em qualquer cliente que não seja um navegador e não tenha o problema de XSS (por exemplo, um desktop ou aplicativo móvel), use JWT
  • Caso contrário, use sessões baseadas em cookies, porque uma solução baseada em JWT será menos segura (XSS) ou não será realmente sem estado (listas negras), ou exigirá que você use um proxy para manter tudo sob o mesmo domínio de qualquer maneira.

Eu escrevi um artigo do Medium sobre Next.js Authentication / User accounts.
É um tutorial extenso e meu braindump de quase dois anos de desenvolvimento de tempo livre e pensamento (meu primeiro comentário sobre esta questão é de fevereiro de 2017).

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

Você continua correlacionando o JWT com ser menos seguro. O JWT não torna o seu site menos seguro. Não é em si uma vulnerabilidade XSS. Se você tiver um exploit XSS, você terá os mesmos problemas de qualquer maneira. Mesmo com um cookie httpOnly , o invasor pode não ser capaz de ler o valor do cookie, mas não importa porque eles podem executar código arbitrário - como solicitações XHR que passariam automaticamente os cookies de sessão (essencialmente agindo como um ataque CSRF). Se sua API é de domínio cruzado e você está fazendo solicitações de navegador para servidor, então o JWT está em algum lugar no código de qualquer maneira (seja no estado inicial ou localStorage). Se você usa o Axios, pode até ter definido padrões globais, onde o invasor precisa apenas fazer uma solicitação do Axios e nem mesmo se preocupar com a autenticação.

Além disso, você não pode realmente falar sobre XSS sem falar também sobre CSRF; onde os cookies de autenticação são especificamente direcionados. Mesmo com uma configuração de proxy reverso, um ataque CSRF permitiria ao invasor executar solicitações autenticadas em sua API.

A propósito, só porque você colocou o JWT em um cookie (por exemplo, saber se o usuário está conectado), não significa que você está impedido de usar o valor do cookie (por exemplo, um JWT) para servidor de domínio cruzado para Acesso ao servidor API no carregamento da página (que também funciona para o cenário de proxy reverso). Você também não está ou está impedido de passar o JWT no estado inicial para solicitações de navegador para servidor de API. Eles não são mutuamente exclusivos.

Como um aparte, acho a ideia de JWT "sem estado" superestimada e limitada na aplicação para a maioria dos casos de uso. Por exemplo:

  • Permissões baseadas em recursos / dinâmicas (por exemplo, não apenas " can edit Post ", mas " can edit Post:1634 ").
  • E se a conta do usuário foi bloqueada / excluída?
  • Não pagou sua assinatura mensal; qual funcionalidade de aceleradores?
  • Colocou o token na lista negra (conforme acima)?

Você não está embutindo tudo isso no JWT, o que significa que você precisa mergulhar na camada de domínio (ou seja, banco de dados) para descobrir. Você acabou de carregar o recurso, então pode muito bem colocá-lo onde o resto do aplicativo pode acessá-lo, e agora você tem o estado. Acho muito bobo pensar que tudo o que você precisa saber sobre o assunto estaria incluído no token; ao mesmo tempo que o mantém anônimo e leve. Sem divagar muito, há um argumento para solicitações "sem estado" na comunicação entre serviços, mas mesmo isso eu acho impraticável (pelo menos no que se refere ao conceito de colocar o que você precisa saber sobre o assunto no JWT). .

Outras estratégias de autenticação disponíveis entretanto (novas em negrito):

  • Local (nome de usuário / e-mail / senha)
  • Facebook
  • Google
  • Hóspede
  • Patreon
  • Twitter
  • Authy (Twilio) - sem senha via SMS

@jaredpalmer você escreveu
Como o php, a unidade atômica do Next é a página. Um dos recursos mais legais é que ele carrega lentamente cada página apenas quando solicitado. Com autenticação apenas do lado do cliente, mas com renderização do servidor, o js para aquela página protegida é de fato baixado pelo navegador. No futuro, quando o Next adicionar fluxos de trabalho do servidor, você poderá bloquear a renderização e redirecionar no servidor para evitar isso completamente. Isso exigirá cookies, sessões e armazenamentos de sessão AFAIK, mas esse é apenas o custo de fazer aplicativos híbridos como esses.

Estamos 2 anos depois. Existe um fluxo de trabalho do servidor para evitar o carregamento de js para páginas protegidas?
@timneutkens talvez
Como posso impedir totalmente o acesso ao conteúdo protegido?

@lishine Você tem um ServerResponse no getInitialProps de sua página - você pode facilmente redirecionar alguém sem privilégios.

Existe um exemplo de auth com redux?

Existe um exemplo de auth com redux?

Você pode tentar este exemplo que está usando redux e verificar se funciona para você ...
Você pode encontrá-lo em algum lugar neste tópico, mas caso você não consiga encontrá-lo, aqui está:
https://github.com/alan2207/nextjs-jwt-authentication

Acho que esse é um problema mais complicado ao usar os resultados da chamada da API do lado do servidor getInitialProps, porque o Virtual DOM usa resultados antigos após a ação LOGOUT-LOGIN. Estou pensando em solução

_EDITADO_
e aqui está minha resposta com redux-observável

| Lado | Auth | TODO |
| --- | --- | --- |
| Servidor | true | Buscar dados iniciais (com proxy de cookie de solicitação).
| Servidor | false | Mostra a página de login e busca dados depois de logado. |
| Cliente | true | Buscar dados iniciais. |
| Cliente | false | Mostra a página de login e busca dados após o login. (Isso acontece apenas quando a sessão expirou na mudança de página para página) |

  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));
      }));
  }));

Parece complicado quando algo simples serve, como:

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} />;
    }
};

Este exemplo foi mesclado como parte do Next.js 8
https://github.com/zeit/next.js/tree/canary/examples/with-cookie-auth

@timneutkens obrigado pelo link.

olhando para https://github.com/zeit/next.js/blob/canary/examples/with-cookie-auth/www/utils/auth.js#L26 -L34 ... não deveria haver algum tipo de verificar depois que auth() foi chamado?

Testar o exemplo sem um cookie faz com que Profile.getInitialProps() seja chamado, enquanto eu pensei que o redirecionamento aconteceria antes mesmo de tentar obter mais "adereços iniciais" ...

Fiz um exemplo aqui que tem pré-renderização + autenticação do lado do servidor com apollo

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

Lembre-se de que as diretrizes de segurança do OWASP não recomendam o armazenamento do token JWT no armazenamento local, ou seja, "Um único Cross Site Scripting pode ser usado para roubar todos os dados nesses objetos, portanto, novamente, é recomendado não armazenar informações confidenciais no armazenamento local."

Aqui está Auth0: onde armazenar tokens e Tom Abbott: onde armazenar seus JWTs - Cookies vs armazenamento da Web HTML5 .

Aqui está um exemplo com servidor proxy Nuxt.js + Express.js + backend Django. Onde o servidor Express é usado para proxy de solicitação de autenticação para o back-end real e está lidando com proteção CSRF ao usar o token JWT armazenado em um cookie (impõe algumas restrições no comprimento do token / quanta informação pode ser armazenada no token JWT): https: / /github.com/danjac/nuxt-python-secure-example

@timneutkens Eu preciso de alguns documentos sobre como enviar token do cookie 🍪 para o middleware redux personalizado SSR. Estou recebendo os cookies dentro de _app.js. Mas como devo passá-lo para customApimiddleware. Onde escrevi solicitações de busca. Obrigado

Eu escrevi um artigo do Medium sobre Next.js Authentication / User accounts.
É um tutorial extenso e meu braindump de quase dois anos de desenvolvimento de tempo livre e pensamento (meu primeiro comentário sobre esta questão é de fevereiro de 2017).

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

Acho que este é um dos melhores tutoriais para lidar com a autenticação em um aplicativo nextj.js. Já vi coisas como armazenar tokens em localStorage (XSS), armazenar tokens em cookies (sem manipular CSRF) e até mesmo armazenar tokens em cookies do navegador (vulneráveis ​​a XSS e CSRF).

Eu realmente gosto da sua solução com o proxy reverso e compartilhar as informações da sessão entre diferentes serviços. Eu realmente gostaria de não criar um servidor personalizado para o aplicativo next.js, mas acho que é a maneira mais direta de lidar com sessões e evitar csrf (e talvez adicionar o proxy reverso). Posso até acabar criando um projeto monolith (tanto para renderizar o aplicativo quanto para lidar com operações de banco de dados, etc.).

Eu vi que algumas pessoas (incluindo ZEIT) mantêm as APIs sem estado e permitem que o aplicativo next.js cuide da sessão. Ele passa os tokens para APIs. Mas ir com as sessões apenas torna as coisas um pouco mais apertadas e menos complicadas.

Seria realmente melhor ter um exemplo de autenticação completo para next.js. Com coisas como autenticação para APIs externas, manter a sessão no aplicativo next.js, compartilhar a sessão entre serviços ou passar tokens para eles e talvez até mesmo atualizar os tokens se eles estiverem expirados. (Muitas pessoas escrevem muito sobre JWTs e apenas os usam em seus tutoriais, mas na maioria das vezes nem os expiram ou nem mesmo os atualizam.)

De qualquer forma, você escreveu um dos tutoriais mais completos sobre o assunto. Então, obrigado!

Eu realmente espero que haja exemplos e documentação muito mais completos e claros.

Seria realmente melhor ter um exemplo de autenticação completo para next.js. Com coisas como autenticação para APIs externas, manter a sessão no aplicativo next.js, compartilhar a sessão entre serviços ou passar tokens para eles e talvez até mesmo atualizar os tokens se eles estiverem expirados. (Muitas pessoas escrevem muito sobre JWTs e apenas os usam em seus tutoriais, mas na maioria das vezes nem os expiram ou nem mesmo os atualizam.)

Eu também não sei qual abordagem escolher.
Muito obrigado pelo link para o artigo.
Atualmente, qual das implementações você escolheu?
Você encontrou um exemplo de autorização abrangente para a próxima v9.3 +?

Vale a pena conferir a nova abordagem baseada em cookies do Auth0
(Claro que isso é para um provedor de identidade específico, mas a abordagem pode ser útil para ver)
https://github.com/auth0/nextjs-auth0

  • Muito legal que você possa "proxy" de solicitações de API por meio das rotas de API do nextjs (mesmo por meio de uma rota dinâmica)
  • Então você nunca terá que expor tokens de acesso etc para o lado do cliente (já que as rotas nextjs api só executam no lado do servidor), o lado do servidor pode obter tokens de acesso etc por meio da biblioteca auth0 e cookie com um segredo
  • Seu código do lado do cliente chamaria suas rotas de api do nextjs, e as rotas de api executariam a solicitação de api real

Tenha em mente que eles dizem que esta abordagem é "experimental" no ReadMe

Vale a pena conferir a nova abordagem baseada em cookies do Auth0
(Claro que isso é para um provedor de identidade específico, mas a abordagem pode ser útil para ver)
https://github.com/auth0/nextjs-auth0

  • Muito legal que você possa "proxy" de solicitações de API por meio das rotas de API do nextjs (mesmo por meio de uma rota dinâmica)
  • Então você nunca terá que expor tokens de acesso etc para o lado do cliente (já que as rotas nextjs api só executam no lado do servidor), o lado do servidor pode obter tokens de acesso etc por meio da biblioteca auth0 e cookie com um segredo
  • Seu código do lado do cliente chamaria suas rotas de api do nextjs, e as rotas de api executariam a solicitação de api real

Tenha em mente que eles dizem que esta abordagem é "experimental" no ReadMe

Este artigo é muito útil e cobre muitas arquiteturas diferentes.
https://auth0.com/blog/ultimate-guide-nextjs-authentication-auth0/

Usar rotas de API como proxy, fazer login / logout por meio de rotas de API, obter o token da API, configurá-lo como cookie HttpOnly é uma abordagem sólida, eu acho.
Uma preocupação pode ser CSRF, mas você pode facilmente criar alguma solução com o pacote csrf npm (não csurf , mas isso também pode funcionar).

@onderonur , obrigado pelo artigo auth0.
ou seja, no momento há um exemplo confiável ou mínimo de implementação em jwt puro com next.js?
Não quero fazer uma camada avançada com cookies e configurá-los. No aplicativo csr, simplesmente armazenamos o token em localstorage e o enviamos junto com a solicitação.

@onderonur , obrigado pelo artigo auth0.
ou seja, no momento há um exemplo confiável ou mínimo de implementação em jwt puro com next.js?
Não quero fazer uma camada avançada com cookies e configurá-los. No aplicativo csr, simplesmente armazenamos o token em localstorage e o enviamos junto com a solicitação.

Eu usei esse método para um de meus repositórios, mas ainda está em rascunho, então certifique-se de testá-los você mesmo :)
https://github.com/onderonur/post-gallery
Na verdade, a "camada de cookies" não é uma coisa avançada. Basta chamar o ponto de extremidade de login da API por meio de /api/login rota da API e, se a solicitação for bem-sucedida, defina o token em um cookie httpOnly .
Você pode verificar meu repo para a mesma implementação exata.

Uma outra opção é (se você quiser ter quase o mesmo fluxo da configuração do token no armazenamento local), você pode usar o pacote js-cookie npm, chamar seu ponto de extremidade de login com uma solicitação do lado do cliente e terminar se ele retornar um token, configure-o em um cookie. E quando você faz uma solicitação (por meio de um interceptor axios etc.), leia o valor do cookie e o passe para sua API como um cabeçalho de solicitação. Já vi muitos aplicativos (e até mesmo alguns populares) usando essa abordagem. Mas isso é um pouco inseguro. Porque você não pode definir cookies de httpOnly no navegador. Assim, o JavaScript será capaz de ler seu cookie de token. Assim, haverá vulnerabilidade XSS.

Agradecemos que este seja um tópico antigo (e um tópico de longa duração em geral), mas para aqueles que procuram referências ou exemplos adicionais, começamos o trabalho em NextAuth.js v2 recentemente. Menciono não tanto como um plug - é um projeto de código aberto e um monte de gente ajudou nele - mas é super simples de usar e o código e abordagem podem ser úteis como uma referência para as pessoas.

Para alguns antecedentes, como NextAuth v1, ele usa cookies que são assinados, prefixados e apenas HTTP, evitando as armadilhas de segurança comuns de usar tokens do lado do cliente.

NextAuth.js v2 suporta login com Apple, Google, Facebook, Twitter, GitHub, Auth0, Okta, Slack, Discord e outros provedores de OAuth (suporta 1.xe 2.x). Você pode usá-lo com MySQL, MariaDB, Postgres, MongoDB - ou nenhum banco de dados (apenas OAuth e JSON Web Tokens para uma solução 100% sem servidor).

O uso é muito simples, há um método estático universal chamado session() e um React Hook chamado useSession() você pode usar nos componentes do lado do cliente:

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>}
  </>
}

Ele é construído para Next.js 9.xe Serverless, e não tem dependências como Express ou PassportJS. Inclui um provedor de autenticação que você pode usar em _app.js para adicionar automaticamente o estado de autenticação a todas as páginas; ele funciona para renderização do lado do cliente e do servidor.

Para obter mais informações, consulte next-auth.js.org ou confira next-auth @ beta no NPM

Ainda é um trabalho em andamento - ainda estamos aprimorando a documentação e o modelo de evento - com uma data de lançamento prevista para o início de meados de junho.

Agradecemos que este seja um tópico antigo (e um tópico de longa duração em geral), mas para aqueles que procuram referências ou exemplos adicionais, começamos o trabalho em NextAuth.js v2 recentemente. Menciono não tanto como um plug - é um projeto de código aberto e um monte de gente ajudou nele - mas é super simples de usar e o código e abordagem podem ser úteis como uma referência para as pessoas.

Para alguns antecedentes, como NextAuth v1, ele usa cookies que são assinados, prefixados e apenas HTTP, evitando as armadilhas de segurança comuns de usar tokens do lado do cliente.

NextAuth.js v2 suporta login com Apple, Google, Facebook, Twitter, GitHub, Auth0, Okta, Slack, Discord e outros provedores de OAuth (suporta 1.xe 2.x). Você pode usá-lo com MySQL, MariaDB, Postgres, MongoDB - ou nenhum banco de dados (apenas OAuth e JSON Web Tokens para uma solução 100% sem servidor).

O uso é muito simples, há um método estático universal chamado session() e um React Hook chamado useSession() você pode usar nos componentes do lado do cliente:

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>}
  </>
}

Ele é construído para Next.js 9.xe Serverless, e não tem dependências como Express ou PassportJS. Inclui um provedor de autenticação que você pode usar em _app.js para adicionar automaticamente o estado de autenticação a todas as páginas; ele funciona para renderização do lado do cliente e do servidor.

Para obter mais informações, consulte next-auth.js.org ou confira next-auth @ beta no NPM

Ainda é um trabalho em andamento - ainda estamos aprimorando a documentação e o modelo de evento - com uma data de lançamento prevista para o início de meados de junho.

Ótimo trabalho isso!
Isso pode ser usado apenas no lado do cliente? Por exemplo, eu tenho um aplicativo Rails API - e uso o próximo JS para o lado do cliente.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

formula349 picture formula349  ·  3Comentários

YarivGilad picture YarivGilad  ·  3Comentários

flybayer picture flybayer  ·  3Comentários

rauchg picture rauchg  ·  3Comentários

olifante picture olifante  ·  3Comentários