node.js JWT(JSON Web Token)自动延长过期时间

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26739167/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 18:05:00  来源:igfitidea点击:

JWT (JSON Web Token) automatic prolongation of expiration

node.jsapisecurityauthenticationjwt

提问by maryo

I would like to implement JWT-based authentication to our new REST API. But since the expiration is set in the token, is it possible to automatically prolong it? I don't want users to need to sign in after every X minutes if they were actively using the application in that period. That would be a huge UX fail.

我想对我们的新 REST API 实施基于 JWT 的身份验证。但是既然token里设置了过期时间,那么是否可以自动延长呢?如果用户在那段时间内积极使用该应用程序,我不希望用户需要在每 X 分钟后登录。那将是一个巨大的用户体验失败。

But prolonging the expiration creates a new token (and the old one is still valid until it expires). And generating a new token after each request sounds silly to me. Sounds like a security issue when more than one token is valid at the same time. Of course I could invalidate the old used one using a blacklist but I would need to store the tokens. And one of the benefits of JWT is no storage.

但是延长到期时间会创建一个新令牌(旧令牌在到期之前仍然有效)。在每个请求之后生成一个新令牌对我来说听起来很愚蠢。当多个令牌同时有效时,这听起来像是一个安全问题。当然,我可以使用黑名单使旧的使用无效,但我需要存储令牌。JWT 的好处之一是没有存储。

I found how Auth0 solved it. They use not only JWT token but also a refresh token: https://docs.auth0.com/refresh-token

我找到了 Auth0 如何解决它。他们不仅使用 JWT 令牌,还使用刷新令牌:https: //docs.auth0.com/refresh-token

But again, to implement this (without Auth0) I'd need to store refresh tokens and maintain their expiration. What is the real benefit then? Why not have only one token (not JWT) and keep the expiration on the server?

但同样,要实现这一点(没有 Auth0),我需要存储刷新令牌并保持它们的到期时间。那么真正的好处是什么呢?为什么不只有一个令牌(不是 JWT)并将过期时间保留在服务器上?

Are there other options? Is using JWT not suited for this scenario?

还有其他选择吗?使用 JWT 不适合这种情况吗?

回答by José F. Romaniello

I work at Auth0 and I was involved in the design of the refresh token feature.

我在 Auth0 工作,参与了刷新令牌功能的设计。

It all depends on the type of application and here is our recommended approach.

这完全取决于应用程序的类型,这是我们推荐的方法。

Web applications

网络应用程序

A good pattern is to refresh the token before it expires.

一个好的模式是在令牌过期之前刷新令牌。

Set the token expiration to one week and refresh the token every time the user open the web application and every one hour. If a user doesn't open the application for more than a week, they will have to login again and this is acceptable web application UX.

将令牌过期时间设置为一周,并在用户每次打开 Web 应用程序时和每隔一小时刷新一次令牌。如果用户超过一周没有打开应用程序,他们将不得不再次登录,这是可接受的 Web 应用程序用户体验。

To refresh the token your API needs a new endpoint that receives a valid, not expired JWT and returns the same signed JWT with the new expiration field. Then the web application will store the token somewhere.

要刷新令牌,您的 API 需要一个新端点,该端点接收有效的、未过期的 JWT 并返回具有新过期字段的相同签名 JWT。然后 Web 应用程序会将令牌存储在某处。

Mobile/Native applications

移动/本地应用程序

Most native applications do login once and only once.

大多数本机应用程序只登录一次。

The idea is that the refresh token never expires and it can be exchanged always for a valid JWT.

这个想法是刷新令牌永不过期,并且可以始终将其交换为有效的 JWT。

The problem with a token that never expires is that nevermeans never. What do you do if you lose your phone? So, it needs to be identifiable by the user somehow and the application needs to provide a way to revoke access. We decided to use the device's name, e.g. "maryo's iPad". Then the user can go to the application and revoke access to "maryo's iPad".

永不过期的令牌的问题在于永不意味着永不过期。手机丢了怎么办?因此,它需要以某种方式被用户识别,并且应用程序需要提供一种撤销访问的方法。我们决定使用设备的名称,例如“maryo's iPad”。然后用户可以转到应用程序并撤销对“maryo's iPad”的访问。

Another approach is to revoke the refresh token on specific events. An interesting event is changing the password.

另一种方法是撤销特定事件的刷新令牌。一个有趣的事件是更改密码。

We believe that JWT is not useful for these use cases so we use a random generated string and we store it on our side.

我们认为 JWT 对这些用例没有用处,因此我们使用随机生成的字符串并将其存储在我们身边。

回答by IanB

In the case where you handle the auth yourself (i.e don't use a provider like Auth0), the following may work:

在您自己处理身份验证的情况下(即不使用像 Auth0 这样的提供程序),以下可能有效:

  1. Issue JWT token with relatively short expiry, say 15min.
  2. Application checks token expiry date before any transaction requiring a token (token contains expiry date). If token has expired, then it first asks API to 'refresh' the token (this is done transparently to the UX).
  3. API gets token refresh request, but first checks user database to see if a 'reauth' flag has been set against that user profile (token can contain user id). If the flag is present, then the token refresh is denied, otherwise a new token is issued.
  4. Repeat.
  1. 发行有效期相对较短的 JWT 令牌,比如 15 分钟。
  2. 应用程序在任何需要令牌的交易之前检查令牌到期日期(令牌包含到期日期)。如果令牌已过期,则它首先要求 API“刷新”令牌(这对 UX 透明地完成)。
  3. API 获取令牌刷新请求,但首先检查用户数据库以查看是否针对该用户配置文件设置了“reauth”标志(令牌可以包含用户 ID)。如果该标志存在,则拒绝令牌刷新,否则将发出新令牌。
  4. 重复。

The 'reauth' flag in the database backend would be set when, for example, the user has reset their password. The flag gets removed when the user logs in next time.

例如,当用户重置密码时,数据库后端中的“reauth”标志将被设置。当用户下次登录时,该标志将被删除。

In addition, let's say you have a policy whereby a user must login at least once every 72hrs. In that case, your API token refresh logic would also check the user's last login date from the user database and deny/allow the token refresh on that basis.

此外,假设您有一个策略,用户必须至少每 72 小时登录一次。在这种情况下,您的 API 令牌刷新逻辑还将从用户数据库中检查用户的上次登录日期,并在此基础上拒绝/允许令牌刷新。

回答by coolersport

I was tinkering around when moving our applications to HTML5 with RESTful apis in the backend. The solution that I came up with was:

在将我们的应用程序移动到带有后端 RESTful api 的 HTML5 时,我正在修补。我想出的解决方案是:

  1. Client is issued with a token with a session time of 30 mins (or whatever the usual server side session time) upon successful login.
  2. A client-side timer is created to call a service to renew the token before its expiring time. The new token will replace the existing in future calls.
  1. 成功登录后,客户端会收到一个会话时间为 30 分钟(或任何通常的服务器端会话时间)的令牌。
  2. 创建客户端计时器以调用服务以在其到期时间之前更新令牌。新令牌将在以后的调用中替换现有令牌。

As you can see, this reduces the frequent refresh token requests. If user closes the browser/app before the renew token call is triggered, the previous token will expire in time and user will have to re-login.

如您所见,这减少了频繁的刷新令牌请求。如果用户在触发更新令牌调用之前关闭浏览器/应用程序,则之前的令牌将及时过期,用户将不得不重新登录。

A more complicated strategy can be implemented to cater for user inactivity (e.g. neglected an opened browser tab). In that case, the renew token call should include the expected expiring time which should not exceed the defined session time. The application will have to keep track of the last user interaction accordingly.

可以实施更复杂的策略来满足用户不活动的需要(例如忽略打开的浏览器选项卡)。在这种情况下,更新令牌调用应包括不应超过定义的会话时间的预期到期时间。应用程序必须相应地跟踪上次用户交互。

I don't like the idea of setting long expiration hence this approach may not work well with native applications requiring less frequent authentication.

我不喜欢设置长时间到期的想法,因此这种方法可能不适用于需要较少身份验证的本机应用程序。

回答by Ollie Bennett

An alternative solution for invalidating JWTs, without any additional secure storage on the backend, is to implement a new jwt_versioninteger column on the users table. If the user wishes to log out or expire existing tokens, they simply increment the jwt_versionfield.

在后端没有任何额外安全存储的情况下,另一种使 JWT 无效的解决方案是jwt_version在用户表上实现一个新的整数列。如果用户希望注销或过期现有令牌,他们只需增加该jwt_version字段。

When generating a new JWT, encode the jwt_versioninto the JWT payload, optionally incrementing the value beforehand if the new JWT should replace all others.

生成新的 JWT 时,将其编码jwt_version到 JWT 有效负载中,如果新 JWT 应替换所有其他 JWT,则可选择预先增加该值。

When validating the JWT, the jwt_versionfield is compared alongside the user_idand authorisation is granted only if it matches.

在验证 JWT 时,该jwt_version字段与 进行比较,user_id并且仅当匹配时才授予授权。

回答by Bhupinder Singh

Below are the steps to do revoke your JWT access token:

以下是撤销 JWT 访问令牌的步骤:

1) When you do login, send 2 tokens (Access token, Refresh token) in response to client .
2) Access token will have less expiry time and Refresh will have long expiry time .
3) Client (Front end) will store refresh token in his local storage and access token in cookies.
4) Client will use access token for calling apis. But when it expires, pick the refresh token from local storage and call auth server api to get the new token.
5) Your auth server will have an api exposed which will accept refresh token and checks for its validity and return a new access token.
6) Once refresh token is expired, User will be logged out.

1)当您登录时,发送2个令牌(访问令牌,刷新令牌)以响应客户端。
2) 访问令牌的有效期较短,刷新的有效期较长。
3)客户端(前端)将刷新令牌存储在他的本地存储中,并将访问令牌存储在 cookie 中。
4) 客户端将使用访问令牌来调用 api。但是当它到期时,从本地存储中选择刷新令牌并调用身份验证服务器 api 以获取新令牌。
5) 您的身份验证服务器将公开一个 api,它将接受刷新令牌并检查其有效性并返回一个新的访问令牌。
6) 一旦刷新令牌过期,用户将被注销。

Please let me know if you need more details , I can share the code (Java + Spring boot) as well.

如果您需要更多详细信息,请告诉我,我也可以共享代码(Java + Spring boot)。

回答by LCJ

Good question- and there is wealth of information in the question itself.

好问题 - 问题本身包含丰富的信息。

The article Refresh Tokens: When to Use Them and How They Interact with JWTsgives a good idea for this scenario. Some points are:-

文章刷新令牌:何时使用它们以及它们如何与 JWT 交互为这种情况提供了一个好主意。一些要点是:-

  • Refresh tokens carry the information necessary to get a new access token.
  • Refresh tokens can also expire but are rather long-lived.
  • Refresh tokens are usually subject to strict storage requirements to ensure they are not leaked.
  • They can also be blacklisted by the authorization server.
  • 刷新令牌携带获取新访问令牌所需的信息。
  • 刷新令牌也可以过期,但寿命相当长。
  • 刷新令牌通常有严格的存储要求,以确保它们不会泄露。
  • 它们也可以被授权服务器列入黑名单。

Also take a look at auth0/angular-jwtangularjs

也看看auth0/angular-jwtangularjs

For Web API. read Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin

对于 Web API。阅读使用 ASP .NET Web API 2 和 Owin 在 AngularJS 应用程序中启用 OAuth 刷新令牌

回答by BytePorter

I actually implemented this in PHP using the Guzzle client to make a client library for the api, but the concept should work for other platforms.

我实际上使用 Guzzle 客户端在 PHP 中实现了这一点,为 api 制作了一个客户端库,但这个概念应该适用于其他平台。

Basically, I issue two tokens, a short (5 minute) one and a long one that expires after a week. The client library uses middleware to attempt one refresh of the short token if it receives a 401 response to some request. It will then try the original request again and if it was able to refresh gets the correct response, transparently to the user. If it failed, it will just send the 401 up to the user.

基本上,我发行了两个令牌,一个短的(5 分钟)一个和一个一周后到期的长的。如果客户端库收到对某个请求的 401 响应,则它使用中间件尝试刷新短令牌。然后它将再次尝试原始请求,如果它能够刷新,则会获得正确的响应,对用户透明。如果失败,它只会将 401 发送给用户。

If the short token is expired, but still authentic and the long token is valid and authentic, it will refresh the short token using a special endpoint on the service that the long token authenticates (this is the only thing it can be used for). It will then use the short token to get a new long token, thereby extending it another week every time it refreshes the short token.

如果短令牌已过期,但仍然是真实的并且长令牌有效且真实,它将使用长令牌认证的服务上的特殊端点刷新短令牌(这是它唯一可以用于的事情)。然后它将使用空头令牌获取新的多头令牌,从而在每次刷新空头令牌时将其延长一周。

This approach also allows us to revoke access within at most 5 minutes, which is acceptable for our use without having to store a blacklist of tokens.

这种方法还允许我们在最多 5 分钟内撤销访问,这对于我们的使用来说是可以接受的,而无需存储令牌黑名单。

Late edit: Re-reading this months after it was fresh in my head, I should point out that you can revoke access when refreshing the short token because it gives an opportunity for more expensive calls (e.g. call to the database to see if the user has been banned) without paying for it on every single call to your service.

后期编辑:在我脑子里新鲜后重新阅读本月,我应该指出您可以在刷新短令牌时撤销访问权限,因为它提供了进行更昂贵调用的机会(例如调用数据库以查看用户已被禁止)而无需在每次调用您的服务时付费。

回答by cchamberlain

jwt-autorefresh

jwt-自动刷新

If you are using node (React / Redux / Universal JS) you can install npm i -S jwt-autorefresh.

如果您使用节点(React / Redux / Universal JS),您可以安装npm i -S jwt-autorefresh.

This library schedules refresh of JWT tokens at a user calculated number of seconds prior to the access token expiring (based on the exp claim encoded in the token). It has an extensive test suite and checks for quite a few conditions to ensure any strange activity is accompanied by a descriptive message regarding misconfigurations from your environment.

该库在访问令牌到期前按用户计算的秒数安排 JWT 令牌的刷新(基于令牌中编码的 exp 声明)。它有一个广泛的测试套件并检查相当多的条件,以确保任何奇怪的活动都伴随着关于环境配置错误的描述性消息。

Full example implementation

完整示例实现

import autorefresh from 'jwt-autorefresh'

/** Events in your app that are triggered when your user becomes authorized or deauthorized. */
import { onAuthorize, onDeauthorize } from './events'

/** Your refresh token mechanism, returning a promise that resolves to the new access tokenFunction (library does not care about your method of persisting tokens) */
const refresh = () => {
  const init =  { method: 'POST'
                , headers: { 'Content-Type': `application/x-www-form-urlencoded` }
                , body: `refresh_token=${localStorage.refresh_token}&grant_type=refresh_token`
                }
  return fetch('/oauth/token', init)
    .then(res => res.json())
    .then(({ token_type, access_token, expires_in, refresh_token }) => {
      localStorage.access_token = access_token
      localStorage.refresh_token = refresh_token
      return access_token
    })
}

/** You supply a leadSeconds number or function that generates a number of seconds that the refresh should occur prior to the access token expiring */
const leadSeconds = () => {
  /** Generate random additional seconds (up to 30 in this case) to append to the lead time to ensure multiple clients dont schedule simultaneous refresh */
  const jitter = Math.floor(Math.random() * 30)

  /** Schedule autorefresh to occur 60 to 90 seconds prior to token expiration */
  return 60 + jitter
}

let start = autorefresh({ refresh, leadSeconds })
let cancel = () => {}
onAuthorize(access_token => {
  cancel()
  cancel = start(access_token)
})

onDeauthorize(() => cancel())

disclaimer: I am the maintainer

免责声明:我是维护者

回答by James A

I solved this problem by adding a variable in the token data:

我通过在令牌数据中添加一个变量解决了这个问题:

softexp - I set this to 5 mins (300 seconds)

I set expiresInoption to my desired time before the user will be forced to login again. Mine is set to 30 minutes. This must be greater than the value of softexp.

expiresIn在用户被迫再次登录之前,我将选项设置为我想要的时间。我的设置为30分钟。这必须大于 的值softexp

When my client side app sends request to the server API (where token is required, eg. customer list page), the server checks whether the token submitted is still valid or not based on its original expiration (expiresIn) value. If it's not valid, server will respond with a status particular for this error, eg. INVALID_TOKEN.

当我的客户端应用程序向服务器 API(需要令牌的地方,例如客户列表页面)发送请求时,服务器会根据其原始过期 ( expiresIn) 值检查提交的令牌是否仍然有效。如果它无效,服务器将响应此错误的特定状态,例如。INVALID_TOKEN.

If the token is still valid based on expiredInvalue, but it already exceeded the softexpvalue, the server will respond with a separate status for this error, eg. EXPIRED_TOKEN:

如果令牌基于expiredIn值仍然有效,但它已经超过该softexp值,则服务器将针对此错误以单独的状态响应,例如。EXPIRED_TOKEN

(Math.floor(Date.now() / 1000) > decoded.softexp)

On the client side, if it received EXPIRED_TOKENresponse, it should renew the token automatically by sending a renewal request to the server. This is transparent to the user and automatically being taken care of the client app.

在客户端,如果它收到EXPIRED_TOKEN响应,它应该通过向服务器发送更新请求来自动更新令牌。这对用户是透明的,并且会自动处理客户端应用程序。

The renewal method in the server must check if the token is still valid:

服务器中的续订方法必须检查令牌是否仍然有效:

jwt.verify(token, secret, (err, decoded) => {})

The server will refuse to renew tokens if it failed the above method.

如果上述方法失败,服务器将拒绝更新令牌。

回答by Daniel Szpisjak

Today, lots of people opt for doing session management with JWTs without being aware of what they are giving up for the sake of perceivedsimplicity. My answer elaborates on the 2nd part of the questions:

如今,很多人选择做与JWTs会话管理没有意识到他们正在放弃了的缘故感觉简单。我的回答详细说明了问题的第二部分:

What is the real benefit then? Why not have only one token (not JWT) and keep the expiration on the server?

Are there other options? Is using JWT not suited for this scenario?

那么真正的好处是什么呢?为什么不只有一个令牌(不是 JWT)并将过期时间保留在服务器上?

还有其他选择吗?使用 JWT 不适合这种情况吗?

JWTs are capable of supporting basic session management with some limitations. Being self-describing tokens, they don't require any state on the server-side. This makes them appealing. For instance, if the service doesn't have a persistence layer, it doesn't need to bring one in just for session management.

JWT 能够支持基本的会话管理,但有一些限制。作为自描述令牌,它们不需要服务器端的任何状态。这使它们具有吸引力。例如,如果服务没有持久层,它就不需要仅仅为了会话管理而引入持久层。

However, statelessness is also the leading cause of their shortcomings. Since they are only issued once with fixed content and expiration, you can't do things you would like to with a typical session management setup.

然而,无国籍状态也是造成他们缺点的主要原因。由于它们仅以固定内容和到期时间发布一次,因此您无法使用典型的会话管理设置做您想做的事情。

Namely, you can't invalidate them on-demand. This means you can't implement a secure logoutas there is no way to expire already issued tokens. You also can't implement idle timeoutfor the same reason. One solution is to keep a blacklist, but that introduces state.

也就是说,您不能按需使它们无效。这意味着您无法实现安全注销,因为无法使已发布的令牌过期。出于同样的原因,您也不能实现空闲超时。一种解决方案是保留黑名单,但这会引入状态。

I wrote a post explaining these drawbacksin more detail. To be clear, you can work around these by adding more complexity (sliding sessions, refresh tokens, etc.)

我写了一篇文章更详细地解释了这些缺点。需要明确的是,您可以通过添加更多复杂性(滑动会话、刷新令牌等)来解决这些问题。

As for other options, if your clients only interact with your service via a browser, I strongly recommend using a cookie-based session management solution. I also compiled a list authentication methodscurrently widely used on the web.

至于其他选项,如果您的客户端仅通过浏览器与您的服务交互,我强烈建议使用基于 cookie 的会话管理解决方案。我还整理了一份目前网络上广泛使用的认证方式列表