我想对我们的新REST API实现基于JWT的身份验证。但既然在令牌中设置了到期时间,有没有可能自动延长呢?我不希望用户需要在每X分钟后登录,如果他们在那个时期正在积极使用应用程序。那将是一个巨大的UX失败。
但是延长到期时间会创建一个新的令牌(而旧的令牌在到期之前仍然有效)。在每个请求之后生成一个新令牌对我来说听起来很傻。当多个令牌同时有效时,听起来像是一个安全问题。当然,我可以使用黑名单使旧的旧的失效,但我需要存储代币。JWT的一个好处是没有存储。
我找到了Auth0是如何解决它的。它们不仅使用JWT令牌,还使用刷新令牌:https://docs.auth0.com/refresh-token
但是,要实现这一点(没有Auth0),我需要存储刷新令牌并维护它们的到期时间。那么真正的好处是什么呢?为什么不只有一个令牌(不是JWT),并在服务器上保留到期时间呢?
还有别的选择吗?使用JWT不适合这个场景吗?
我在Auth0工作,我参与了刷新令牌特性的设计。
这完全取决于应用程序的类型,下面是我们推荐的方法。
一个好的模式是在令牌过期之前刷新它。
将令牌过期时间设置为一周,并在用户每次打开web应用程序时和每隔一小时刷新令牌。如果用户超过一周没有打开应用程序,他们将不得不再次登录,这是可以接受的web应用程序UX。
要刷新令牌,您的API需要一个新的端点,该端点接收有效的,未过期的JWT,并返回具有新过期字段的相同签名的JWT。然后web应用程序将把令牌存储在某个地方。
大多数本机应用程序只登录一次。
其思想是刷新令牌永远不会过期,并且可以始终将其交换为有效的JWT。
永不过期的令牌的问题在于,永不意味着永不。如果你丢了手机怎么办?因此,它需要以某种方式被用户识别,并且应用程序需要提供一种撤销访问的方法。我们决定使用设备的名字,例如“Maryo's iPad”。然后用户就可以转到应用程序并撤销对“Maryo's iPad”的访问权限。
另一种方法是撤销特定事件上的刷新令牌。一个有趣的事件是更改密码。
我们认为JWT对这些用例没有用处,所以我们使用一个随机生成的字符串,我们把它存储在我们这边。
在您自己处理身份验证的情况下(即不使用像Auth0这样的提供程序),以下操作可能会起作用:
例如,当用户重置密码时,数据库后端中的“reauth”标志将被设置。当用户下次登录时,该标志将被移除。
此外,假设您有一个策略,用户必须至少每72小时登录一次。在这种情况下,API令牌刷新逻辑还将从用户数据库中检查用户的上次登录日期,并在此基础上拒绝/允许令牌刷新。
在将我们的应用程序移动到HTML5时,我在后端使用了RESTful API。我想出的解决办法是:
如您所见,这减少了频繁的刷新令牌请求。如果用户在触发更新令牌调用之前关闭浏览器/应用程序,则上一个令牌将及时过期,用户将不得不重新登录。
可以实现更复杂的策略来满足用户不活动的情况(例如忽略了打开的浏览器选项卡)。在这种情况下,renew令牌调用应该包括预期的过期时间,该过期时间不应该超过定义的会话时间。应用程序必须相应地跟踪上次用户交互。
我不喜欢设置长过期的想法,因此这种方法可能不适用于需要较少频繁身份验证的本机应用程序。