Basta ya de investigar por internet ya que estás al lugar exacto, tenemos la respuesta que quieres pero sin problema.
Solución:
Si estamos hablando no solo de una autenticación sin estado segura sino también de trabajo, deberá considerar la estrategia adecuada con ambos access
y refresh
tokens.
-
Token de acceso es un token que proporciona acceso a un recurso protegido.
Expiration
aquí podría instalarse aproximadamente en ~ 1 hora (depende de sus consideraciones). -
Actualizar token es un token especial que debe usarse para generar más
access token
en caso de que haya caducado o se haya actualizado la sesión del usuario. Obviamente, debe hacerlo duradero (en comparación conaccess token
) y asegúrelo tanto como sea posible.
Expiration
aquí podría instalarse aproximadamente en ~ 10 días o incluso más (también depende de sus consideraciones).
FYI: Ya que refresh tokens
son de larga duración, para que sean realmente seguros, es posible que desee almacenarlos en su base de datos (las solicitudes de token de actualización rara vez se realizan). De esta manera, digamos, incluso si su token de actualización fue pirateado de alguna manera y alguien regenerado access/refresh
tokens, por supuesto perderá permisos, pero luego podrá iniciar sesión en el sistema, ya que sabe iniciar sesión / pasar (en caso de que los use más adelante) o simplemente iniciando sesión a través de cualquier red social.
¿Dónde almacenar estos tokens?
Básicamente hay 2 lugares comunes:
- Almacenamiento web HTML5 (localStorage / sessionStorage)
Bueno para ir, pero al mismo tiempo lo suficientemente arriesgado. Se puede acceder al almacenamiento a través del código javascript en el mismo dominio. Eso significa que en caso de que tenga XSS, sus tokens podrían ser pirateados. Por lo tanto, al elegir este método, debe tener cuidado y codificar / escapar todos los datos que no son de confianza. E incluso si lo hizo, estoy bastante seguro de que usa algunos módulos del lado del cliente de terceros y no hay garantía de que ninguno de ellos tenga algún código malicioso.
También Web Storage
no aplica ningún estándar seguro durante la transferencia. Por lo tanto, debe asegurarse de que se envíe JWT HTTPS
y nunca HTTP
.
- Galletas
Con especifico HttpOnly
Las cookies de opción no son accesibles a través de javascript y son inmunes a XSS. También puede configurar el Secure
bandera de cookie para garantizar que la cookie solo se envíe a través de HTTPS. Sin embargo, las cookies son vulnerables a un tipo diferente de ataque: la falsificación de solicitudes entre sitios (CSRF). En este caso CSRF
podría evitarse mediante el uso de algún tipo de patrones de token sincronizados. Hay una buena implementación en AngularJS
, en la sección Consideraciones de seguridad.
Un artículo que quizás quieras seguir.
Para ilustrar cómo funciona en general:
Pocas palabras sobre JWT sí mismo:
Para dejar en claro, hay un depurador JWT realmente genial de los chicos de Auth0. Hay 2 (a veces 3) tipos de notificaciones comunes: public
, private
(y reservado).
Un ejemplo de JWT
cuerpo (carga útil, puede ser lo que quieras):
name: "Dave Doe",
isAdmin: true,
providerToken: '...' // should be verified then separately
Más información sobre JWT
estructura que encontrará aquí.
Para responder a las dos preguntas específicas que planteó:
¿Cuál sería el flujo cuando un usuario inicia sesión con un proveedor de OAuth por primera vez? ¿Debería emberjs enviar todos los detalles al backend en cada inicio de sesión para que el backend pueda agregar nuevos usuarios a la base de datos?
Siempre que un usuario se registre o inicie sesión a través de oauth y su cliente reciba un nuevo token de acceso, lo haría reafirmar (actualícelo o insértelo) en su tabla de usuarios (o colección) junto con cualquier información nueva o actualizada que haya recuperado sobre el usuario de la API del proveedor de oauth. Sugiero almacenarlo directamente en el registro de cada usuario para garantizar que el token de acceso y la información de perfil asociada cambien de forma atómica. En general, normalmente lo compondría en algún tipo de middleware que realice estos pasos automáticamente cuando haya un nuevo token.
¿Qué debería ser parte de mi cuerpo JWT? Estaba pensando en el token de acceso proporcionado por el proveedor y el uid. Un problema en el que puedo pensar aquí es que el token de acceso específico del proveedor puede cambiar. El usuario puede revocar el token del sitio del proveedor y registrarse nuevamente con emberjs.
El cuerpo de JWT generalmente consiste en el reclamos de los usuarios. Personalmente, veo pocos beneficios en almacenar el token de acceso del proveedor en el cuerpo de un token JWT, ya que tendría pocos beneficios para su aplicación cliente (a menos que esté haciendo muchas llamadas API directas desde su cliente a su API, prefiero hacerlo esas llamadas del lado del servidor y enviar a mi cliente de la aplicación un conjunto normalizado de reclamos que se adhieren a mi propia interfaz). Al escribir su propia interfaz de reclamos, no tendrá que evitar las diversas diferencias presentes de múltiples proveedores desde su aplicación cliente. Un ejemplo de esto sería combinar los campos específicos de Twitter y Facebook que se nombran de manera diferente en sus API con los campos comunes que almacena en su tabla de perfil de usuario, luego incrustar los campos de su perfil local como reclamos en su cuerpo JWT para ser interpretados por su aplicación cliente. . Esto tiene un beneficio adicional: no conservará ningún dato que pueda filtrarse en el futuro en un token JWT sin cifrar.
Ya sea que esté almacenando o no el token de acceso proporcionado por el proveedor de oauth dentro del cuerpo del token JWT, deberá otorgar un nuevo token JWT cada vez que cambien los datos del perfil (puede implementar un mecanismo para evitar la emisión de nuevos tokens JWT si no hay actualizaciones de perfil ocurrió y el token anterior sigue siendo bueno).
Además de los campos de perfil que almacene como reclamos en el cuerpo del token JWT, siempre definiría los campos del cuerpo del token JWT estándar de:
user_id",
aud: "YOUR_CLIENT_ID",
exp: 1372674336,
iat: 1372638336
Para cualquier flujo de trabajo de OAuth, definitivamente debe usar la biblioteca passportjs. También debería leer la documentación completa. Es fácil de entender, pero cometí el error de no leerlo todo la primera vez y tuve problemas. Contiene autenticación OAuth con más de 300 proveedores y tokens emisores.
Sin embargo, si desea hacerlo manualmente o desea una comprensión básica, aquí está el flujo que usaría:
-
La interfaz tiene una página de inicio de sesión que enumera el inicio de sesión con Google / Facebook, etc., donde se implementa OAuth.
-
El éxito de OAuth da como resultado un uid, inicio de sesión, access_token, etc. (objeto JSON)
-
PUBLICAS el objeto JSON en tu
/login/
route en su aplicación Node.js. (Sí, envía la respuesta completa independientemente de si es un usuario nuevo o existente. Enviar datos adicionales aquí es mejor que hacer dos solicitudes) -
La aplicación de backend lee el
uid
y elaccess_token
. Asegúrese de que elaccess_token
es válido siguiendo (https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken) o solicitando datos de usuario del proveedor utilizando el token de acceso. (Esto fallará para el token de acceso no válido ya que los tokens de acceso de OAuth se generan por aplicación / desarrollador) Ahora, busque su base de datos backend. -
Si el
uid
existe en la base de datos, actualiza el access_token del usuario y expiresIn en la base de datos. (El access_token le permite obtener más información de Facebook para ese usuario en particular y generalmente brinda acceso durante algunas horas). -
De lo contrario, crea un nuevo usuario con uid, información de inicio de sesión, etc.
-
Después de actualizar el access_token o crear un nuevo usuario, envía el token JWT que contiene el
uid
. (Codifique el jwt con un secreto, esto aseguraría que fue enviado por usted y no ha sido manipulado. Checkout https://github.com/auth0/express-jwt) -
En la interfaz después de que el usuario haya recibido el jwt de
/login
, guárdalo ensessionStorage
porsessionStorage.setItem('jwt', token);
-
En la interfaz, también agregue lo siguiente:
if ($window.sessionStorage.token)
xhr.setRequestHeader("Authorization", $window.sessionStorage.token);
Esto garantizaría que, si hay un token jwt, se envíe con cada solicitud.
- En su archivo Node.js app.js, agregue
app.use(jwt( secret: 'shhhhhhared-secret').unless(path: ['/login']));
Esto validaría ese jwt para cualquier cosa en su ruta, asegurando que el usuario haya iniciado sesión, de lo contrario no permitirá el acceso y redirigirá a la página de inicio de sesión. El caso de excepción aquí es /login
ya que ahí es donde le da a sus usuarios nuevos o no autenticados un JWT.
Puede encontrar más información en la URL de Github sobre cómo obtener el token y averiguar qué solicitud de usuario está atendiendo actualmente.