Next.js: Agregar ejemplo de inicio de sesión / autenticación

Creado en 29 oct. 2016  ·  208Comentarios  ·  Fuente: vercel/next.js

Con:

  • Ayudante de autenticación reutilizable en todas las páginas.
  • sincronización de sesiones entre pestañas
  • backend de correo electrónico sin contraseña simple alojado en now.sh

Creo que esto será de gran ayuda para muchos recién llegados.

p0

Comentario más útil

Así que tengo auth funcionando a la perfección. Como se mencionó en otra parte, es solo del lado del cliente, que en última instancia es solo la mitad de la batalla.

"Bastante seguro"

Como php, la unidad atómica de Next es la página. Una de las características más interesantes es que carga de forma diferida cada página solo cuando se solicita. Con la autenticación solo del lado del cliente pero con la representación del servidor, el navegador descarga el js para esa página protegida. En el futuro, cuando Next agregue flujos de trabajo del servidor, es de esperar que pueda bloquear el procesamiento y redireccionar en el servidor para evitar esto por completo. Esto requerirá cookies, sesiones y tiendas de sesiones AFAIK, pero ese es solo el costo de hacer aplicaciones híbridas como estas.

Ejemplo de autenticación

Suponga que tiene una API protegida por JWT con dos puntos finales de interés: /token y /me . /token acepta credenciales de correo electrónico / contraseña y devuelve un JWT firmado ( id_token ) mientras que /me devuelve información de perfil relacionada con el usuario autenticado por JWT. Adapté el siguiente AuthService.js del bloqueo de Auth0 (eliminando el emisor de eventos, aunque esa no es la peor idea). Extrae casi todo el manejo de tokens JWT para que pueda usarse en la página de inicio de sesión y también en un Componente de orden superior (más sobre esto más adelante).

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

El siguiente es un HOC para simplificar la protección de las páginas. Para evitar un destello no deseado de información sensible, la página renderizará el servidor Loading... en la primera renderización mientras reacciona arranca / lee el token de localStorage. Esto significa que las páginas protegidas no harán SEO, lo que probablemente esté bien a partir de ahora, pero definitivamente no es óptimo.

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

La página de inicio de sesión no puede usar el HOC tal como está ahora, porque el inicio de sesión debe ser público. Entonces solo crea una instancia de AuthService directamente. También harías algo similar para una página de registro.

// ./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 por los estilos de reacción de Airbnb, también comencé a trabajar en un next-with-auth lib, que sería una función que devuelve un HOC para usar en las páginas. También jugué con la fusión de AuthService y este HOC. Una solución podría ser hacer que este HOC acepte una función de nivel de permiso como argumento además del componente, como redux connect. Independientemente, en mi opinión, usarías next-with-auth así:

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

Hacer todo esto con Redux parecía innecesariamente complicado, pero básicamente puede seguir el ejemplo de la wiki, pero mueva AuthService a Acciones (inicio de sesión y cierre de sesión) y tenga un Reductor de usuarios. Sin embargo, solo puede llamar a estas acciones en el cliente, ya que no hay localStorage en el servidor, por lo que debe verificarlo en sus Acciones. En última instancia, la tienda redux se coloca en el window todos modos. Por lo tanto, podría almacenar en caché bien el usuario en window por su cuenta en lugar de usar el contexto. Si no quiere redux, también puede probar react-broadcast .

Por último, suponiendo que next/server envíen de acuerdo con # 25. next-with-auth podría abstraer el complicado almacenamiento local frente a las cookies del desarrollador con middleware + un HOC. También podría manejar la actualización de tokens.

Todos 208 comentarios

Sugerencia: use Redux y JWT para realizar el ejemplo

Estoy trabajando en un ejemplo para esto. Actualmente tengo problemas para que el componenteWillReceiveProps se active en mi componente de alto nivel (donde planeo verificar si el usuario está autenticado y redirigir a la página de inicio de sesión si no)

Así que tengo auth funcionando a la perfección. Como se mencionó en otra parte, es solo del lado del cliente, que en última instancia es solo la mitad de la batalla.

"Bastante seguro"

Como php, la unidad atómica de Next es la página. Una de las características más interesantes es que carga de forma diferida cada página solo cuando se solicita. Con la autenticación solo del lado del cliente pero con la representación del servidor, el navegador descarga el js para esa página protegida. En el futuro, cuando Next agregue flujos de trabajo del servidor, es de esperar que pueda bloquear el procesamiento y redireccionar en el servidor para evitar esto por completo. Esto requerirá cookies, sesiones y tiendas de sesiones AFAIK, pero ese es solo el costo de hacer aplicaciones híbridas como estas.

Ejemplo de autenticación

Suponga que tiene una API protegida por JWT con dos puntos finales de interés: /token y /me . /token acepta credenciales de correo electrónico / contraseña y devuelve un JWT firmado ( id_token ) mientras que /me devuelve información de perfil relacionada con el usuario autenticado por JWT. Adapté el siguiente AuthService.js del bloqueo de Auth0 (eliminando el emisor de eventos, aunque esa no es la peor idea). Extrae casi todo el manejo de tokens JWT para que pueda usarse en la página de inicio de sesión y también en un Componente de orden superior (más sobre esto más adelante).

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

El siguiente es un HOC para simplificar la protección de las páginas. Para evitar un destello no deseado de información sensible, la página renderizará el servidor Loading... en la primera renderización mientras reacciona arranca / lee el token de localStorage. Esto significa que las páginas protegidas no harán SEO, lo que probablemente esté bien a partir de ahora, pero definitivamente no es óptimo.

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

La página de inicio de sesión no puede usar el HOC tal como está ahora, porque el inicio de sesión debe ser público. Entonces solo crea una instancia de AuthService directamente. También harías algo similar para una página de registro.

// ./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 por los estilos de reacción de Airbnb, también comencé a trabajar en un next-with-auth lib, que sería una función que devuelve un HOC para usar en las páginas. También jugué con la fusión de AuthService y este HOC. Una solución podría ser hacer que este HOC acepte una función de nivel de permiso como argumento además del componente, como redux connect. Independientemente, en mi opinión, usarías next-with-auth así:

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

Hacer todo esto con Redux parecía innecesariamente complicado, pero básicamente puede seguir el ejemplo de la wiki, pero mueva AuthService a Acciones (inicio de sesión y cierre de sesión) y tenga un Reductor de usuarios. Sin embargo, solo puede llamar a estas acciones en el cliente, ya que no hay localStorage en el servidor, por lo que debe verificarlo en sus Acciones. En última instancia, la tienda redux se coloca en el window todos modos. Por lo tanto, podría almacenar en caché bien el usuario en window por su cuenta en lugar de usar el contexto. Si no quiere redux, también puede probar react-broadcast .

Por último, suponiendo que next/server envíen de acuerdo con # 25. next-with-auth podría abstraer el complicado almacenamiento local frente a las cookies del desarrollador con middleware + un HOC. También podría manejar la actualización de tokens.

¡Estoy emocionado de probar esto! Gracias por la implementación de barebones :)

@jaredpalmer Estoy trabajando en algo similar. ¿Cómo funciona su AuthService cuando un componente se procesa en el lado del servidor? El servidor necesitaría acceder al JWT pero no puede leerlo desde el almacenamiento local.

@amccloud No lo hace. Ese es todo el problema. El HOC muestra <div>Loading..</div> en rutas protegidas y debe leer el token y decidir si redirigir o no en componentDidMount . Para que funcione de la manera deseada y se procese en el lado del servidor, Next necesita el número 25, o al menos la capacidad de configurar una cookie con el valor de JWT AFAIK.

Usé cookie-js para configurar una cookie, pero es un truco.
la cuestión es: si no envía una cookie, pierde todos los beneficios de nextjs y la representación del lado del servidor en rutas autenticadas

@jaredpalmer ¡ esto es genial! gracias por el esfuerzo. Intentaré terminar de implementar tu ejemplo (o ayudarte a hacerlo si quieres) en los próximos días.

¡Yo! Publiqué un ejemplo con nextjs y auth0 aquí: https://github.com/luisrudge/next.js-auth0
Tiene el concepto de un diseño principal y también "páginas seguras" que se cargan solo cuando el usuario está autenticado.
Déjame saber lo que piensas 🎉

@luisrudge increíble. Estoy clonando y haciendo algunos cambios, pero se ve muy bien.

¡Frio! ¿Qué crees que falta? ¿Qué cambios estás pensando?

El domingo 6 de noviembre de 2016 a la 1:12 p. M. -0200, "Dan Zajdband" < [email protected] [email protected] > escribió:

@luisr udgehttps: //github.com/luisrudge increíble. Estoy clonando y haciendo algunos cambios, pero se ve muy bien.

Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en Gi silencie el th

1) Uso de standard para el revestimiento (por lo que es coherente con todo lo que estamos construyendo a continuación)
2) Agregar soporte de múltiples pestañas solicitado por @rauchg
3) La parte css se puede simplificar

Te enviaré un pr :)

¿A qué te refieres con compatibilidad con varias pestañas?

El domingo, 6 de noviembre de 2016 a la 1:16 p. M. -0200, "Dan Zajdband" < [email protected] [email protected] > escribió:

1) Uso de estándar para pelusa (para que sea coherente con todo lo que estamos construyendo a continuación)
2) Adición de soporte de múltiples pestañas solicitado por @rauchghttps : //github.com/rauchg
3) La parte css se puede simplificar

Te enviaré un pr :)

Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en Gi silencie el th

Tiene 2 pestañas abiertas, cerrar sesión en 1, cerrar sesión automáticamente en la otra

Ah. ¡Eso es genial!

El domingo 6 de noviembre de 2016 a la 1:21 p. M. -0200, "Dan Zajdband" < [email protected] [email protected] > escribió:

Tiene 2 pestañas abiertas, cierra sesión en 1, cierra automáticamente la sesión en las demás

Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en Gi silencie el th

Hola @luisrudge te envié un PR con los cambios https://github.com/luisrudge/next.js-auth0/pull/2

muchas gracias por hacer esto <3

por cierto, este es el resultado:

2016-11-06 11 14 31

@impronunciable @luisrudge ¡ Fantástica implementación! Si desea usarlo sin Auth0, parece que solo necesitará cambiar los archivos en el directorio ./utils, tal vez incluso solo lock.js . Lo intentaré pronto. Por cierto, la pestaña múltiple se ve increíble 💯

@ugiacoman Comencé a implementar un pequeño servidor con passwordless.net, avíseme si desea obtener mi código como punto de partida

@impronunciable ¡ Eso sería genial! De hecho, iba a hacer algo similar con Digits de Twitter Fabric.

@impronuncible sugiero no usar la contraseña less.net, en su lugar, puede usar passport-local y simplemente enviar a los usuarios un enlace con su correo electrónico y token en la cadena de consulta.

Gracias @impronunciable ❤️

@ugiacoman sí, es bastante fácil eliminar la dependencia auth0. Lo usé porque no quería tener una API separada para manejar la autenticación

@jaredpalmer , hasta donde yo sé, tener el número 25 sería genial, pero ¿no es bloquear? Quiero decir que tenemos acceso al lado del servidor req en getInitialProps así que nada impide aplicarle cookie-parser . La autenticación del lado del servidor y la administración de sesiones son cosas nuevas para mí 😬

Por cierto, considerando que localStorage no se pueden usar en el lado del servidor, ¿son las cookies la única forma de tener sesiones en el lado del servidor? Tengo un vago recuerdo de que podría no ser el más seguro. ¿Pero hay alguna otra opción?

@sedubois

El enfoque de cookies puede ser muy seguro si se realiza correctamente. Hacer lo siguiente es bastante trivial:

  • use la bandera httpOnly (evita el acceso de JavaScript a las cookies)
  • use bandera segura (solo configure cookies para solicitudes https)
  • Cookies firmadas (verificar la fuente de la cookie)

También existe una ventaja de latencia muy significativa cuando puede acceder a la información de autenticación directamente en el servidor.

Deberíamos mover este ejemplo a examples/ Veré qué se me ocurre

Me las arreglé para usar react-cookie para cookies isomorfas envolviendo nextjs en un servidor expreso personalizado de la siguiente manera:

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

Esto me permite realizar una solicitud autenticada desde el lado del servidor. Esto no aborda ninguno de los problemas de las viñetas originales, pero resolvió el problema de compartir el estado entre el cliente y el servidor.

Desde el punto de vista de alguien que está aprendiendo muchas de estas cosas. Creo que sería mejor si los ejemplos no se basan en servicios de terceros como auth0. Sería más beneficioso para los recién llegados ver un ejemplo más básico con formularios de inicio de sesión / registro y el uso de Redux y JWT.

El ejemplo que planeamos _bundle_ se basará en las API del servidor Node.js de código abierto

Agregué un ejemplo de autenticación basada en correo electrónico a un proyecto de inicio de ejemplo en https://github.com/iaincollins/nextjs-starter

Tiene soporte de sesión (con Express Sessions en el backend y la sesión del navegador API de almacenamiento para almacenar en caché en el front-end), httpOnly cookies, proyección CSRF, utiliza SMTP integrado para enviar correos electrónicos, un fácil de cambiar el backend que por defecto es SQL Lite . No se requiere configuración para ejecutarlo.

El proyecto también tiene páginas de diseño, rutas personalizadas e incluye el ejemplo de reloj de la wiki. No es el ejemplo más elegante de autenticación, pero podría ser útil para aquellos que buscan comenzar con un proyecto simple que sea fácil de entender y jugar.

Estoy de acuerdo con @iamjacks en que un ejemplo con JWT parece una buena idea.

Me complace mejorar el manejo de errores y agregar características como una página de perfil simple que los usuarios pueden editar e integración de pasaporte con ejemplos de oAuth para Facebook, Google y Twitter si eso fuera útil para las personas. Si la gente tiene buenas ideas sobre mejores formas de propagar / exponer la información de la sesión a los componentes, estoy muy interesado.

Example screenshot showing what to expect

Eso es bastante increíble @iaincollins. Lo presentaremos en las notas de la versión 2.0 con seguridad :)

@rauchg ¡ Gracias! :)

Supongo que debería agregar explícitamente que en este ejemplo las sesiones se basan tanto en el cliente como en el servidor, es decir, funcionan con y sin JavaScript (y en sistemas sin sessionStorage), y la misma sesión se comparte entre ambos.

Lograr esto se vuelve un poco complicado en el componente Session, que profundiza en variables con nombres como req.connection._httpMessage.locals._csrf para obtener el token CSRF de los encabezados del servidor, ya que el objeto 'req' se pasa a las páginas en getInitialProps () es curiosamente un poco diferente del objeto req expuesto en Express, ya que normalmente accedería a él a través de req.locals._csrf (sin embargo, req.session es el mismo en ambos).

localStorage no es seguro. cuál es la mejor manera de hacerlo más seguro. ¡¡Cualquiera puede robar datos de localStorage y volver a ponerlos en su navegador y puede registrarse como usuario víctima !!

@Chathula eso no es cierto. Este argumento es incorrecto.
Si alguien tiene acceso físico al navegador, puede hacer cualquier cosa.

Eso también es cierto para las cookies.

El uso de localStorage para la autenticación es algo seguro porque podríamos deshacernos de los problemas de sec basados ​​en cookies.
Pero, por otro lado, afecta a SSR.

@arunoda he creado un inicio de sesión con Laravel API y Next.js Client. Guardo authUser access_token dentro de localStorage. luego verifique el usuario registrado o no mediante la autenticación. pero no es seguro. si alguien robó los datos de localStorage. él / ella puede usarlo.

si alguien robó los datos de localStorage.

¿Cómo? Básicamente, debería tener acceso físico al navegador. Entonces esa persona podría hacer cualquier cosa.
Entonces, no debemos preocuparnos por eso.

¿No podemos usar ningún cifrado para hacerlo mucho más seguro?

@Chathula, esto se está saliendo del tema. No es exactamente relevante para Next.js y queremos ir con cómo funcionan normalmente las cosas web.

@Chathula puede ser que pueda comenzar un nuevo hilo en el proyecto de inicio anterior.

@arunoda jajaja !! ¡Gracias por tu información! :D

@Chathula Estoy feliz de discutirlo más en un tema, el proyecto de inicio, si tiene inquietudes específicas.

Me gustaría corregir cualquier malentendido, para evitar que la gente se alarme innecesariamente.

La API de almacenamiento web (es decir, localStorage y sessionStorage) es, como las cookies sin httpOnly set, restringida a través de la misma política de origen (protocolo, nombre de host, número de puerto), no es cierto que "cualquiera puede robarlo"; pero sí, si alguien puede ejecutar JavaScript arbitrario en su sitio a través de una vulnerabilidad de secuencia de comandos entre sitios en su aplicación, entonces también puede acceder a la tienda, por lo que no debe almacenar identificadores de sesión en ella.

Es por eso que verá que el token de sesión en sí no está almacenado en localStorage / sessionStorage y no se puede leer en JavaScript, solo se transmite a través de una cookie HTTP Only (por lo que la clase de sesión usa XMLHttpRequest () en lugar de fetch () - como se explica en la documentación de la clase).

Esto significa que incluso si alguien puede aprovechar una vulnerabilidad de secuencia de comandos entre sitios en su aplicación y ejecutar JavaScript arbitrario en su aplicación web, no podrá leer ni exportar un token de sesión de usuario.

Esta es quizás una distinción importante que vale la pena trabajar en la documentación.

Nota: el cifrado adicional de los datos del usuario no es útil aquí porque la aplicación siempre necesita poder leer los datos para que se puedan procesar (por lo que también necesitaría almacenar una clave de descripción en la aplicación, lo que representaría el cifrado del datos del usuario bastante discutibles).

_UPDATE: En la última semana o dos, el ejemplo se refactorizó para usar localStorage sobre sessionStorage, ya que sessionStorage no se comparte entre pestañas y compartir datos no sensibles de esta manera reduce la cantidad de verificaciones de autenticación innecesarias y mantiene el estado de la sesión consistente entre pestañas.

Tal vez sea útil para algunas personas que hice esta aplicación de muestra mientras experimentaba:

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

Respaldado por este backend de juguete:

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

Implementado aquí:

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

Backend aquí:

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

@possibilidades ¡ Gracias Mike! Además de exponer un microservicio separado que muestra una buena forma de manejar páginas seguras que pensaba que podría ser un buen pragma y en el que podría inspirarse. Tengo algunas ideas que surgirán en el repositorio next.js-with-auth.

Después de pensar un poco más, probablemente no consideraría mis esfuerzos como un gran ejemplo. Probablemente cambiaría a que el registro / inicio de sesión envíe completamente el estilo web 1.0 para que podamos establecer una cookie solo HTTP en el servidor (eliminando la accesibilidad al JWT a través de XSS) y luego adjuntar el objeto de usuario a req lugar de que el token completo. Esto deja la posibilidad de vulnerabilidades CSRF, pero creo que esto es más sencillo de mitigar que XSS (¿no?). Un buen efecto secundario de esto es que la aplicación cliente puede usar un flujo casi idéntico al iniciar sesión a través de un servicio de juramento.

También veo en retrospectiva que podría evitar el "middleware" (y por lo tanto la necesidad de un servidor personalizado) analizando la cookie en el Page HoC's getInitialProps .

@possibilidades Dado que la página 'secreta' es solo una página y está incluida en el paquete web, ¿no se enviaría al navegador si voy a / secret?

Sí, supongo que debería estar conectado para hacer una redirección del lado del servidor.

Por cierto, volví a distraerme con mi proyecto inicial de iniciar sesión con github. El flujo es similar con más atención a la seguridad (es decir, no exponer ningún secreto en el cliente para evitar que el token de oauth se exponga a través de XSS). Está vinculado a una aplicación adecuada, pero si hay algún interés, podría dividirlo en algo que podría ser útil para el flujo de oauth en general.

@possibilidades Creo que sería de gran ayuda, si pudiera haber un PR con un ejemplo mínimo (pero adecuado) con auth 🙂 Estoy trabajando en auth en mi aplicación (https://github.com/relatenow/ relacionar) pero actualmente es solo del lado del cliente (localStorage).

@sedubois Tengo un pr para eso usando passwordless.net https://github.com/zeit/next.js/pull/646 pero trasladaremos el servidor de autenticación a otro lugar

¿qué pasa con el uso de graphql?

Apollo da un ejemplo de autenticación graphql:

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

Aquí estamos autenticando una solicitud graphql, pero podría adaptarse a nuestro caso.

Además, graphql puede abstraer la implementación y la lógica. Podría usarse con contraseña sin contraseña, auth0 o cualquier otra cosa que prefiramos.

@impronunciable FYI con tu ejemplo todavía no sé cómo manejar las sesiones, ya que quiero deshacerme de la contraseña. Intentaré adaptar el ejemplo de @iaincollins en mi aplicación.

Mis requisitos son:

  • seguro
  • autenticación del lado del servidor y luego del lado del cliente
  • admite tanto Facebook como inicio de sesión / contraseña
  • los proveedores de autenticación deben cambiarse fácilmente
  • crear usuario en Graphcool
  • autenticar solicitudes de GraphQL posteriores a Graphcool

En caso de que ayude a alguien, aquí está mi aplicación con autenticación del lado del servidor 🙂

La autenticación debería separarse del servidor Next.js, pero estoy esperando a que alguien más me dé inspiración sobre eso ... Además, no estoy seguro de si está protegido correctamente contra CSRF.

Esto es lo que hice:

  • cuando el usuario inicia sesión, javascript del lado del cliente establece una cookie en el navegador que contiene el token de portador de autenticación
  • al realizar solicitudes autenticadas, el código del lado del servidor (next.js) lee el token de portador en los encabezados de la solicitud del navegador y lo usa para contactar al servidor de la API en nombre del cliente

Esto es vulnerable a XSS (incluso si reaccionar hace mucho para prevenirlo) y ataques CSRF, pero es simple y funciona con SSR.

Solo para agregar a las solicitudes

graph.cool + apollo + jwt + auth0 + next.js, las primeras 4 partes de eso ya están hechas en https://github.com/graphcool-examples/react-apollo-auth0-example

@balupton en su ejemplo, ¿qué sucede cuando el token expira mientras el usuario aún está conectado, la sesión acaba de terminar o se renueva de alguna manera?

@nmaro no lo sé, no escrito por mí, es mejor preguntar allí

Para mi propia aplicación, tengo auth con next.js y auth0, luego con un servidor zeit / micro API que verifica los tokens de portador.

Debería poder abrir código en algún momento de febrero.

Siguiendo la misma filosofía que ha estado siguiendo next.js (hacer una cosa y hacerlo bien), he desarrollado el esqueleto de un sistema de cuentas de usuario extensible para node. Vea la filosofía aquí: https://medium.com/the-ideal-system/ooth-user-accounts-for-node-js-93cfcd28ed1a#.97kyfg4xg

El proyecto github está aquí: https://github.com/nmaro/ooth/

El objetivo es tener un servicio de administración de usuarios + autenticación independiente y extensible que sea ideal para ser utilizado como un microservicio separado, una estructura de aplicación que funcionaría muy bien con node.js.

Un ejemplo con autenticación de correo electrónico + contraseña está aquí: https://github.com/nmaro/ooth/tree/master/examples/ooth
Ya existen paquetes para la autenticación de Facebook y Google (ooth-facebook y ooth-google), que deberían ser fáciles de implementar basados ​​en passport-facebook y passport-google respectivamente.

Publicaré un ejemplo de integración de next.js lo antes posible. Siéntase libre de unirse a la discusión y contribuir.

Perdón por el enchufe, pero es por el bien común. Realmente creo que todavía no hay una buena solución para el nodo, y esta es la audiencia adecuada de personas que podrían querer tal cosa. Paz

Mientras tanto ... aquí hay un ejemplo de API graphql que requiere autenticación con un JWT-Token solo para operaciones de escritura. Siéntase libre de usarlo con su método de autenticación favorito :)

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

Actualicé https://nextjs-starter.now.sh para agregar compatibilidad con oAuth.

screen shot 2017-02-10 at 05 03 19

  • Utiliza Passport para oAuth, junto con sesiones exprés (como antes).
  • Hay soporte para Facebook, Google y Twitter + oAuth y es fácil agregar más (ver AUTHENTICATION.md y routes / auth-passport.js ).
  • Utiliza el sistema de sesión universal cliente / servidor (con tokens CSRF, protección XSS a través de cookies HTTP Only, la capa ORM que admite Mongo, bases de datos SQL, Redshift, etc.) como el inicio de sesión de correo electrónico.
  • Poder informar sobre la experiencia de configurar oAuth en portales de desarrolladores es tan terrible como siempre (ocurren errores extraños y es difícil de depurar).

La naturaleza de oAuth (db + sesiones + pasaporte y manejo de errores que necesitan trabajar en estrecha colaboración) y de las sesiones que necesitan trabajar tanto en el cliente como en el servidor y cómo funciona con la representación universal, significa un poco mucho para tratar de resolver de una vez si está tratando de averiguar qué está sucediendo, pero la lógica del cliente no tiene ninguna configuración específica de oAuth, por lo que no es demasiado complicado.

Me complacería dividir solo la autenticación en un ejemplo separado, aunque sospecho que no sería mucho más pequeño. Si alguien más quiere, genial. Probablemente agregaré un poco más al ejemplo en algún momento (como una página de administración de cuentas). Probablemente sería bueno vincular más a la documentación.

¡Increíble trabajo! Si pudieras dividir la autenticación y agregarla al repositorio next.js, sería increíble: corazón:

Yo también puedo hacerlo por cierto 👍🏻

No tendré tiempo durante las próximas 2 semanas, así que si alguien quiere hacerlo, sería genial.

Me encantaría refactorizarlo y ver si se puede reducir a solo un módulo (que expone componentes simples como botones de inicio de sesión y formularios de inicio de sesión para incrustar), y tomar algo de inspiración en el ejemplo anterior realmente agradable del lado del cliente. por @impronunciable.

Actualización: De hecho, me voy, por eso no puedo, pero cuando regrese, ¡estoy feliz de poder hacer eso!

Seguí la guía de inicio rápido auth0 / react con algunas modificaciones, pero cuando llamo a lock.show() , la aplicación se queja:

Error no detectado: addComponentAsRefTo (...): solo un ReactOwner puede tener referencias. Es posible que esté agregando una referencia a un componente que no se creó dentro del método render un componente, o tiene varias copias de React cargadas

@iaincollins @timneutkens con respecto a su ejemplo,

El ejemplo utiliza una cookie httpOnly para almacenar un token de sesión, lo que lo hace seguro contra ataques de injiection javascript (XSS). Luego se toma la molestia de tener un token csrf almacenado en el almacenamiento local para protegerse también contra los ataques CSRF.

Existe la suposición subyacente de que esta combinación de técnicas hace que las cosas sean seguras, lo que podría engañar al usuario / desarrollador. Un atacante aún puede dañar javascript en la página (XSS), leer el token csrf y usarlo para realizar solicitudes autenticadas (cookies) a las API. ¿Vale la pena mencionarlo en el archivo Léame?

Hola @davibe

Por desgracia, actualmente no hay un enfoque técnicamente mejor que una cookie de sesión y un token CSRF giratorio por separado, así que supongo que debo decir que estoy ampliamente satisfecho con el modelo tal como está, ya que en realidad no hay otra forma de hacerlo. (incluso si la implementación real siempre podría mejorarse).

Podríamos agregar la huella digital del navegador, tal vez el token de sesión podría rotar con más frecuencia (olvido si eso es automático en este momento), el token CSRF podría ser un encabezado en lugar de un parámetro, las cookies realmente deberían ser SSL solo en producción y podríamos agregar una fecha de vencimiento para los tokens de inicio de sesión, pero AFAICS hay un margen de mejora bastante limitado en lo que respecta al modelo de seguridad y nada que realmente otorgue una mayor protección en caso de que alguien pueda inyectar cualquier código que desee en una aplicación; pero siéntase libre de plantear esas cosas como problemas para mejoras en el repositorio.

Si hubiera alguna opción de modificación del estado de la cuenta (que no hay actualmente), podríamos tener un CAPTCHA antes de realizarlas para dificultar la realización de cambios en el servidor sin permiso, pero todo lo que sucede en el ejemplo es que los usuarios pueden iniciar sesión. y fuera, por lo que actualmente está fuera de alcance.

El almacenamiento local de tokens de sesión lo haría notablemente menos seguro e iría en contra del uso previsto en la especificación, por lo que personalmente no estoy a favor de eso, aunque aprecio que simplificaría las cosas, ya que no tiene proyección CSRF, pero la gente probablemente No necesito un ejemplo de eso, ya que sería muy sencillo de hacer; solo he intentado proporcionar uno porque es muy incómodo. :-)

Estoy completamente de acuerdo con ustedes en que yo también odio lo necesariamente incómodo que es y no he renunciado a tratar de convertirlo en un módulo.

Hmm ... lo entiendo.
Este número fue muy útil para mí, gracias.

Este es el punto de vista de Auth0 https://auth0.com/blog/cookies-vs-tokens-definitive-guide/
Básicamente, sugieren evitar las cookies, pero creo que eso omitiría SSR en la carga de la primera página.

¡Hola a todos!

También estoy trabajando en la autenticación con next.js y los comentarios de este problema, en particular @davibe + el repositorio de @iaincollins y el PR de @timneutkens han sido de gran ayuda.

Mi solución hace lo siguiente:

  • Tras iniciar sesión correctamente, mi redux reductor guarda el token y los datos del usuario en una cookie utilizando react-cookie .
  • Cualquier página / componente que necesite esa información se incluye en un componente de orden superior similar a @timneutkens with- session.js . Si es SSR, el token estará disponible en ctx.req.headers.cookie; de ​​lo contrario, simplemente cójalo del documento del navegador (usa el método de carga react-cookie).
  • Una vez que tengo el token, puedo configurarlo en los encabezados Portador / Autorización cada vez que hago una solicitud.

Ayuda que tenga mi propio microservicio de autenticación de token JWT ejecutándose en un contenedor de Docker.
¿Quizás sería más fácil proporcionar un servicio JWT simple como parte del ejemplo with-auth? Por lo tanto, ¿evitar piratear el server.js y potencialmente perder los beneficios de la recarga en caliente, el ssr y el enrutamiento de next.js?

Tampoco estoy seguro de si este enfoque es seguro en términos de CSRF / XSS. Cualquier comentario es bienvenido.

Gracias a todos por el increíble trabajo que han realizado hasta ahora. ¡Soy un gran fan de este proyecto!

@jcsmesquita Estoy trabajando en una nueva versión del ejemplo con todo separado de Next.js similar a cómo se implementa auth en zeit.co.

@subsumo Gracias por mencionarlo, FYI: NAP es mío, la autenticación a través de la web se basa en @iaincollins nextjs-starter más reaccionar el inicio de sesión del cliente nativo con token.

@timneutkens, ¿la solución en la que está trabajando le permite autenticarse en un servicio separado?

He estado mirando la solicitud de extracción de ejemplo de autenticación actual https://github.com/zeit/next.js/pull/1141
Me parece que solo permite autenticarse hacia el servidor next.js, pero no isomórficamente hacia un servicio separado.

En otras palabras, suponga que desea separar el servidor next.js y la API de la aplicación real (por ejemplo, REST o GraphQL), entonces lo que quiere hacer es que el cliente y el servidor puedan autenticarse en la API. No creo que esta solución te ayude realmente.

Pensé en un flujo.

Entidades:

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

El objetivo es establecer 3 sesiones basadas en cookies:

  1. CS
  2. California
  3. SA

La sesión 1) es para que el cliente reconozca el servidor, y 2) 3) es para que tanto el cliente como el servidor puedan usar sus respectivas sesiones para acceder a la API de forma independiente.

Este es el flujo:

  1. CS se realiza fácilmente, no se necesita autenticación
  2. CA se realiza con autenticación (por ejemplo, con nombre de usuario / contraseña). Además, se proporciona un JWT
  3. El cliente proporciona JWT al servidor y luego lo descarta
  4. SA se hace con JWT, que luego se descarta

¿Cual es tu opinion? hay una manera mas facil? ¿Deberíamos, en cambio, mantener la API junto con el servidor next.js, de modo que solo se necesite una sesión (CS)?

@rauchg No lo convoco fácilmente, pero creo que esto también se trata de la dirección que debe tomar next.js: como un "front-end universal", ¿debería ejecutarse por separado de la API que proporciona los datos? Si es así, tenemos que hacerlo bien.

@timneutkens, ¿la solución en la que está trabajando le permite autenticarse en un servicio separado?

Esa es la idea, sí. He estado ocupado arreglando otras cosas en Next.js. Volveré a ello lo antes posible.

Rompí partes específicas de github-auth de mi aplicación en un ejemplo bastante digerible. puede ser interesante para algunos y me encantaría recibir comentarios sobre el código o el flujo: https://github.com/possbilities/next-github-auth-example

ACTUALIZACIÓN: Refactoricé la aplicación de ejemplo en un conjunto reutilizable de componentes que se pueden "colocar" en las siguientes aplicaciones.

Seguimiento: escribí una integración con ooth y una API GraphQL.

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

se basa en enfoques existentes, es decir, la autenticación hacia el servidor next.js, es decir, asume que la API y el servidor de autenticación se ejecutan todos en el mismo proceso, por lo que solo se debe crear una sesión. La ventaja: en principio, puede almacenar credenciales para / es extensible a prácticamente cualquier estrategia de passport.js.

@timneutkens ¿ algún progreso en ese frente?

Refactoricé mi aplicación de ejemplo de autenticación de github en un conjunto reutilizable de decoradores y componente de página para "colocar la autenticación de github en" las siguientes aplicaciones. Se agradecen los comentarios sobre el código y la funcionalidad. https://github.com/possbilities/next-github-auth

Creo que podría ser interesante definir los tableros aquí y luego continuar evolucionando esto hacia un marco de autenticación más genérico para el próximo.

@timneutkens
Perdón por hacerte ping, pero ¿has progresado en esto? Estoy completamente perdido sobre cómo debo configurar la autenticación con next.js correctamente.

@kolpav He trabajado un poco en eso, actualmente inundado con otras cosas 😥 Por supuesto, esto es bastante alto en mi lista de prioridades para el Siguiente 😄

Una forma clara, bien documentada y segura de lograr la autenticación con una aplicación next.js es fundamental para su éxito como marco.

No recuerdo la última vez que construí una web que no tenía autenticación.
Como la mayoría de las personas que imagino, también quiero hacer llamadas seguras a mis datos respaldados para descansar, por lo que JWT parece la solución obvia.

¡Pero hay tanta discusión entre los problemas y las relaciones públicas que no estoy seguro de por dónde empezar!

@timneutkens
Genial 👍 Creo que será muy valioso para los demás.

@camstuart @kolpav Hay algunos buenos ejemplos de trabajo anteriores que incluyen la compatibilidad con oAuth y la autenticación basada en correo electrónico que utiliza cookies JWT y HTTP de los colaboradores @jaredpalmer , @luisrudge , @impronunciable , @possbilities y yo.

Para resaltar algunos enlaces, consulte:

(El ejemplo de micro autenticación fue bueno, pero creo que necesita una actualización).

Hay margen para una mejora adicional, que otros comentaristas han comentado anteriormente, incluido un componente de almacenamiento de sesiones y la división de la lógica del servidor; y un ejemplo que simplifica aún más las cosas en las que Tim ha estado trabajando.

La simplicidad para la autenticación es un área desafiante para las aplicaciones universales, pero debería poder poner en marcha los ejemplos anteriores, o simplemente probar las demostraciones directamente, y ver cómo funcionan sin demasiado alboroto.

@iaincollins Son buenos ejemplos. Pero, ¿cómo puedo usar (por ejemplo) el proyecto de inicio? Entonces, si quiero construir mi aplicación. ¿Necesito clonar este repositorio? ¿O necesito "copiar y pegar" el código del proyecto de inicio fragmento por fragmento en mi propio código?

Si se actualizará el proyecto inicial, ¿qué debo hacer?

@iaincollins
Buenos ejemplos, especialmente el tuyo.
Pero aún así, me gustaría ver un ejemplo de autenticación con el sello de aprobación zeit 😄 todos los ojos apuntarían en una dirección para que cualquier error o error no pasara desapercibido. Por ahora, tengo mi propia autorización de trabajo, pero no estoy seguro de qué tan segura es.

De acuerdo @kolpav ,

He creado una pila para esto que puede manejar la autenticación fácilmente con GraphQL: https://github.com/thebillkidy/MERGE-Stack

@salmazov He encontrado una forma útil de comenzar y comprender lo que está sucediendo en un ejemplo o proyecto de inicio puede ser bifurcarlo, luego eliminar las cosas que no son relevantes hasta que se quede solo con el código relacionado con la funcionalidad desea implementar; y luego intentar transferir solo esa funcionalidad a otro proyecto.

@kolpav @camstuart Este hilo contiene una discusión muy extensa sobre varios modelos de seguridad, incluida la desacreditación de algunos conceptos erróneos y compensaciones comunes. En particular, destacaría los puntos sobre las cookies HTTP Only y los tokens CSRF (y la protección adicional que brindan contra XSS y CSRF sobre el uso de JWT y / o la API de almacenamiento web para tokens de sesión). Realmente vale la pena leerlo.

@iaincollins ¿

Hola chicos :)

Tengo una pregunta, estaba leyendo a continuación: no puedo obtener los tokens para la autenticación con jwt de localstorage.

Si tengo un servidor para renderizar solo la primera carga de mi sitio con next. Y tengo otro servidor para mi api. Esto recibe el usuario / pase del cliente y da un jwt. ¿En qué casos necesito obtener el token en el servidor?

¿Por qué necesitaría un token en el servidor de procesamiento (siguiente)?

Si el cliente no envía el token al servidor de la API, la API no proporciona los datos y el usuario no puede obtener información privada. No entiendo por qué necesito enviar el token al servidor de renderizado.

@kamilml

Esto podría ayudar. En general, tiene dos opciones:

  1. Dar el siguiente servidor JWT (posiblemente a través de una cookie). Esto permitirá que el servidor Next realice llamadas a la API en nombre del cliente. Querría esto si la representación completa del lado del servidor es importante para usted.

  2. Almacene JWT en el almacenamiento local del cliente y no le dé acceso al servidor Next. En este caso, simplemente puede omitir las llamadas a la API del lado del servidor y posponer el procesamiento completo hasta que se complete la carga del lado del cliente.

Disculpas por reabrir esto, pero pensé que agregaría mis 2 centavos a este hilo y cómo mi I + D inicial se está desarrollando en esta área. Menos ejemplos de código, más flujo de alto nivel.

Primero, para el contexto, la mayor parte de nuestra aplicación ya está construida en Symfony 3 (PHP) y usa Vue para una experiencia híbrida. El servidor genera una página contenedora y asigna datos de la aplicación a __INITIAL_STATE__ para que la aplicación los recoja. Esto ha resultado en tomar una decisión entre renderizar páginas de marketing en Symfony (para SEO) y elegir UX / UI sobre SEO en otras áreas al obtener datos a través de JS y brindar una sensación más SPA. También vale la pena señalar que no todas las páginas son públicas / privadas binarias (como he visto en algunos ejemplos). Algunas páginas son públicas de forma predeterminada y luego se muestran de manera diferente si se autentican. Estábamos considerando usar un SPA para partes del sitio, pero en muchos aspectos prácticos, era una UX / UI peor (TTI más lento, barras de progreso, etc.). Además, eso no resuelve el problema de SEO, a menos que introduzcamos FOUC y rendericemos el texto dos veces (una vez a través de Symfony, una vez más como componentes JS), etc.

Ingrese SSR / Universal JS ...

En mi caso, lo que hice fue imitar la lógica PHPSESSID , creando una cookie HttpOnly UJSSESSID (inventando el nombre), y estableciendo el valor para el usuario JWT. La aplicación Symfony transmite esto en cada solicitud de página a medida que el usuario navega por el sitio. Cuando el usuario accede a las páginas de UJS, el lado del servidor de esas aplicaciones recibirá las cookies en la solicitud (según el comportamiento integrado del navegador). Si se establece la cookie UJSSESSID , la aplicación llama a la API para obtener la información del usuario (por ejemplo, /api/v1/users/me con pasar el token a través del encabezado Authentication ). El resto de las llamadas se realizan a través de la API, utilizando el mismo token. El mecanismo de cierre de sesión de Symfony borra la cookie UJSSESSID . La próxima vez que se cargue la aplicación UJS, las páginas se mostrarán en modo de usuario anónimo. Para su información, el enrutamiento de la página Symfony vs.UJS se realiza a través de ProxyPass Apache. LocalStorage no se utiliza.

El resultado final es una UX perfecta en la que el usuario está en algunas páginas que son PHP con JS del lado del cliente y algunas páginas que son UJS. Esto nos permite hacer pruebas A / B y actualizar el sitio de forma iterativa; no todo el mundo puede empezar desde cero :)

Si bien esto es un poco más complicado debido al PHP / UJS simbiótico, el mismo principio se puede usar en una solución UJS completa con una API o middleware de servidor Node.js (por ejemplo, Express, Adonis, etc.). En lugar de configurar la cookie UJSSESSID través de una solicitud de página PHP ( HttpOnly bandera), haga que el usuario inicie sesión a través de su SPA / UJS y configure la cookie allí. Lo que NO DEBE hacer es usar su aplicación para decodificar el JWT o realizar llamadas de terceros que requieran client_secret . Utilice un middleware que permanezca en el servidor para eso.

Espero que ayude a alguien. Otros ejemplos que he visto fueron una placa de Petri demasiado pequeña para mí.

@jaredpalmer Hola, gracias por esa implementación, lo probé, simplemente copie y pegue todo su código reemplazando su dashboard.js con mi index.js que se ve así:

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

export default withAuth(index)

y en conAuth hoc lo cambié para redirigir a la página de inicio de sesión.
Pero antes de que redirija a la página de inicio de sesión, el contenido de la página de índice todavía parpadea un poco. :S

¿Cuál es el estado de este problema? 😇

Esto es un poco abrumador para las personas nuevas que leen toda la discusión. Decidí implementar la autenticación muy básica aquí. Solo 2 páginas (índice, inicio de sesión) y un servidor personalizado
https://github.com/trandainhan/next.js-example-authentication-with-jwt

Básicamente, tenemos un middleware de autenticación en el servidor para verificar el token en el encabezado de cada solicitud. El jwt-token se almacenará en cookies. Encuentro que esto es muy simple, sencillo y funciona muy bien.

@trandainhan ¿Podría agregar un punto final POST que use un token secreto para evitar ataques CSRF?

@sbking Código fuente actualizado con un punto final de ejemplo protegido por ataques CSRF

¿Está listo para usar 😬?

¿Alguien probó la autenticación con redux-auth-wrapper ?

¡Hola todos! Durante los últimos meses creé y ahora refiné

Cuenta con:

  • Registro con correo electrónico y contraseña
  • Inicie sesión con correo electrónico o nombre de usuario y contraseña
  • Página de la cuenta donde puede establecer su nombre de usuario, cambiar su contraseña y reenviar un correo electrónico de verificación
  • ¿Olvidó la contraseña / restableció las páginas de la contraseña?
  • Verificar la página de correo electrónico
  • Una API GraphQL básica vinculada a un MongoDB (1 archivo, se puede eliminar fácilmente)
  • Texto estándar mínimo (tanta lógica como sea posible se encapsula en las bibliotecas)

Vea una demostración en vivo aquí: http://staart.nmr.io/

Lo hice principalmente por mí mismo para la creación rápida de prototipos, para comenzar rápidamente con una aplicación con un sistema de cuentas simple y funcional que no depende de servicios externos. Estoy bastante contento con los resultados. Tengo la intención de seguir usando y manteniendo estas bibliotecas, así que pruébelo si cree que aún falta un sistema de cuentas establecido + una interfaz de usuario para el nodo.

@trandainhan Gracias, ese es un gran ejemplo y mucho más simple para mucha gente y funcionaría en muchos escenarios.

Voy a pensar si / cómo puedo adaptar la lógica actual en nextjs-starter para usar algo como esto en su lugar pero de forma segura, sin dejar de ser compatible con la lógica de sesión rápida para los casos de uso del mundo real que tengo (como usar cosas como las API de Google oAuth donde necesito que el servidor mantenga y rastree los tokens otorgados en el primer inicio de sesión).

Aún no he descubierto si eso es posible, pero sería mucho más fácil para la gente si lo fuera.

Si no es así, al menos vale la pena escribirlo en algún lugar para explicarle a la gente las diferentes opciones.

@trandainhan : Si agrego <Link href="/">Home</Link> en login.js, y luego hago clic en el enlace que se genera, puedo acceder a index.js sin iniciar sesión. ¿Cómo sugeriría arreglar esto en su ejemplo?

@iaincollins Veo que la mayoría de las soluciones aquí se autentican contra un servicio Oauth. ¿Existe alguna buena solución para la autenticación frente a una API que se basa en JWT?

@paulwehner Creo que eso sucede porque @trandainhan solo ha manejado el enrutamiento de autenticación del lado del servidor. Todavía no tengo claro cómo funciona el enrutamiento del lado del cliente en next.js porque todo lo maneja bajo el capó el componente next / Link.

@ carlos-peru Bajo el capó, Link en realidad usa next / router para introducir una nueva ruta en el historial del navegador. Parece que la mayoría de las cosas son manejadas por el navegador, no hay nada que hacer aquí para un middleware en el lado del servidor. Hasta ahora, solo puedo pensar en crear nuestro propio componente Link y hacer otras cosas cada vez que cambiemos la URL.

@ carlos-peru Siempre puede usar un servidor personalizado para tener un control total sobre el enrutamiento.

¡Gracias @trandainhan y @kolpav! Debo tener una mejor comprensión después de pasar un tiempo durante el fin de semana. También logré implementar una solución que se basa en HOC que mantiene el token jwt en una cookie, por lo que tanto el servidor como el cliente pueden consumir la API.

@nmaro Tengo un Backend con autenticación JWT para un sitio web en next.js y una aplicación (react-native).

Entonces. Creo que en el siguiente modelo: el cliente obtiene un token de Facebook (el usuario acepta el inicio de sesión de Facebook) y luego el cliente envía al backend el token para verificar y validar el token de usuario (passport-facebook-token en node-js backend) . Luego, si el token es bueno, el backend envía al cliente el JWT generado en el backend.

¿El problema? ¿Tu herramienta puede obtener la clave de facebook y google? Estoy usando hello.js pero no es compatible con next.js y todo el mundo usa passport-facebook. Creo que es un gran error porque el servidor api debe ser compatible con el cliente web y la aplicación móvil.

Gracias

PD: No puedo obtener una invitación en tu canal slack.

@hmontes here : una invitación al canal slack para el proyecto ooth (cuentas de usuario para el nodo, en particular next.js) no dude en unirse. Ooth usa passport-facebook-token (no passport-facebook) y passport-google-id-token que creo que resuelve su problema.

@nmaro quiero ayudarte con tu proyecto. ¡También tengo un backend con Graphql con passport-facebok y passport-google-id-token!

Pero. ¿Conoce un paquete compatible para conectar el cliente de facebook / google en next.js?

En el lado del cliente, podría usar un patrón similar al que usé aquí: 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
(en lugar de oothClient.authenticate simplemente envíe una solicitud de publicación a su ruta de autenticación).

@timneutkens, ¿ sería

@nmaro en tu ejemplo. ¿Por qué utiliza componentDidMount en lugar de componentWillMount?

Estoy creando un componente HoC (proveedor) para el inicio de sesión de Google.

¿Se llama a componentWillMount solo en el cliente? Entonces debería estar bien.

Bueno. Muchas gracias por tus ejemplos. Finalmente puedo implementar la autenticación con las redes sociales.

Última pregunta. Me crearon un access_token y un refresh_token y quiero poner localStorage para guardar los datos.

¿Dónde puedo poner esto en next.js para verificar el inicio de sesión cuando se actualice la página? En create-react-app lo puse en 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}>
....

Gracias

Con ooth, no almaceno los tokens de Facebook, solo los uso una vez con el pasaporte y luego creo una sesión de usuario normal basada en cookies.

¡Ah! Bueno. Estoy usando JWT en lugar de sesiones. Entonces, envío access_token al backend, verifica si el token es válido en el servicio (google, facebook), luego si el usuario existe en mi aplicación y me envía un JWT.

Gracias :)

Debo agregar que es peligroso almacenar JWT en almacenamiento local (debido a XSS), es mejor enviarlos como cookies si todo está en un servidor / dominio, por lo que el código javascript del cliente no puede obtener el JWT, pero el navegador enviará el JWT automáticamente como cookies. Los JWT son complicados (vea las discusiones largas arriba), por eso en ocasiones utilizo sesiones.

Utilizo un JWT solo si ooth se usa como un microservicio de autenticación externo (porque las cookies solo funcionan en el mismo dominio), e incluso entonces lo uso exactamente una vez para luego crear una sesión con el servidor, por lo que no se almacena en ningún lugar del cliente .

Para incrementar la seguridad de JWT, puede usar un token de actualización (tomado de oauth2).

¿Una aplicación móvil puede manejar cookies?

@nmaro Okey. Te entiendo. Perdón por mi error.

¿Tiene un tutorial o un código para guardar el token JWT en una cookie? Estoy buscando https://github.com/zeit/next.js/blob/master/examples/with-firebase-authentication (Mi sistema de autenticación es con graphql)

No, lo siento, nunca hice eso.

@hmontes @nmaro
Escribí esto por mí mismo, pero puedo ayudar:
https://github.com/malixsys/mobazoo

Hola @malixsys, gracias por compartir. Algo que no entiendo de su código: parece que configuraría API_BASE_URL para algo externo, ¿verdad? Si se autentica en esa API, supongo que iniciaría una sesión basada en cookies, ¿verdad? Pero entonces, ¿cómo transfieres esa cookie al servidor next.js, asumiendo que el servidor next.js está en otro dominio?

Ah, no, ya veo, configuras / auth / signin en el mismo proceso. Ok, entonces es básicamente el mismo enfoque que usé para ooth con next.js / staart.

En realidad no, todavía estoy confundido: en / auth / signin devuelves un JWT, pero nunca haces nada con eso en el lado del cliente. Más tarde, "getUserFromCookie", pero no veo dónde colocó la cookie.

@nmaro Solo quería un inicio de sesión básico para trabajar con universal inicialmente. Ambos configuré un jwt y una cookie por ahora. La cookie se usa para hacer universal. Todavía tengo que usar el jwt en otra llamada de axios en este repositorio. Lo hago en una bifurcación privada donde API_BASE_URL apunta a otro dominio ...
Todo está en mi lista de TODO, con PWA.
No dude en abrir un problema o enviarme un ping aquí ...

Sé que este problema está cerrado, pero me gustaría mostrar mi solución.
https://next-auth.now.sh/
Creo que es un poco similar al sitio web de zeit.co

9 de abr

He trabajado un poco en eso, actualmente inundado con otras cosas 😥 Por supuesto, esto es bastante alto en mi lista de prioridades para Siguiente

22 de septiembre # 2974

Estamos planeando lanzar un ejemplo de autenticación oficial pronto, ¿podría publicarlo como un repositorio separado? ¡Thaaaanks!

@timneutkens

Hola, siento molestarte por esto de nuevo, pero ¿podrías compartir cómo es el estado del ejemplo de autenticación oficial? Definitivamente es algo que me encantaría ver y, a juzgar por la cantidad de comentarios en este número, otros también. Entonces, ¿qué tan alto está en la lista de prioridades y debemos hacernos ilusiones? 😄

Hola @kolpav , lo han anunciado oficialmente como en la hoja de ruta para Next.js 5.

Finalmente, estamos agregando algunos ejemplos muy solicitados (como la autenticación de usuario), documentación mejorada para los componentes internos de Next.js y funciones más pequeñas y correcciones de errores.

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

@babenzele Eso es una gran noticia. Debo haberme perdido eso 😅

¿Dónde podemos estar al día con el desarrollo de Next.js 5? Estoy integrando auth0 en este momento, pero sería genial tener una ruta / ejemplo oficial de autenticación de nextjs a seguir

Parece que Next.js necesita desesperadamente un ejemplo de autenticación local oficial utilizando JWT / cookies / localStorage (o una combinación de ellos, siempre que sea seguro y esté protegido de XSS / CSRF) ... Pasé varias semanas tratando de Piense en esto usando un servidor de API Express separado con una estrategia local Passport.js. Probé JWT en una cookie / localStorage para solicitudes sin estado, y también probé una cookie normal con el ID de sesión del middleware de sesión rápida una vez que finalmente abandoné JWT y la apatridia. Terminé teniendo problemas en los que se estaban creando sesiones adicionales en el servidor de la API Express debido a la forma en que funciona la sesión rápida (probado con saveUninitialized: false). Consideré mover el código Express a server.js en mi aplicación Next.js, pero realmente preferiría que fuera un servidor separado. También estoy bastante seguro de que mi implementación no está a salvo de XSS / CSRF o del secuestro de cookies. Necesitamos un ejemplo oficial que cubra las mejores prácticas para la autenticación / inicio de sesión local, o tal vez un módulo oficial como parte de Next.js que se encargará de las complejidades por nosotros.

Mientras esperamos Next.js 5.xy más ejemplos, es posible que desee echar un vistazo a https://nextjs-starter.now.sh, que todavía se mantiene activamente y usa Next.js con Express, Express Sessions, CSRF ( CRSF Tokens), XSS (HTTP solo cookies para tokens de sesión) y utiliza Passport JS para admitir oAuth y correo electrónico.

De hecho, estoy en el proceso de refactorizar el código de autenticación en un módulo, lo que hace que sea realmente fácil agregar autenticación a los proyectos de Next.js de una manera fácil de usar. El módulo debería permitirle usarlo fácilmente con cualquier base de datos que desee, sin tener que copiar un montón de código del ejemplo de Starter Project. Espero terminar con esto esta semana.

Como referencia, el método de "cookie de envío doble" proporciona una manera fácil de agregar protección CSRF si está buscando un enfoque simple pero seguro.

El problema con JWT en localStorage es que siempre se puede leer desde JavaScript del lado del cliente, por lo que es un vector para el secuestro de sesiones a través de XSS, si tiene contenido que no es de confianza (por ejemplo, contenido enviado por el usuario o anuncios).

Si usa cookies HTTP Only para tokens de sesión, o algo como un JWT con un valor de token en HTTP Only (o encripta todo el JWT y descifra usando un token HTTP Only en el servidor), entonces las sesiones están tan protegidas como pueden ser. La idea es solo que, idealmente, los tokens de sesión específicamente no deberían poder leerse a través de JavaScript del lado del cliente.

Por supuesto, muchos sitios no usan cookies solo HTTP porque es una molestia para las aplicaciones de una sola página e invariablemente implica tener alguna lógica de autenticación en la interfaz, pero sigue siendo el enfoque ideal.

Otra opción sigue siendo https://github.com/nmaro/ooth, que se mantiene activamente, ya viene en paquetes y se está utilizando en un par de aplicaciones de producción.

@iaincollins Descargué Next.js Starter Project y comencé a revisarlo. Necesitaré separar las partes importantes relacionadas con la seguridad (XSS / CSRF) e intentar integrarlas en mi aplicación, o esperar hasta que termine el módulo separado. ¿Hay algún lugar donde pueda realizar un seguimiento del desarrollo de ese módulo?

Hola @ kelleg1 ,

El módulo separado ahora se publica como el módulo next-auth para facilitar su uso en otros proyectos. Incluye un proyecto de ejemplo que muestra cómo usarlo.

Para mayor referencia, el proyecto nextjs-starter.now.sh ahora también usa next-auth, que simplifica enormemente el código en el proyecto de inicio, y ahora es mucho más fácil agregar soporte para nuevos proveedores de oAuth o usarlo con diferentes bases de datos ( aunque el ejemplo todavía usa Mongo DB).

Sin embargo, todavía es algo complicado, por lo que si tiene una aplicación existente, es posible que le resulte más fácil usarla como referencia, pero si es así, espero que ayude.

NB: CSRF actualmente todavía está bastante acoplado en él. Utiliza lusca, por lo que asume que res.locals._csrf está configurado, pero diferentes bibliotecas CSRF usan diferentes vars privadas.

Aprecio que aún es más complicado de usar de lo que a nadie le gustaría, pero al menos ahora el código de autenticación finalmente está separado en un módulo para que pueda comenzar a refactorizar. Espero que sea más fácil de usar (con controladores predeterminados para diferentes bases de datos y una configuración más fácil de oAuth) con el tiempo.

@iaincollins , parece que la única dependencia de next.js es https://github.com/iaincollins/next-auth/blob/master/index.js#L342 ? Si es así, sería genial hacer que el lib next.js sea independiente.

@sedubois ¡ Totalmente de acuerdo!

Aunque probablemente sea una buena discusión por sus problemas de repositorio de GitHub en lugar de aquí. 🙂 Si desea sugerir formas en que podría mejorarse, simplificarse y hacerse más genérico, me encantaría colaborar.

(Tener algo que sea tan fácil de usar con el siguiente como sea posible sigue siendo un objetivo principal, pero no veo que deba ser exclusivo, incluso si también termina con las próximas opciones enfocadas específicas).

@timneutkens Felicitaciones por el lanzamiento de nextjs 5. En un futuro cercano, ¿veremos la adición de un ejemplo de autenticación local oficial agregado a la carpeta de ejemplos? ¿O es esto algo que todavía está en proceso para un lanzamiento posterior?

Ooth ahora tiene una documentación completa, que incluye los detalles de la autenticación next.js.

@jaredpalmer Me gusta su enfoque, copié partes de su implementación. Sin embargo, ¿es seguro el método getUser ? ¿Qué pasa si alguien cambia la cadena dentro del almacenamiento local a mano? ¿Sería inteligente que la aplicación se basara en esto? ¿Tendría más sentido cambiarlo a un método que decodifique la parte pública de JWT del token cada vez y lea el estado del usuario desde allí? De esa manera podemos confiar más en este estado debido a la naturaleza JWT de las cosas. ¿Cuál es tu opinión?

Debes guardarlo en una cookie. Escribí que antes, Next tenía soporte para servidores personalizados.

@jaredpalmer ¿Puedes contarme más al respecto? ¿Deberíamos almacenar todo en cookies en lugar de almacenamiento local? ¿Tu HOC también sería diferente? ¿Significa que ahora podemos usar el método getInitialProps para renderizar sitios web protegidos del lado del servidor?

Solo un aviso _si lo que está hablando de almacenar son datos confidenciales en el almacenamiento local / Almacenamiento web_:

"Nunca almacene datos confidenciales mediante el almacenamiento web: el almacenamiento web no es un almacenamiento seguro. No es" más seguro "que las cookies porque no se transmite por cable. No está cifrado. No hay una marca de seguridad o solo HTTP, por lo que no es un lugar para mantener la sesión u otros tokens de seguridad ".

Configure el token en una cookie después de iniciar sesión. use cookie-js. Luego, use el analizador de cookies express en el servidor para que pueda verificar get req.headers.cookies.myToken o equivalente. En getInitialProps de un hoc, verifique si req existe, luego tome el token de req.cookies, de lo contrario consígalo en el cliente de Cookies.get ('mytoken'). En este punto, tendrá acceso a su token en el cliente y el servidor. Luego, desea crear un contenedor / instancia de fetch / axios y fusionarlo con el ctx de next en getInitialProps para que todas sus páginas tengan una forma de realizar solicitudes isomórficas autenticadas. Es posible que también desee obtener su usuario en el hoc. Así que no es necesario que lo repitas en todas partes. Luego puede hacer más hocs si lo necesita para entidades comunes como withUser (withTeam (Page))

getUser es una mala idea, solo debe almacenar el token.

@jaredpalmer Creé un enfoque casi exactamente así y todo funciona bien. El problema que estoy tratando de resolver ahora es cómo actualizar los tokens. La API con la que estoy trabajando tiene tokens de duración relativamente corta (2 horas) y estoy tratando de entender algún sistema para mantener al usuario conectado mientras usa la aplicación.
¿Tiene alguna aportación al respecto?

También puede guardar una solicitud en cada transición de página al no pasar datos a través de next.js para su usuario. Para hacer esto, leería el token de la cookie en server.js e intentaría buscar al usuario y pasarlo al controlador de solicitudes de next. En el documento, obténgalo de los accesorios y guárdelo en JSON en como window.USER. Luego, en su hoc, simplemente lo lee desde la ventana cuando está en la tierra del cliente. Por último, debe usar axios con un interceptor de respuesta que borre inmediatamente su cookie al recibir el código 403 y recarga la página.

@pbrandone Aunque repetitivo, la solución más simple y predecible es enviar su token con cada solicitud, recibiendo un token recién actualizado en el encabezado / cuerpo de la respuesta. Su cliente actualiza su valor conocido en cada ciclo de solicitud / respuesta, otorgando efectivamente tokens de un solo uso que nunca están obsoletos y no se pueden usar incorrectamente.

Por lo general, con este enfoque, el /token/refresh solo se usa para el "despertar" inicial de la aplicación (piense en componentWillMount gancho para <App/> ), verificando si el último guardado el token sigue siendo válido y utilizable.

Nunca tuve que lidiar con tokens de actualización, pero me gustaría intentar hacerlo en un interceptor axios. Tendría que pensarlo más, pero en teoría interceptaría una solicitud incorrecta, luego usaría el token de actualización para obtener un nuevo token, establecería la cookie con el nuevo token y volvería a reproducir la primera solicitud con el nuevo token. Los interceptores Axios son realmente poderosos porque le permiten mantener cosas como esta abstraídas y fuera de la vista de su código de producto. Es posible que deba escribir un interceptor de solicitud y respuesta y / o mantener algún tipo de objeto / mapa con estado en progreso que podría complicarse. Ojalá eso ayude

O haz lo que dijo Luke. Más fácil.

Si desea actualizar los tokens, deberá interceptar la cookie (si está utilizando cookies) a través de una configuración de servidor personalizada. Hice esto usando express. De lo contrario, puede hacerlo todo del lado del cliente utilizando tokens JWT. Como estaba haciendo el mío a través de cookies, tuve 2 casos de uso: manejo de solicitudes de navegación en el lado del cliente y solicitudes de página en el lado del servidor (cada vez que alguien escribe manualmente una URL o actualiza la página). Pero básicamente son el mismo flujo simplemente se ejecuta de manera diferente ya que uno está en el lado del cliente y el otro en el lado del servidor. En el lado del servidor, desde que usé Express, acabo de crear un middleware para procesar la cookie, decodificar el JWT dentro de ella y verificar la caducidad. Si venció, solicite obtener un nuevo token y continúe pasando el usuario decodificado a redux en el cliente. En el cliente, tengo un contenedor HOC para enlaces seguros que está constantemente buscando al usuario, cada vez que navega en algún lugar del cliente. El HOC obtendrá su cookie o JWT (en mi caso, mi JWT está en una cookie) y comprobará si sigue siendo válido rápidamente. Si no es válido, intentará obtener un JWT / Cookie actualizado y continuar; de lo contrario, cerrará la sesión. Sí, más complicado, pero también más seguro. ¡Espero que ayude! No dude en hacer preguntas si está interesado; me tomó un tiempo averiguarlo todo. Personalmente, me pregunto si las personas que inician sesión con Github o FB, etc., probablemente estén bien para muchos casos, pero en algunos casos simplemente no son profesionales. Todavía no he visto un banco, atención médica, ninguna de mis facturas que pago, etc., inicie sesión con mi cuenta de FB. Sin embargo, podría ser solo cuestión de tiempo;)

En cuanto a los tokens de acceso refrescantes;
No estoy seguro de si esta es una forma adecuada de hacerlo, pero mis tokens de acceso duran muy poco (un minuto más o menos). Para hacer frente a los problemas de caducidad del token del lado del cliente, he adoptado este enfoque;
Cada vez que se carga una página, comprueba si el token está caducado, si es así, lo actualiza. Sin embargo, si todavía es válido, llama al método que verifica cuánto tiempo hasta el vencimiento y establece el tiempo de espera que actualizará el token de acceso al vencimiento. Y luego repite este proceso mientras el visitante esté en el sitio.

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

Esto devuelve milisegundos hasta el vencimiento:

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

Cualquier comentario sobre este enfoque sería muy bienvenido.

@kunokdev - Creo que es un buen comienzo. Moví una función similar.Tuve que calcular el tiempo restante a una función que acababa de devolver un booleano dependiendo de si estaba vencida o no y eso hizo que mi código fuera un poco más legible y simple, además de que no necesitaba preocuparme por restablecer los temporizadores. Luego, solo verifico si el usuario ha expirado o no en algún tipo de solicitud que requiera autenticación. Si tuviera un temporizador de cuenta regresiva o algo así que fuera visible para el cliente o depurando, entonces su función sería ideal. Esos son mis 2 centavos 👍

¿No debería el ejemplo de autenticación / inicio de sesión ser el más básico posible? es decir, almacenamiento de memoria y sin JWT o tokens extraños. Por lo general, cuando piensa en alguien que "inició sesión", significa que tiene una cookie / sesión activa.

Tengo un cliente y servidor de trabajo HOC de inicio de sesión. Utiliza un JWT y un backend (puede ser un nodo, django, lo que sea).

Compruébalo y cualquier comentario será muy apreciado.

https://github.com/hugotox/AppStarter

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

@hugotox Me gusta la idea de usar la tienda Redux para almacenar datos de autenticación, pero cuando llames a store.dispatch desde getInitialProps varias veces, prepárate para efectos secundarios no deseados, cuando el componente decorado se renderizará incluso si el proceso de autenticación aún no ha terminado. Tomemos un ejemplo de uso de su código:

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

En getInitialProps usted llama verifyToken antes de verificationOk , entonces MyPage.mapStateToProps se llamará 2 veces (si escucha store.auth.user) y la primera vez store.auth.user será nulo incluso para una página que requiera un usuario conectado.

Ayer también lancé la primera versión WIP del propio kit de inicio con tecnología Next.js, pero comencé con Docker, Flow y fast-redux: https://github.com/dogada/microchain

Utilizo allí imágenes de Docker dedicadas para el servidor API y la aplicación web que pueden ejecutarse en diferentes dominios, por lo que sigo buscando una solución que funcione que permita iniciar sesión a usuarios reales pero también emitir tokens de portador OAuth para clientes móviles, por ejemplo.

@spencersmb ¡ excelentes saludos! Esta es exactamente la forma en que lo estaba haciendo. En términos de la representación del lado del servidor para una URL la primera vez que la visita (nunca inició sesión), ¿la está interceptando, viendo que no hay cookies y redirigiendo a una página de tipo pages / login.js, por ejemplo?

@ james-ff Gracias por publicar eso, incluso si se siente como gritar al vacío.

Se ha dicho en este hilo varias veces, pero aparentemente todos siguen ignorando que no deberían almacenar tokens de sesión en JWT con localStorage o en cookies accesibles de JavaScript y parece intentar reinventar las sesiones y la autenticación de una manera menos segura. (y eso requiere JS del lado del cliente). 🙃🤦🏻‍♂️

El autor de esta publicación escribió muy bien lo que está mal con eso en 2016, pero lamentablemente las cosas no parecen haber mejorado:

Desafortunadamente, parece que encontré el límite superior en la longitud del artículo antes de que la gente deje de leer; muchos de los comentaristas en Reddit y Hacker News siguieron sugiriendo las mismas "soluciones" una y otra vez, ignorando por completo que ya se abordaron y no resultaron prácticas. en el propio artículo.

Sin embargo, tanto el artículo original como el diagrama de seguimiento son bastante buenos.

Los JWT de @iaincollins y las aplicaciones / autenticación sin sesión tienen sus ventajas y muchas personas / empresas (incluido Google) todavía quieren usarlas. Habiendo leído el caso contra los JWT , todavía creo que debería haber dos ejemplos / bibliotecas oficiales de next-auth, uno usando sesiones (como next-auth) y otro usando JWT, o tal vez solo uno que permita usar cualquiera. Las ventajas, desventajas y advertencias deben explicarse claramente para cada una en las páginas de tutoriales oficiales de Next.js y / o en la documentación del módulo en alguna parte. Hasta que alguien invente una mejor plantilla / biblioteca, probablemente continuaré usando nextjs-starter y next-auth, ya que son las mejores que he encontrado.

Solo he estado controlando esto de manera casual, pero mi experiencia con la autenticación universal de JS es almacenar un JWT en una cookie HttpOnly . Realmente no hay razón para usar localStorage cuando tiene la opción de usar el lado del servidor de la aplicación para almacenar la cookie. Si necesita acceso al JWT para llamadas de API de origen cruzado en el navegador, puede pasar el JWT en un escenario de tipo __INITIAL_STATE__ (tenga en cuenta las vulnerabilidades XSS, pero al menos no está "almacenando" es del lado del cliente). Para el acceso del mismo origen, la cookie se pasará de un lado a otro (asumiendo que está usando withCredentials para axios, o credentials: 'include' para buscar), anulando la necesidad de poner el JWT en JS en absoluto. . Puede usar un proxy en el lado del servidor de la aplicación para tomar el JWT de la cookie HttpOnly , _entonces_ hacer sus llamadas API de origen cruzado también. También niega la llamada previa al vuelo en ese escenario. Para cada uno, pero personalmente no creo que localStorage sea necesario para aplicaciones universales.

@bjunc Sí, de todas las opciones sobre dónde se debe almacenar un JWT (localStorage vs.HttpOnly cookie vs.Redux, o lo que sea), podría estar equivocado, pero creo que la respuesta debería ser básicamente siempre una cookie HttpOnly. Esto parece haber sido declarado innumerables veces en numerosos blogs y foros. (No estoy seguro acerca de los tokens de actualización, ¿quizás también deberían almacenarse en una cookie HttpOnly?) Creo que dónde se almacenan los JWT debería ser un asunto resuelto aparte del resto de las ventajas / desventajas de los JWT.

Traté de hacer más o menos exactamente lo que dijiste en un proyecto mío antes de comenzar a adoptar nextjs-starter y next-auth. Ha pasado un tiempo, pero si recuerdo correctamente, creo que el problema que encontré fue que mi servidor de API Express con el que me estaba autenticando (que estaba usando Express-Session) no estaba inicializando / recuperando la sesión correctamente. Obtendría un comportamiento extraño en el que las sesiones se inicializarían varias veces. Tengo la intención de continuar haciendo más o menos lo que describiste si puedo arreglar eso. También continuaré trabajando con nextjs-starter y next-auth, ya que las sesiones eliminan muchas de las otras preocupaciones que plantean los JWT, como cómo invalidar tokens.

Una vez más, sería útil un ejemplo (o ejemplos) oficial. Palabra clave: oficial, lo que significa que los autores de Next.js han considerado todas las posibilidades e incorporado las mejores ideas y prácticas. Lo ideal sería utilizar las funciones modernas de ES6 / ES7 / ES8, como promesas y asincrónica / espera.

@ kelleg1 parece que sus problemas están relacionados con las características específicas de la creación de cookies. Por ejemplo, puede crear accidentalmente varias cookies con el mismo nombre utilizando diferentes configuraciones ( HttpOnly , dominio, ruta, vencimiento, etc.); lo que podría crear efectos secundarios extraños como los que está describiendo. Una forma de depurar es utilizar las herramientas de desarrollo Aplicación-> Cookies. Si ve un montón de cookies con el mismo nombre, eso podría indicarle la dirección correcta.

De todos modos, no estoy en el equipo central, así que no puedo ayudar con una contribución "oficial" (y de hecho, estoy usando Nuxt.js, no Next.js), pero los principios son los mismos. Experimenté con algunas formas diferentes de hacer esto (sopesando los pros / contras de JWT, cookies, CSFR, websocket CSWSH, localStorage, etc.). Finalmente llegué a la conclusión de que la naturaleza universal de Next / Nuxt se prestaba bien al uso de cookies HttpOnly JWT. Quizás otros llegarían a una conclusión diferente, pero yo personalmente no estoy en el campo de "oh Dios, no uses JWT, ¿¡no leíste ese artículo que dice que JWT te da cáncer !?".

@iaincollins siento traer esto de vuelta de nuevo, pero cada tutorial en la web usa localStorage para guardar el token y estás diciendo que es inseguro. Si es así, ¿dónde se supone que debemos almacenar el token?

¡GALLETAS! :D
usamos algo como esto:

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

Un poco torpe pero bastante seguro ya que el javascript del lado del cliente nunca ve un token y la cookie es httpOnly, segura, firmada y enviada automágicamente por axios u otro ...

Hola a todos, acabo de crear un ejemplo de autenticación con cookies y redux. Vea aquí https://github.com/zeit/next.js/pull/4011

Espera, ¿dónde terminó este problema? Nunca pude encontrar un ejemplo de autenticación Express en el repositorio oficial.

Veo que se habla mucho de cookies y sesiones, pero ¿cómo funciona eso para mi aplicación móvil nativa cuando necesita acceder a la API?

Se puede hacer, pero para eso es JWT: p

Acabo de hacer una lectura detallada de todo este hilo y me siento obligado a resumir mis hallazgos.

Parece que hay dos proyectos iniciales viables con módulos de autenticación sólidos factorizados:

¡Excelente trabajo de @iaincollins y @nmaro !

Gracias @curran :)

Documenté todos los cambios de código que hice para eliminar los aspectos extraños de nextjs-starter en esta solicitud de extracción https://github.com/iaincollins/nextjs-starter/pull/86

Esto puede estar acercándose a un ejemplo sólido de autenticación básica para el repositorio de Next.js.

Recientemente tuve un requisito para implementar OAuth con Office 365, así que pensé en compartir un ejemplo muy simple que arrojé aquí . Necesita trabajo (y no está tan desarrollado como algunos de los ejemplos anteriores) pero creo que podría generalizarse eventualmente para usar también varios clientes OAuth. Utiliza varios de los ejemplos que ya se muestran en el repositorio de ejemplos de Next.js, así como el hilo de rutas protegidas aquí . En cualquier caso, pensé que lo compartiría en caso de que alguien quisiera un ejemplo rápido de cómo (tal vez) hacer esto con Microsoft.

Para cualquier persona interesada en una autenticación simple con token de JWT que funcione en el lado del cliente y del servidor, obtuve ayuda en el chat de Spectrum y pensé que la compartiría con todos ustedes. Siempre se agradece cualquier comentario.

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

¡Hola tios!
Aquí hay otro ejemplo de autenticación con next.js que construí hace un par de meses, tal vez alguien lo encuentre útil:

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

Ooth 2.0 está disponible con nuevos y mejores documentos

Ooth es un sistema de gestión de identidad de usuario creado para node.js (con next.js en mente).

Estrategias soportadas actualmente:

  • Principal: nombre de usuario / contraseña (incluido verificar el correo electrónico, olvidé mi contraseña, etc.), invitado, facebook, google
  • Secundario: sesiones basadas en cookies, JWT

Vea este ejemplo en vivo que lo pone todo junto con next.js ( código fuente ).

Muchas de las muestras de autenticación aquí (y en la carpeta de ejemplos) devuelven la sesión de usuario / token / etc de getInitialProps . Por lo que tengo entendido, cuando la página se representa en el lado del servidor, esto significa que la información de la sesión del usuario se enviará como parte de la respuesta de la página HTML (como parte de NEXT_DATA ) al navegador.

Veo dos problemas de seguridad con este patrón cuando getInitialProps se ejecuta en el lado del servidor:
1) La sesión del usuario se transmite a través de la red desde el servidor al navegador. Si cualquiera de estas solicitudes utiliza http:// y no https:// , el token del usuario, etc., se expondría a través de la red en texto sin formato.

2) La sesión del usuario se devuelve del servidor al navegador como parte de la página HTML (en una etiqueta de script NEXT_DATA ). Tener el token / etc del usuario directamente en la página HTML parece arriesgado, especialmente una vez que la página es analizada, representada por el navegador y es posible que ahora se estén ejecutando otros scripts de terceros.

¿Son estos problemas que ya se han abordado? ¿Existen mitigaciones para estas amenazas?

Por eso utilizo cookies. Vea mi ejemplo aquí https://github.com/hugotox/nextjs-starter/blob/master/pages/_app.js

¡Finalmente! Aquí puede encontrar un ejemplo de autenticación de next.js completamente acoplado con los siguientes contenedores:

  • next.js
  • microservicio de autenticación (ooth)
  • api (graphql)
  • almacenamiento de sesiones (redis)
  • pequeño proxy inverso

Todo se combina con docker-compose.

Nota rápida sobre los JWT. Los JWT tienen las ventajas de ser apátridas, buenos para dispositivos móviles y buenos si necesita pasar credenciales de un dominio a otro. La principal desventaja es que exponen el navegador a XSS. Para este ejemplo, opté por una solución basada exclusivamente en sesiones de cookies. Todavía logré dividir las cosas en microservicios gracias al proxy inverso (todos los microservicios se ejecutan bajo el mismo dominio) y al almacenamiento de sesión compartido.

https://github.com/nmaro/staart/tree/master/examples/staart
El ejemplo en vivo es, como de costumbre: https://staart.nmr.io

Creo que es prudente aclarar que el uso de JWT no te "expone" intrínsecamente a XSS. Por el contrario, si su sitio tiene una vulnerabilidad XSS (no debido al JWT en sí), un JWT podría verse comprometido (junto con cualquier otra información accesible por script); mientras que las cookies httpOnly no serán accesibles. ¡No importa que puede usar un JWT como el valor de una cookie httpOnly !

Las soluciones de solo cookies pueden funcionar bien para la comunicación del mismo dominio, pero si tiene una API sin cabeza (por ejemplo, example.com llamando a api.example.com ), entonces esa no es realmente una solución a menos que desee un navegador proxy solicitudes desde example.com a api.example.com haciendo sus llamadas API a example.com y reenviarlas con la cookie adjunta a la solicitud (que viene con su propio conjunto de pros / contras) .

Personalmente, creo que las desventajas de JWT son muy exageradas y se mitigan con bastante facilidad a través de una gran cantidad de protecciones comúnmente implementadas. No menos importante, una lista negra de tokens que hace referencia al reclamo jti (por ejemplo, UUID Version4) en el caso de que el token se haya visto comprometido antes de su vencimiento.

Hola @bjunc, sí, gracias por

¡No importa que puede usar un JWT como valor de una cookie httpOnly!

Sí, me gustaría agregar que eso elimina la única ventaja de JWT de transferir credenciales entre dominios.

una lista negra de tokens

Eso, y la otra práctica común de un token de actualización, eliminan la otra ventaja de JWT de ser realmente apátrida.

Esta es mi comprensión de la situación:

  • Si necesita autenticación de ubicación cruzada, use JWT <- pero si es posible evítelo por razones de seguridad
  • Si necesita autenticar desde cualquier cliente que no sea un navegador y no tenga el problema XSS (por ejemplo, una aplicación de escritorio o móvil) use JWT
  • De lo contrario, use sesiones basadas en cookies, porque una solución basada en JWT será menos segura (XSS) o no será realmente apátrida (listas negras), o requerirá que use un proxy para mantener todo bajo el mismo dominio de todos modos.

Escribí un artículo de Medium sobre Autenticación / Cuentas de usuario de Next.js.
Es un tutorial extenso y mi cerebro de casi dos años de desarrollo y pensamiento en el tiempo libre (mi primer comentario sobre este tema es de febrero de 2017).

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

Sigues correlacionando JWT con ser menos seguro. JWT no hace que su sitio sea menos seguro. No es en sí mismo una vulnerabilidad XSS. Si tiene un exploit XSS, tiene el mismo problema de todos modos. Incluso con una cookie httpOnly , es posible que el atacante no pueda leer el valor de la cookie, pero no importa porque puede ejecutar código arbitrario, como solicitudes XHR que pasarían automáticamente las cookies de sesión (actuando esencialmente como un ataque CSRF). Si su API es de dominio cruzado y está realizando solicitudes de navegador a servidor, entonces el JWT está en algún lugar del código de todos modos (ya sea en el estado inicial o en el almacenamiento local). Si usa Axios, es posible que incluso haya establecido valores predeterminados globales, donde el atacante solo necesita hacer una solicitud de Axios y ni siquiera preocuparse por la autenticación.

Además, no se puede hablar realmente de XSS sin hablar también de CSRF; donde las cookies de autenticación se dirigen específicamente. Incluso con una configuración de proxy inverso, un ataque CSRF permitiría al atacante ejecutar solicitudes autenticadas contra su API.

Por cierto, solo porque coloque el JWT en una cookie (por ejemplo, saber si el usuario ha iniciado sesión), no significa que no pueda usar el valor de la cookie (por ejemplo, un JWT) para servidor a dominio cruzado Acceso al servidor API en la carga de la página (que también funciona para el escenario de proxy inverso). Tampoco está impedido de pasar el JWT en el estado inicial también para las solicitudes de navegador a servidor API. No son mutuamente excluyentes.

Aparte, encuentro la idea de JWT "sin estado" tanto sobrevalorado como limitado en la aplicación para la mayoría de los casos de uso. Por ejemplo:

  • Permisos basados ​​en recursos / dinámicos (por ejemplo, no solo " can edit Post ", sino más bien " can edit Post:1634 ").
  • ¿Qué pasa si la cuenta del usuario ha sido bloqueada / eliminada?
  • No ha pagado su suscripción mensual; ¿Qué funcionalidad acelera?
  • ¿Incluiste el token en la lista negra (como se indica arriba)?

No está introduciendo todo eso en el JWT, lo que significa que debe sumergirse en la capa de dominio (es decir, la base de datos) para averiguarlo. Acaba de cargar el recurso, por lo que también podría colocarlo donde el resto de la aplicación pueda acceder a él, y ahora tiene el estado. Me parece realmente tonto pensar que todo lo que necesitas saber sobre el tema estaría integrado en la ficha; mientras que al mismo tiempo lo mantiene en su mayoría anónimo y ligero. Sin divagar demasiado, hay un argumento a favor de las solicitudes "sin estado" en la comunicación entre servicios, pero incluso eso me parece poco práctico (al menos en lo que respecta al concepto de incluir en el JWT lo que necesita saber sobre el tema). .

Mientras tanto, hay otras estrategias de autenticación disponibles (nuevas en negrita):

  • Local (nombre de usuario / correo electrónico / contraseña)
  • Facebook
  • Google
  • Huésped
  • Patreon
  • Gorjeo
  • Authy (Twilio): sin contraseña a través de SMS

@jaredpalmer que escribiste
Como php, la unidad atómica de Next es la página. Una de las características más interesantes es que carga de forma diferida cada página solo cuando se solicita. Con la autenticación solo del lado del cliente pero con la representación del servidor, el navegador descarga el js para esa página protegida. En el futuro, cuando Next agregue flujos de trabajo del servidor, es de esperar que pueda bloquear el procesamiento y redireccionar en el servidor para evitar esto por completo. Esto requerirá cookies, sesiones y tiendas de sesiones AFAIK, pero ese es solo el costo de hacer aplicaciones híbridas como estas.

Estamos 2 años después. ¿Existe un flujo de trabajo del servidor para evitar la carga de js para páginas protegidas?
@timneutkens ¿ quizás está
¿Cómo puedo evitar por completo el acceso a contenido protegido?

@lishine Tiene una ServerResponse en el getInitialProps de su página; puede redirigir fácilmente a alguien sin privilegios.

¿Hay un ejemplo de autenticación con redux?

¿Hay un ejemplo de autenticación con redux?

Puede probar este ejemplo que usa redux, y verifique si funciona para usted ...
Puede encontrarlo en algún lugar de este tema, pero en caso de que no pueda encontrarlo, aquí está:
https://github.com/alan2207/nextjs-jwt-authentication

Creo que este es un problema más complicado cuando se utilizan los resultados de la llamada de la API del lado del servidor getInitialProps, porque Virtual DOM usa resultados antiguos después de la acción LOGOUT-LOGIN. Estoy pensando en una solución

_EDITADO_
y aquí está mi respuesta con redux-observable

| Side | Auth | TODO |
| --- | --- | --- |
| Servidor | verdadero | Obtener datos iniciales (con proxy de cookie de solicitud). |
| Servidor | falso | Mostrar página de inicio de sesión y recuperar datos después de iniciar sesión. |
| Cliente | verdadero | Obtener datos iniciales. |
| Cliente | falso | Mostrar página de inicio de sesión y recuperar datos después de iniciar sesión. (Esto sucede solo cuando la sesión expiró en el movimiento de página a 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 cuando algo simple funcionaría, en la línea de:

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 ejemplo se fusionó como parte de Next.js 8
https://github.com/zeit/next.js/tree/canary/examples/with-cookie-auth

@timneutkens gracias por el enlace.

mirando https://github.com/zeit/next.js/blob/canary/examples/with-cookie-auth/www/utils/auth.js#L26 -L34 ... ¿no debería haber algún tipo de comprobar después de llamar a auth() ?

Probar el ejemplo sin una cookie lleva a que se llame a Profile.getInitialProps() , mientras que pensé que la redirección ocurriría incluso antes de intentar obtener más "accesorios iniciales" ...

Hice un ejemplo aquí que tiene pre-renderizado + autenticación del lado del servidor con apollo

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

Tenga en cuenta que las pautas de seguridad de OWASP recomiendan no almacenar el token JWT en el almacenamiento local, es decir, "Se puede usar un solo Cross Site Scripting para robar todos los datos en estos objetos, por lo que nuevamente se recomienda no almacenar información confidencial en el almacenamiento local".

Aquí está Auth0: dónde almacenar tokens y Tom Abbott: dónde almacenar sus JWT: cookies frente a almacenamiento web HTML5 .

Aquí hay un ejemplo con Nuxt.js + servidor proxy Express.js + backend Django. Donde el servidor Express se usa para enviar solicitudes de autenticación al backend real y maneja la protección CSRF cuando se usa el token JWT que se almacena en una cookie (impone alguna restricción en la longitud del token / cuánta información se puede almacenar en el token JWT): https: / /github.com/danjac/nuxt-python-secure-example

@timneutkens Necesito algunos documentos sobre cómo enviar token desde la cookie 🍪 al middleware SSR personalizado redux. Recibo las cookies dentro de _app.js. Pero, ¿cómo debo pasarlo a customApimiddleware. Donde he escrito solicitudes de recuperación. Gracias

Escribí un artículo de Medium sobre Autenticación / Cuentas de usuario de Next.js.
Es un tutorial extenso y mi cerebro de casi dos años de desarrollo y pensamiento en el tiempo libre (mi primer comentario sobre este tema es de febrero de 2017).

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

Creo que este es uno de los mejores tutoriales para manejar la autenticación en una aplicación nextj.js. He visto cosas como almacenar tokens en localStorage (XSS), almacenar tokens en cookies (sin manejar CSRF) e incluso almacenar tokens en cookies del navegador (tanto XSS como CSRF son vulnerables).

Realmente me gusta su solución con el proxy inverso y compartir la información de la sesión entre diferentes servicios. Realmente me gustaría no crear un servidor personalizado para la aplicación next.js, pero creo que es la forma más sencilla de manejar sesiones y prevenir csrf (y tal vez agregar el proxy inverso). Incluso puedo terminar creando un proyecto monolito (tanto para renderizar la aplicación como para manejar operaciones de base de datos, etc.).

He visto que algunas personas (incluido ZEIT) mantienen las API sin estado y dejan que la aplicación next.js maneje la sesión. Pasa los tokens a las API. Pero ir con sesiones solo hace que las cosas sean un poco más ajustadas y menos complicadas.

Sería realmente mejor tener un ejemplo de autenticación completo para next.js. Con cosas como la autenticación para API externas, mantener la sesión en la aplicación next.js, compartir sesiones entre servicios o pasarles tokens, y tal vez incluso actualizar los tokens si están vencidos. (Mucha gente escribe mucho sobre JWT y simplemente los usa en sus tutoriales, pero en su mayoría ni siquiera los vencen o ni siquiera los actualizan).

De todos modos, has escrito uno de los tutoriales más completos sobre este tema. ¡Así que gracias!

Realmente espero que haya ejemplos y documentación mucho más completos y claros.

Sería realmente mejor tener un ejemplo de autenticación completo para next.js. Con cosas como la autenticación para API externas, mantener la sesión en la aplicación next.js, compartir sesiones entre servicios o pasarles tokens, y tal vez incluso actualizar los tokens si están vencidos. (Mucha gente escribe mucho sobre JWT y simplemente los usa en sus tutoriales, pero en su mayoría ni siquiera los vencen o ni siquiera los actualizan).

Yo también estoy perdido en cuanto a qué enfoque elegir.
Muchas gracias por el enlace al artículo.
Actualmente, ¿en cuál de las implementaciones te has decidido?
¿Ha encontrado un ejemplo de autorización completo para la próxima v9.3 +?

Vale la pena revisar el nuevo enfoque basado en cookies de Auth0
(Por supuesto, esto es para un proveedor de identidad en particular, pero podría ser útil ver el enfoque)
https://github.com/auth0/nextjs-auth0

  • Realmente genial que pueda "proxy" solicitudes de API a través de las rutas de API de nextjs (incluso a través de una ruta dinámica)
  • Entonces nunca tendrá que exponer tokens de acceso, etc.al lado del cliente (ya que las rutas de la API de nextjs solo se ejecutan en el lado del servidor), el lado del servidor puede obtener tokens de acceso, etc.a través de la biblioteca auth0 y la cookie con un secreto
  • El código del lado del cliente llamaría a las rutas de la API de nextjs, y las rutas de la API realizarían la solicitud real de la API

Tenga en cuenta que dicen que este enfoque es "experimental" en el archivo Léame.

Vale la pena revisar el nuevo enfoque basado en cookies de Auth0
(Por supuesto, esto es para un proveedor de identidad en particular, pero podría ser útil ver el enfoque)
https://github.com/auth0/nextjs-auth0

  • Realmente genial que pueda "proxy" solicitudes de API a través de las rutas de API de nextjs (incluso a través de una ruta dinámica)
  • Entonces nunca tendrá que exponer tokens de acceso, etc.al lado del cliente (ya que las rutas de la API de nextjs solo se ejecutan en el lado del servidor), el lado del servidor puede obtener tokens de acceso, etc.a través de la biblioteca auth0 y la cookie con un secreto
  • El código del lado del cliente llamaría a las rutas de la API de nextjs, y las rutas de la API realizarían la solicitud real de la API

Tenga en cuenta que dicen que este enfoque es "experimental" en el archivo Léame.

Este artículo es muy útil y cubre muchas arquitecturas diferentes.
https://auth0.com/blog/ultimate-guide-nextjs-authentication-auth0/

Usar rutas API como proxy, iniciar sesión / cerrar sesión a través de rutas API, obtener el token de API, configurarlo como cookie HttpOnly es un enfoque sólido, creo.
Una preocupación podría ser CSRF, pero puede crear fácilmente alguna solución con el paquete csrf npm (no csurf , pero también podría funcionar).

@onderonur , gracias por el artículo de auth0.
es decir, en la actualidad hay un ejemplo confiable o al menos mínimo de implementación en jwt puro con next.js?
No quiero hacer una capa avanzada con cookies y configurarlas. En la aplicación csr, simplemente almacenamos el token en localstorage y lo enviamos junto con la solicitud.

@onderonur , gracias por el artículo de auth0.
es decir, en la actualidad hay un ejemplo confiable o al menos mínimo de implementación en jwt puro con next.js?
No quiero hacer una capa avanzada con cookies y configurarlas. En la aplicación csr, simplemente almacenamos el token en localstorage y lo enviamos junto con la solicitud.

He usado este método para uno de mis repositorios pero todavía está en borrador, así que asegúrese de probarlos usted mismo :)
https://github.com/onderonur/post-gallery
En realidad, la "capa de cookies" no es una cosa avanzada. Simplemente llame al punto final de inicio de sesión de su API a través de la ruta /api/login API y, si la solicitud es exitosa, configure el token en una cookie httpOnly .
Puede consultar mi repositorio para ver exactamente la misma implementación.

Otra opción es (si desea tener casi el mismo flujo que configurar el token en el almacenamiento local), puede usar el paquete js-cookie npm, llamar a su punto final de inicio de sesión con una solicitud del lado del cliente, finalizar si regresa un token, ponerlo en una cookie. Y cuando realiza una solicitud (a través de un interceptor axios, etc.), lea el valor de la cookie y páselo a su API como un encabezado de solicitud. He visto muchas aplicaciones (e incluso algunas populares) que utilizan este enfoque. Pero esto no es un poco seguro. Porque no puede configurar httpOnly cookies en el navegador. Por lo tanto, JavaScript podrá leer su cookie de token. Por lo tanto, habrá vulnerabilidad XSS.

Aprecio que este es un hilo antiguo (y un tema de larga duración en general), pero para aquellos que buscan referencias o ejemplos adicionales, hemos comenzado a trabajar en NextAuth.js v2 recientemente. No lo menciono tanto como un complemento, es un proyecto de código abierto y un grupo de personas ha ayudado en él, pero es súper simple de usar y el código y el enfoque pueden ser útiles como referencia para la gente.

Para algunos antecedentes, como NextAuth v1, utiliza cookies que están firmadas, con prefijo y solo HTTP, evitando los errores de seguridad comunes del uso de tokens del lado del cliente.

NextAuth.js v2 admite el inicio de sesión con Apple, Google, Facebook, Twitter, GitHub, Auth0, Okta, Slack, Discord y otros proveedores de OAuth (admite tanto 1.xy 2.x). Puede usarlo con MySQL, MariaDB, Postgres, MongoDB, o sin base de datos (solo OAuth y JSON Web Tokens para una solución 100% sin servidor).

El uso es muy simple, hay un método estático universal llamado session() y un React Hook llamado useSession() que puede usar en los componentes del lado del 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>}
  </>
}

Está construido para Next.js 9.xy Serverless, y no tiene dependencias como Express o PassportJS. Incluye un proveedor de autenticación que puede usar en _app.js para agregar automáticamente el estado de autenticación a todas las páginas; funciona tanto para el cliente como para el servidor.

Para obtener más información, consulte next-auth.js.org o consulte next-auth @ beta en NPM

Todavía es un trabajo en progreso, todavía estamos puliendo la documentación y el modelo de eventos, con una fecha de lanzamiento prevista de ~ principios ~ mediados de junio.

Aprecio que este es un hilo antiguo (y un tema de larga duración en general), pero para aquellos que buscan referencias o ejemplos adicionales, hemos comenzado a trabajar en NextAuth.js v2 recientemente. No lo menciono tanto como un complemento, es un proyecto de código abierto y un grupo de personas ha ayudado en él, pero es súper simple de usar y el código y el enfoque pueden ser útiles como referencia para la gente.

Para algunos antecedentes, como NextAuth v1, utiliza cookies que están firmadas, con prefijo y solo HTTP, evitando los errores de seguridad comunes del uso de tokens del lado del cliente.

NextAuth.js v2 admite el inicio de sesión con Apple, Google, Facebook, Twitter, GitHub, Auth0, Okta, Slack, Discord y otros proveedores de OAuth (admite tanto 1.xy 2.x). Puede usarlo con MySQL, MariaDB, Postgres, MongoDB, o sin base de datos (solo OAuth y JSON Web Tokens para una solución 100% sin servidor).

El uso es muy simple, hay un método estático universal llamado session() y un React Hook llamado useSession() que puede usar en los componentes del lado del 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>}
  </>
}

Está construido para Next.js 9.xy Serverless, y no tiene dependencias como Express o PassportJS. Incluye un proveedor de autenticación que puede usar en _app.js para agregar automáticamente el estado de autenticación a todas las páginas; funciona tanto para el cliente como para el servidor.

Para obtener más información, consulte next-auth.js.org o consulte next-auth @ beta en NPM

Todavía es un trabajo en progreso, todavía estamos puliendo la documentación y el modelo de eventos, con una fecha de lanzamiento prevista de ~ principios ~ mediados de junio.

¡Buen trabajo este!
¿Se puede usar solo en el lado del cliente? Por ejemplo, tengo una aplicación de API de rieles y uso el siguiente JS para el lado del cliente.

¿Fue útil esta página
0 / 5 - 0 calificaciones