Feathers: Agregar soporte para tokens de actualización

Creado en 22 dic. 2015  ·  64Comentarios  ·  Fuente: feathersjs/feathers

Actualmente permitimos obtener un nuevo token mediante la publicación de un token de autenticación válido en <loginEndpoint>/refresh . Los tokens de actualización tienen un flujo de trabajo ligeramente diferente, como se explica aquí:
https://auth0.com/learn/refresh-tokens

Authentication Feature

Comentario más útil

Esta característica es una DEBE cuando se trata de aplicaciones React Native. El usuario inicia sesión al principio y cuando abre la aplicación después de varias semanas espera estar todavía conectado.

Todos 64 comentarios

: +1: @corymsmith y yo estábamos hablando de esto. Con la esperanza de ayudar a patear algo de esto sobre la línea de meta durante las "vacaciones".

Tenemos soporte para esto en master pero también tenemos soporte para esto en la rama decoupling . Para actualizar un token tiene 2 opciones:

  1. Puede volver a autenticarse mediante correo electrónico / contraseña, Twitter, etc.
  2. Puede pasar un token válido a GET /auth/token/refresh

Tenemos un proceso de renovación de tokens implementado, pero no es completamente compatible con el token de actualización como se describe en el enlace Auth0 que publiqué anteriormente. Un token de actualización real funciona de manera similar a un código / contraseña de autenticación de GitHub, pero solo se puede usar para obtener un nuevo token JWT. Entonces, incluso si su token JWT caduca, si tiene un token de actualización, puede usarlo para iniciar sesión nuevamente. Se conservan en la base de datos con userId intacto y se pueden revocar en cualquier momento. Al menos, eso es lo que estoy recopilando del artículo de Auth0.

Ah, tienes razón @marshallswain. Supongo que debería haber hecho clic en el enlace: guiño:

Creo que para el primer corte dejaremos esto fuera del hito 1.0. Es bastante fácil para las personas volver a autenticarse.

Voto un poco para que hagamos de esto una cosa feathers-authentication 2.0.

Grandes mentes.

Todavía estoy bastante confundido, ya que no está claro cómo funciona exactamente el flujo de trabajo de autenticación.

Lo que estoy haciendo ahora es.
1.) El cliente envía nombre de usuario y contraseña

 curl -X POST https://xxx/auth/local   -H "Content-Type: application/json"   -d '{ "email":"xxx", "password":"yyy"}'

Esto devuelve el token JWT.

{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjYyODUsImV4cCI6MTQ3MDQxMjY4NSwiaXNzIjoiZmVhdGhlcnMifQ.OVvQbnxfoDGxPFm3Y6tBhRae2Qa6_mDq-PVIo8RcC8Y"}

2.) Luego, puse este token en el encabezado http Authorizatin para acceder a la API.

curl -X GET https://xxx/users  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjU1NzYsImV4cCI6MTQ3MDQxMTk3NiwiaXNzIjoiZmVhdGhlcnMifQ._CHdx3RpEuI189t90mXq-IMPXRNuoVh7nBwY1ON7xCY'

Lo que no entiendo es cómo actualizar realmente este token.
Lo que intenté es enviar este token a xxx / auth / token / refresh
Lo que obtuve es solo otra ficha muy larga. Luego intenté usar tanto el token antiguo como este nuevo para acceder a la API. ambos funcionan ... (¿no debería desactivarse el viejo?)

curl -X GET https://xxx/auth/token/refresh  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjU1NzYsImV4cCI6MTQ3MDQxMTk3NiwiaXNzIjoiZmVhdGhlcnMifQ._CHdx3RpEuI189t90mXq-IMPXRNuoVh7nBwY1ON7xCY'
{"query":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjU1NzYsImV4cCI6MTQ3MDQxMTk3NiwiaXNzIjoiZmVhdGhlcnMifQ._CHdx3RpEuI189t90mXq-IMPXRNuoVh7nBwY1ON7xCY"},"provider":"rest","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJxdWVyeSI6eyJ0b2tlbiI6ImV5SjBlWEFpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5leUpmYVdRaU9pSTFOemhoTmpVeU4yUmtNVFppTWpJd01EUmhZMlpqTm1FaUxDSnBZWFFpT2pFME56QXpNalUxTnpZc0ltVjRjQ0k2TVRRM01EUXhNVGszTml3aWFYTnpJam9pWm1WaGRHaGxjbk1pZlEuX0NIZHgzUnBFdUkxODl0OTBtWHEtSU1QWFJOdW9WaDduQndZMU9ON3hDWSJ9LCJwcm92aWRlciI6InJlc3QiLCJ0b2tlbiI6ImV5SjBlWEFpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5leUpmYVdRaU9pSTFOemhoTmpVeU4yUmtNVFppTWpJd01EUmhZMlpqTm1FaUxDSnBZWFFpT2pFME56QXpNalUxTnpZc0ltVjRjQ0k2TVRRM01EUXhNVGszTml3aWFYTnpJam9pWm1WaGRHaGxjbk1pZlEuX0NIZHgzUnBFdUkxODl0OTBtWHEtSU1QWFJOdW9WaDduQndZMU9ON3hDWSIsImRhdGEiOnsiX2lkIjoiNTc4YTY1MjdkZDE2YjIyMDA0YWNmYzZhIiwiaWF0IjoxNDcwMzI1NTc2LCJleHAiOjE0NzA0MTE5NzYsImlzcyI6ImZlYXRoZXJzIiwidG9rZW4iOiJleUowZVhBaU9pSktWMVFpTENKaGJHY2lPaUpJVXpJMU5pSjkuZXlKZmFXUWlPaUkxTnpoaE5qVXlOMlJrTVRaaU1qSXdNRFJoWTJaak5tRWlMQ0pwWVhRaU9qRTBOekF6TWpVMU56WXNJbVY0Y0NJNk1UUTNNRFF4TVRrM05pd2lhWE56SWpvaVptVmhkR2hsY25NaWZRLl9DSGR4M1JwRXVJMTg5dDkwbVhxLUlNUFhSTnVvVmg3bkJ3WTFPTjd4Q1kifSwiaWF0IjoxNDcwMzI2NDQyLCJleHAiOjE0NzA0MTI4NDIsImlzcyI6ImZlYXRoZXJzIn0.TqUv3051TTGbX4cPfkN-6pOOB5SN9nH-E7TU1HHSsb8","data":{"_id":"578a6527dd16b22004acfc6a","iat":1470325576,"exp":1470411976,"iss":"feathers","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjU1NzYsImV4cCI6MTQ3MDQxMTk3NiwiaXNzIjoiZmVhdGhlcnMifQ._CHdx3RpEuI189t90mXq-IMPXRNuoVh7nBwY1ON7xCY"}}

Aún más extraño es que intenté usar este nuevo token y enviarlo a / auth / token / refresh nuevamente.
Tengo una ficha aún más larga que esta.

No estoy seguro de qué hice mal o entendí mal aquí. Por favor recomiende.

@parnurzeal , todavía no tenemos soporte para tokens de actualización. Es por eso que esta es una característica propuesta.

La forma de obtener un nuevo token es hacer una POST en /auth/token con su JWT válido existente o iniciar sesión con otro mecanismo de autenticación. Parece que estás haciendo todo correctamente.

Correcto, pero por favor mire de cerca cómo lo hice y el resultado que obtuve.

Usemos un ejemplo sencillo.
Cuando solicito un nuevo token usando abcdefghijklmno (solo token aleatorio sin sentido).
La respuesta es solo una versión más larga del token anterior -> abcdefghijklmnopqrstuvwxyz
Si intento hacerlo de nuevo usando abcdefghijklmnopqrstuvwxyz , obtendré una versión más larga ->
abcdefghijklmnopqrstuvwxyz1234567890 y el ciclo continúa (solicitando más, obtienes una versión más larga de la anterior).

Además, los tres tokens anteriores se pueden usar al mismo tiempo.
¿No debería caducar el token anterior después de que solicitemos un token nuevo?

@parnurzeal lo que estoy diciendo es que no hagas lo que hiciste porque esa función no está realmente implementada. Según la implementación (hasta ahora), el hecho de que el token sigue creciendo cada vez que pulsa /auth/token/refresh se debe a que simplemente estamos devolviendo los datos al token. No es así como se pretende que funcione y no hemos tenido tiempo de terminarlo y por qué esto no está documentado. Se supone que no debes usarlo.

¿No debería caducar el token anterior después de que solicitemos un token nuevo?

Esta es la naturaleza de JWT. Caducan solos de su TTL. Si desea evitar que se utilicen tokens antiguos que aún no han caducado, debe mantener una lista negra. Actualmente, esto se lo deja a usted y tenemos un problema abierto (# 133) en torno a eso, pero es probable que no lleguemos a eso pronto (si es que alguna vez lo haremos).

Hola, miré en / auth / refresh / token y salí con algo así:

...
function pick (o, ...props) {
  return Object.assign({}, ...props.map(prop => ({[prop]: o[prop]})));
}

// Provider specific config
const defaults = {
  payload: ['id', 'role'],
  passwordField: 'password',
  issuer: 'feathers',
  algorithm: 'HS256',
  expiresIn: '1d', // 1 day
};
...
// GET /auth/token/refresh
  get (id, params) {
    if (id !== 'refresh') {
      return Promise.reject(new errors.NotFound());
    }

    const options = this.options;

    // Add payload fields
    const data = pick(params.payload, options.payload);

    return new Promise(resolve => {
      jwt.sign(data, config.get('auth').token.secret, options, token => {
        return resolve({token: token});
      });
    });

  }

¿Es demasiado ingenuo como implementación? Si no, podría intentar pulir, agregar un par de pruebas y crear un PR.

@aboutlo gracias por el esfuerzo! Es mejor esperar hasta que salga la v0.8 (ha estado en alfa por un tiempo) ya que se han producido un montón de cambios y esa ruta podría desaparecer esta semana.
Hoy estoy cortando una versión beta y actualmente estoy terminando la guía de migración. Así que no tardará mucho y la v0.8 resuelve muchos de los problemas actuales con auth.

Hemos pensado mucho en actualizar los tokens, por lo que una vez que se publique 0.8 (esta semana) me encantaría abordar este tema para discutirlo. Probablemente pondré nuestros pensamientos preliminares a finales de esta semana.

bastante justo @ekryski , esperaré el 0.8 :)

Esta característica es una DEBE cuando se trata de aplicaciones React Native. El usuario inicia sesión al principio y cuando abre la aplicación después de varias semanas espera estar todavía conectado.

@deiucanta la buena noticia es que tuvimos esta característica en mente mientras diseñamos [email protected]. No creo que pase mucho tiempo antes de que lo consigamos y lo documentemos.

¡esas son buenas noticias! 👍 esperando eso

@marshallswain Esperamos una actualización de esta función. Por favor, avíseme cuándo podemos esperar esto. ¿O ya está lanzado? Gracias por adelantado.

@deiucanta Mientras tanto, hasta que se

@atulrpandey no se lanzó oficialmente, pero tampoco es difícil de implementar. Simplemente agrega un gancho para generar un nuevo token de actualización y lo almacena en el objeto de usuario en la base de datos y una vez que se agota o expira, lo elimina del usuario.

@petermikitsh otra cosa que puede hacer (si está en un dispositivo móvil) es almacenar un clientId y clientSecret forma segura en el cliente y si el JWT accessToken expira, simplemente vuelva a autorizarlo.

@ekryski, ¿ podría
o, ¿tomará mucho tiempo para que se publique el soporte oficial? ¡esto realmente ayudará con la autenticación móvil!

Sobre el tema de los tokens de actualización:

Los tokens de actualización contienen la información necesaria para obtener un nuevo token de acceso. En otras palabras, siempre que se requiera un token de acceso para acceder a un recurso específico, un cliente puede usar un token de actualización para obtener un nuevo token de acceso emitido por el servidor de autenticación. Los casos de uso comunes incluyen obtener nuevos tokens de acceso después de que los antiguos hayan expirado o obtener acceso a un nuevo recurso por primera vez. Los tokens de actualización también pueden caducar, pero son bastante duraderos. Los tokens de actualización suelen estar sujetos a estrictos requisitos de almacenamiento para garantizar que no se filtren. También pueden ser incluidos en la https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

Así que entré en React Native antes de lo que pensé. Estoy pensando en posiblemente contribuir con esto, pero quiero estar seguro de que entiendo completamente la mecánica para estar seguro de que es la implementación correcta. Dado que los tokens de actualización no son apátridas, habrá algunas restricciones de uso (por ejemplo, los desarrolladores deberán proporcionar un adaptador de almacenamiento).

¿Puedes usar un JWT válido para obtener un token de actualización? ¿O los tokens de actualización se devuelven automáticamente en la respuesta de autenticación (por ejemplo, además de accessToken )? Parece que Auth0 los incluye en las respuestas de autenticación (tanto accessToken como refreshToken ) en su ejemplo en https://auth0.com/learn/refresh-tokens/.

@petermikitsh No creo que deba poder usar un JWT válido para obtener un token de actualización. Si lo hace, cualquiera puede obtener un JWT y mantener el acceso a una cuenta.

Los tokens de actualización generalmente se devuelven con respuestas de inicio de sesión / registro y luego el cliente realmente no puede acceder a ellos nuevamente para la sesión específica a menos que inicien sesión / se registren nuevamente, lo que les da una nueva sesión y un nuevo token de actualización.

Los tokens de actualización no necesitan caducar realmente, pero se pueden evocar si se almacenan en la base de datos y, de esta manera, el usuario también puede ver cuántas sesiones activas tiene. Puede almacenar más información al emitir tokens de actualización (como sistema operativo, ip, nombre del dispositivo, etc. para que sean identificables, como lo hacen Facebook, GitHub).

Al menos así es como lo hago yo.

Debería estar bien usar un JWT válido para obtener un token de actualización siempre que verifique la autorización tanto cuando emite el token de actualización como cuando intenta usar el token de actualización.

@marshallswain

Actualmente permitimos obtener un nuevo token mediante la publicación de un token de autenticación válido en <loginEndpoint>/refresh . Los tokens de actualización tienen un flujo de trabajo ligeramente diferente ...

Entonces debería llamarse renew no refresh para evitar la confusión <loginEndpoint>/renew

Como dijo @abhishekbhardwaj

accessToken, no debe ser actualizado por un accessToken, sino solo por un refresehToken o nombre de usuario / contraseña, un refreshToken solo debe ser actualizado por una autenticación de usuario / contraseña, o algún otro secreto, que no es accesible por el navegador, como un factor 2 auth ...

Actualmente, es posible actualizar su accessToken con un accessToken, como se menciona aquí:

https://github.com/feathersjs/authentication-jwt/issues/61

Otro enfoque es almacenar el token de actualización dentro de la carga útil de accessToken, luego la API de actualización actual verifica si el token de actualización no está revocado (a través de la base de datos o la llamada de redis). De esta manera, el token de actualización podría ser una identificación simple de incremento automático. Además, la API de actualización ya no debería verificar la caducidad. Dado que el token de actualización (entero simple) está firmado con un token de acceso, es seguro.

De esta manera, los cambios en la base del código actual deben ser mínimos: para actualizar la API, proporcione una forma para que el usuario pueda proporcionar un gancho para verificar si un token de acceso no está revocado (mediante la verificación del token de actualización dentro de su carga útil), si este gancho se proporciona don ' t validar el tiempo de caducidad.

¿Podrías explicar este enfoque un poco más @ arash16 ?

Si almacena el token de actualización en la carga útil de accessTokens, y la API de actualización no verifica el vencimiento, ¿no ha hecho que cada accessToken sea "sin vencimiento" de manera efectiva?

Porque cualquier accessToken podría usarse para obtener un nuevo token de acceso, ¿verdad?

¿Me estoy perdiendo de algo?

@BigAB
Me refiero a que no verifique el vencimiento solo para la API de actualización , el mismo accessToken se usa como token de actualización y token de acceso. Este token no caduca solo para actualizar y obtener un nuevo token de acceso, el usuario puede

El desarrollador debe tener un db-table / redis para almacenar todos los ID de actualización. Cuando un usuario necesita revocar o cerrar sesión en algunas (o todas) las demás sesiones, podemos proporcionarle una lista de todos los ID de actualización (más alguna otra información adicional como el navegador o la fecha de creación, etc.) y elige eliminar (firmar -fuera de) ellos de forma selectiva. Después de eso, una vez que caduca el token real que contiene esos ID de actualización, la API de actualización se niega a proporcionar una nueva.

El identificador de actualización dentro del token no se usa la mayor parte del tiempo y la autorización no tiene estado hasta que el token expira, después de eso, podemos tener una sola llamada a db para validar el identificador de actualización y devolver un token de acceso nuevo.

El tiempo de vencimiento del token de acceso puede ser corto (menos de 10 minutos), el usuario puede cerrar la página y alejarse, más tarde, cuando abre la página, el token de acceso ya está vencido y se desconecta. Pero el ID de actualización dentro del token tiene un tiempo de vida mucho

Desde el punto de vista de la seguridad, el token de acceso utilizado de esta manera debe tratarse como una clave de sesión antigua, con el beneficio adicional de que no tendremos que llamar a la base de datos para validarla cada vez (solo una vez que haya caducado).

@ arash16 , me gusta su idea de almacenar el token de actualización dentro del JWT de acceso. ¿Hay algún ejemplo de cómo recuperar este token de actualización en el lado del servidor?

Mi problema actual: si el token de acceso ha caducado, la carga útil no está disponible en el contexto del gancho de feathers. Supongo, una forma sería utilizar el verifyJWT() función de utilidad del @feathersjs/authentication paquete, por ejemplo, en el comienzo mismo de app.service('authentication').hooks({ before: { create: ... } }) ?

¿Hay alguna forma consciente de usar fichas de actualización en plumas en este momento? ¿Algún plan de agregar soporte para ellos?

Hola a todos ,

Parece que todavía no está disponible (o me falta algo). ¿Podría informarnos cuándo estará disponible? Veo que hay un repositorio de tokens de actualización de pasaportes disponible. ¿Alguien intentó esto?
https://github.com/fiznool/passport-oauth2-refresh

Hola @daffl ,
¿Alguien puede ayudarme a comprender cómo se puede actualizar el token para la estrategia de Google? ¿Porque no tendré contraseña para el escenario de inicio de sesión de Google?

Gracias

Otro enfoque es almacenar el token de actualización dentro de la carga útil de accessToken

Entonces, cualquiera que tenga incluso uno de sus tokens de acceso incluso vencidos podrá generar fácilmente un conteo interminable de nuevos tokens de acceso o ¿me pierdo algo?

Los tokens de actualización deben almacenarse de forma segura en el cliente y nadie, excepto este cliente, debe tener acceso a ellos.

@deiucanta la buena noticia es que tuvimos esta característica en mente mientras diseñamos [email protected]. No creo que pase mucho tiempo antes de que lo consigamos y lo documentemos.

Esto se publicó en 2016. Chicos, ¿todavía tienen planes de admitir esta función imprescindible?

No. No puedes hacer nada con un token caducado. Además, los tokens de actualización son mucho más fáciles de conseguir en la versión 4 . Una entrada de libro de cocina sobre cómo hacerlo será parte del lanzamiento final.

Además, los tokens de actualización son mucho más fáciles de conseguir en la versión 4 .

¿Hay una fecha aproximada de lanzamiento?

Una entrada de libro de cocina sobre cómo hacerlo será parte del lanzamiento final.

Hasta que se publique esta guía, ¿podría explicar en pocas palabras cómo se puede hacer en la versión 4?

@daffl ping

¿Existe una forma oficial de hacer esto todavía? v4 está aquí y no veo nada en los documentos

@daffl, ¿ podría explicar cómo se puede lograr esto con 4.0 y sin hackeos al servicio de autenticación?

@MichaelErmer como solución alternativa, puede usar una estrategia local o personalizada para renovar jwt, no es ideal, pero funciona bien para la comunicación interna, digamos entre el trabajador y la API.

function initAuth() {
  return async (ctx) => {
    if (ctx.path !== 'authentication') {
      const [authenticated, accessToken] = await Promise.all([
        ctx.app.get('authentication'),
        ctx.app.authentication.getAccessToken(),
      ]);

      if (!accessToken || !authenticated) {
        const result = await ctx.app.authenticate(apiLocalCreds);
        ctx.params = {
          ...ctx.params,
          ...result,
          headers: { ...(ctx.params.headers || {}), Authorization: result.accessToken },
        };
      } else {
        const { exp } = decode(accessToken);
        const expired = Date.now() / 1000 > exp - 60 * 60;
        if (expired) {
          const result = await ctx.app.authenticate(apiLocalCreds);
          ctx.params = {
            ...ctx.params,
            ...result,
            headers: { ...(ctx.params.headers || {}), Authorization: result.accessToken },
          };
        }
      }
    }
    return ctx;
  };
}

client
  .configure(rest(apiHost).superagent(superagent))
  .configure(auth(authConfig))
  .hooks({ before: [initAuth()] });

Actualmente estoy usando este gancho after en v4 authentication , para actualizar mi accessToken después de 20 días ...

javascript
const {DateTime} = require ('luxon')
const renewAfter = {días: 20}

module.exports = () => {
devolver contexto asincrónico => {
si (
context.method === 'crear' &&
context.type === 'después de' &&
context.path === 'autenticación' &&
context.data && context.data.strategy === 'jwt' &&
context.result &&
context.result.accessToken) {
// comprobar si el token necesita ser renovado
const payload = aguardar context.app.service ('autenticación'). verifyAccessToken (context.result.accessToken)
const PublishedAt = DateTime.fromMillis (payload.iat * 1000)
const renovarDespués = emitidoAt.plus (renovarDespués)
const ahora = DateTime.local ()
if (ahora> renovar después) {
context.result.accessToken = aguardar context.app.service ('autenticación'). createAccessToken ({sub: payload.sub})
}
}
contexto de retorno
}
}
`` ``

Es importante tener este gancho en after y como último gancho, para que todas las verificaciones, etc., hayan pasado

¿Algún plan para integrar tokens de actualización en plumas?

Secundo esa pregunta un mensaje antes.

También me pregunto sobre el flujo de trabajo del token de actualización. ¿Es la solución redactada por @ m0dch3n una buena práctica? ¿Deberíamos implementarlo de otra manera?

Todo el flujo de trabajo de refreshToken en mi opción solo protege un poco contra los ataques man in the middle, de modo que si el intermediario roba el accessToken, al menos no puede actualizarlo y tener acceso infinito a los recursos.

No protege contra XSS, porque en ese caso, el atacante puede robar cualquier cosa almacenada en el lado del cliente. Así también el refreshToken ...

El problema ahora es que si hace que el tiempo de caducidad de su accessToken sea demasiado pequeño (es decir, 5 minutos), también debe actualizarlo con más frecuencia. El hombre en el medio solo necesita escuchar durante 5 minutos las solicitudes de los clientes para interceptar el refreshToken y luego ... Si alarga la expiración, tiene más acceso con solo el accessToken ...

Honestamente, si algún cliente me dice que le robaron el acceso, necesito poner en la lista negra accessToken Y actualizarToken de todos modos para estar seguro. Así que me veo obligado a realizar una solicitud de base de datos en cada solicitud de todos modos.

En mi caso, cuando tengo conocimiento de tal caso, pongo en lista negra todos los accessTokens de los últimos 40 días, porque mis accessTokens tienen una validez de 40 días ...

El uso de la solicitud HTTPS hace que los ataques de intermediarios sean realmente difíciles. ¿No estás usando solicitudes HTTPS?

Por supuesto que estoy usando https, pero hay 3 posibilidades de robar el accessToken. Primero está en el lado del cliente (es decir, XSS), segundo en el transporte (man en el medio) y tercero en el lado del servidor.

En el cliente y en el transporte, solo soy la mitad responsable de la seguridad, y la otra mitad es el cliente, que no está totalmente bajo mi control. Pero puedo ayudar al cliente, a evitar riesgos de seguridad, haciendo imposible XSS y asegurando el transporte con https ...

El objetivo de un refreshToken es acortar el vencimiento de un accessToken Y no transmitir un token válido más largo o infinito en CADA solicitud

Entonces, la única seguridad que brinda es que a partir de 100 solicitudes, es decir, no hace que las 100 sean vulnerables en el transporte, sino solo 1 solicitud

Entonces, básicamente, un hombre en el medio de un ataque, no puede ser protegido por un refeshToken y por supuesto no por un XSS ... Solo se puede reducir, por la cantidad de veces que transmite este refreshToken ... El costo de transmitirlo es menor sin embargo, es necesario que el accessToken sea válido por más tiempo ...

Solo copio / pego mis comentarios del canal de Slack:

Creo que el token de actualización es una característica imprescindible y no se trata de renovar automáticamente el token de acceso existente. El token de acceso no tiene estado y no se almacenará en el lado del servidor. ¡El lado negativo es que es válido para siempre! Cuanto más largo sea el token de acceso, más riesgo se impone. Pero si el token de acceso es demasiado corto, entonces sus usuarios deben iniciar sesión con bastante frecuencia, lo que afectará en gran medida la usabilidad.

Ahí es donde entra el token de actualización, el token que se usa para actualizar el token de acceso y es un token de larga duración. Cuando el token de acceso expiró, el cliente puede usar el token de actualización para obtener un nuevo token de acceso, y ese es el único propósito del token de actualización.

El token de actualización se puede revocar en caso de que la cuenta de usuario se haya visto comprometida. Y esa es la gran diferencia entre el token de acceso y el token de actualización. Para revocar el token de actualización emitido, el servidor debe almacenar todos los tokens de actualización emitidos. En otras palabras, el token de actualización tiene estado. El servidor necesita saber cuál es válido cuál no es válido.

Para implementar correctamente el token de actualización, necesitamos algún tipo de almacén de tokens para conservar el token de actualización. También necesitamos implementar al menos tres flujos:

Actualizar la validación del token
Actualizar el token de acceso con un token de actualización válido
Revocar el token de actualización del usuario comprometido

También es bueno tener otras funcionalidades de administración, como las estadísticas de uso de tokens.

Arriba está mi conocimiento actual sobre cómo implementar el token de actualización. No es fácil, pero definitivamente es necesario construir un sistema más seguro.

Resulta que Feathers ya incorporó todas las funcionalidades / módulos necesarios para implementar correctamente los tokens de actualización:

  1. Tienda de tokens de actualización: puede ser fácilmente compatible con el servicio Feathers.
  2. Emitir y validar el token de actualización: puede simplemente reutilizar el soporte JWT existente que incorporó AuthenticationService.

Basado en el trabajo realizado por TheSinding (https://github.com/TheSinding/authentication-refresh-token), implementé mi propia versión de refresh-tokens con un servicio personalizado y tres ganchos (https://github.com/ jackywxd / feathers-refresh-token) que habilita las funcionalidades básicas de los tokens de actualización:

  1. Emita el token de actualización después de la autenticación del usuario con éxito;
  2. Actualizar el token de acceso con un token de actualización JWT válido;
  3. Cerrar sesión de usuario eliminando el token de actualización

Si bien aprovecha al máximo la base de código existente en Feathres, el esfuerzo de codificación real es mínimo y se integra muy bien con la arquitectura actual de Feathers. Demuestra que la arquitectura actual de Feathers es muy ampliable.

Pero una característica completa de Refresh-token también requiere soporte en el lado del Cliente, como almacenar el token de actualización en el lado del cliente, volver a autenticar al usuario después de la expiración del token de acceso, cerrar la sesión del usuario con el token de actualización.

Después de revisar el código fuente de la autenticación de plumas y el cliente de autenticación, creo que el token de actualización podría aprovecharse en el código de funciones existente para permitir activar el soporte de token de actualización tan fácilmente como activar la autenticación.

Ya porté mi base de código de actualización de token de versión de hooks a @ feathersjs / authentication. A continuación, intentaría realizar cambios en el cliente de autenticación para habilitar las funciones del lado del cliente. Mi objetivo final es habilitar el soporte de tokens de actualización tanto en el lado del servidor como en el del cliente.

Mi pregunta / preocupación es ¿cómo se almacenaría el token de actualización en el cliente?

Consulte https://auth0.com/blog/securing-single-page-applications-with-refresh-token-rotation/

Desafortunadamente, los RT de larga duración no son adecuados para los SPA porque no existe un mecanismo de almacenamiento persistente en un navegador que pueda asegurar el acceso solo por la aplicación deseada. Dado que existen vulnerabilidades que pueden explotarse para obtener estos artefactos de alto valor y otorgar a los actores malintencionados acceso a los recursos protegidos, se desaconseja enfáticamente el uso de tokens de actualización en las SPA.

Consulte https://afteracademy.com/blog/implement-json-web-token-jwt-authentication-using-access-token-and-refresh-token

Entonces, ¿cuál es el mejor lugar posible para almacenar los tokens de forma segura? Puede leer más sobre esto en Internet si le apasiona lograr un almacenamiento completamente seguro. Algunas de las soluciones son ideales pero no muy prácticas. Prácticamente lo almacenaría en las Cookies con banderas httpOnly y Secure. No es 100% seguro, pero hace el trabajo.

Vea esta larga discusión sobre cookies: https://github.com/feathersjs-ecosystem/authentication/issues/132 también

@bwgjoseph Sugeriría usar una sesión exprés regular y almacenar todos los tokens allí, en lugar del lado del cliente. Eso es lo que hago y funciona perfectamente bien con todo tipo de aplicaciones, incluido SPA.

@sarkistlt ¿

@bwgjoseph igual que siempre, cookie, solo agregue middlewere antes de registrar sus servicios:

app.use('* | [or specific rout]', session(sess), (req, res, next) => {
      req.feathers.session = req.session || {};
      next();
    });

luego, en su servidor, por ejemplo, el servicio de inicio de sesión del cliente, cuando el cliente está autenticado, simplemente almacena el token en la sesión como ctx.params.session.token = token , donde el token es su acceso JWT o el token de actualización, depende de la lógica de su aplicación.
Y con cualquier nueva solicitud del cliente, verificará si existe un token en la sesión y lo usará para la autenticación. Este es un enfoque mucho más seguro, ya que ninguno de los tokens está expuesto en el lado del cliente.

Solo agregaré que esto funciona mejor para aplicaciones cliente (navegador) - servidor. Al comunicarse internamente entre servidores, o trabajador / servidor, no necesita sesión.

Esto se ha discutido mucho antes (también agregué una entrada a las preguntas frecuentes ) y no es necesariamente más seguro almacenar un token en una sesión. Si alguien tiene acceso a su página para poder ejecutar scripts, también ha secuestrado la sesión y puede realizar solicitudes autenticadas de todos modos.

Por lo tanto, generalmente está bien almacenar un token en, por ejemplo, localStorage (al que solo la página actual tiene acceso también) y también funciona a la perfección con otras plataformas que no son de navegador (como aplicaciones móviles nativas, servidor a servidor, etc.) __ y websockets__ (No puedo enfatizar lo suficiente lo doloroso que es hacer que los websockets funcionen sin problemas y de forma segura con cookies HTTP; mi vida ha sido mucho más fácil desde que dejamos de intentarlo). Sin embargo, en general, un token de actualización debería ser revocable, ya que suele ser mucho más duradero.

De cualquier manera, una solicitud de extracción para esto sería muy bienvenida, creo que facilita mucho la tarea de aclarar los detalles.

@jackywxd - eché un vistazo breve y parece una gran adición.
¿Existe alguna forma de facilitarle al desarrollador la implementación de esto?
¿Como integrar los ganchos que ha creado en la biblioteca, para que no necesitemos agregar los ganchos más tarde?

Creo que debería crear una solicitud de extracción y podríamos tener la discusión allí.

@daffl sí de acuerdo, especialmente con WS. Y si tanto el cliente como el backend lo construyen usted o su equipo, sí, es mejor usar JWT y evitar dependencias y complejidad adicionales en su aplicación.
Pero en algunos casos, por ejemplo, al crear una API REST de escaparate que será utilizada por empresas / desarrolladores de terceros, es más fácil usar la sesión regular y pedir a los desarrolladores que incluyan credenciales con sus solicitudes y luego que describan cómo recuperar el acceso (y actualizar ), guárdelo y páselo con cada solicitud. Lo cual fue manejado perfectamente por el cliente de feathers, pero en la mayoría de los casos cuando el desarrollador no está familiarizado con cómo se construyó el backend, usarán request, superagent, fetch or axios para conectar su aplicación al backend. Al menos en mi caso, esta fue la razón principal para mover la parte del escaparate de la API para trabajar con sesiones regulares en lugar de JWT directamente.

Pero, ¿no sería esa la decisión y la responsabilidad del diseño, del fabricante de dicho escaparate, implementar en su propia API y luego documentar adecuadamente esta característica, en lugar de "forzar" esta decisión a la comunidad?

@TheSinding Creo que podemos decir 'forzar' sobre un enfoque menos utilizado, no sobre las cookies, que han sido (y siguen siendo) el método más utilizado para administrar las sesiones de los usuarios.

Y sí, es una decisión de diseño que se toma después de los comentarios de los desarrolladores que han estado usando la API. Cuando está ejecutando un sistema de múltiples inquilinos o una API que utilizan varios equipos, en algún momento lo mejor es seguir las prácticas generales de la industria para evitar confusión adicional y tiempo adicional para los desarrolladores, especialmente si la solución alternativa no brinda ninguna ventaja para el usuario final.

Una vez más, para ser claros, usar JWT es excelente y mucho más fácil de utilizar, especialmente para la API en tiempo real, y eso es lo que estamos usando en el 90% de los casos + no es necesario ejecutar redis u otra cosa para administrar las sesiones de cookies.
Pero hay algunos casos excepcionales en los que puede que no sea la mejor opción, traje un ejemplo de esa situación en mi comentario anterior.

No estaba diciendo que estuvieras equivocado, solo estaba pensando "en voz alta" :)

En mi opinión, creo que el enfoque con JWT sería un mejor enfoque, ya que eso es lo que ya es compatible y, como @daffl , también es más fácil trabajar con él.

¡Gracias por todos tus comentarios! JWT vs session es un tema diferente que podemos discutir por separado.

Creo que una cosa en la que todos estamos de acuerdo es que el token de acceso + el token de actualización es una solución mucho más segura que el simple token de acceso. Ha sido ampliamente adoptado por los principales gigantes de Internet. Es justo decir que a la comunidad de Feathers le encantaría ver la base de código principal de Feathers para proporcionar soporte integrado para el token de actualización. En realidad, me sorprendió cuando me di cuenta por primera vez de que Feathers no admite el token de actualización.

He estado usando AWS cognito en un par de mis proyectos, AWS Amplify guardará tres tokens en localStorage: token de identificación, token de acceso y token de actualización, Amplify maneja todos los trabajos sucios relacionados con la administración de tokens y proporciona un par de API que permiten una fácil y interfaz sencilla que trabaja con el backend de Cognito. Me gustaría ver una experiencia de desarrollo similar con Feathers.

@TheSinding ¡ Gracias por tu gran trabajo anterior!

Debido a que la autenticación existente se implementa como un servicio normal, podríamos habilitar fácilmente la compatibilidad con el token de actualización extendiendo la clase y agregando un par de enlaces:

this.hooks({ after: { create: [issueRefreshToken(), connection('login'), event('login')], remove: [logoutUser(), connection('logout'), event('logout')], patch: [refreshAccessToken()], },

Al igual que activamos la autenticación mediante la CLI, podríamos ofrecer la opción similar en la CLI para la compatibilidad con el token de actualización. El desarrollador simplemente puede responder SÍ, la CLI crea automáticamente un servicio de "tokens de actualización" del cliente y actualiza las configuraciones relacionadas con el token de actualización en el archivo de configuración predeterminado. Por tanto, es como una solución llave en mano para desarrolladores.

@daffl David, ¡gracias por crear Feathers! Me pregunto si hay alguna "guía de contribución", "guía de codificación", "guía de estilo" para Feathers.

Como mencioné antes, un RP con la implementación de tokens de actualización (o incluso solo algunas ideas) sería muy bienvenido. Todavía no tuve la oportunidad de ver los repositorios vinculados y esta discusión se está volviendo bastante larga. Tenerlo todo actualizado y en un solo lugar facilitaría mucho las cosas. Algunas viñetas importantes:

  • Se pueden emitir tokens de actualización ampliando el servicio de autenticación
  • Los tokens de actualización deben almacenarse en localStorage
  • Los tokens de actualización deben ser revocados (por lo que debe haber una implementación de propósito más general del mecanismo de revocación descrito en https://docs.feathersjs.com/cookbook/authentication/revoke-jwt.html)
  • Debido a la complejidad y configuración adicionales (como Redis para almacenar tokens revocados), puede estar disponible como una función, pero no debe estar habilitada de forma predeterminada (es decir, una aplicación generada estándar, puede ser parte de la CLI pero antes de hacer algo al respecto, Todavía estoy buscando ayuda para iniciar un generador basado en hygen )
  • La información de contribución se puede encontrar en la guía de colaboradores

Si alguien está usando refreshToken, creamos una biblioteca que maneja en frontend, accessToken y refreshToken como "sesiones", demasiado fácil de usar.

Solo es necesario pasar los tokens y la biblioteca intenta obtener un nuevo acessToken con refreshToken cuando caduque. Actualmente se utiliza en Videsk .

Repositorio: controlador de autenticación frontal

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

Temas relacionados

harrytang picture harrytang  ·  3Comentarios

corymsmith picture corymsmith  ·  4Comentarios

arve0 picture arve0  ·  4Comentarios

eric-burel picture eric-burel  ·  3Comentarios

rstegg picture rstegg  ·  3Comentarios