【问题标题】:Oauth2 Implicit Flow with single-page-app refreshing access tokens具有单页应用刷新访问令牌的 Oauth2 隐式流
【发布时间】:2014-06-24 10:35:42
【问题描述】:

我正在使用 Thinktecture AuthorizationServer (AS),它运行良好。

我想写一个可以直接调用 WebAPI 的原生 javascript 单页应用,但是隐式流不提供刷新令牌。

如果进行 AJAX 调用,如果令牌已过期,API 将发送重定向到登录页面,因为数据使用动态弹出窗口,这将中断用户。

Facebook 或 Stackoverflow 如何做到这一点,并且仍然允许页面上运行的 javascript 调用 API?

建议的解决方案

下面的场景听起来合理吗(假设这可以通过 iframe 完成):

我的 SPA 将我定向到 AS,我通过隐式流获得了一个令牌。在 AS 我点击允许Read data 范围,然后点击Remember decision,然后点击Allow 按钮。

由于我点击了Remember decision 按钮,每当我点击 AS 获取令牌时,都会自动传回一个新令牌,而无需我登录(我可以看到 FedAuth cookie 记住了我的决定并相信这是启用此功能只是工作)。

使用我的 SPA(不受信任的应用程序),我没有刷新令牌,只有访问令牌。所以我改为:

  1. 确保用户已登录并单击记住决定(否则 iframe 将无法工作)
  2. 调用 WebAPI,如果 401 响应尝试通过以下步骤获取新令牌...
  3. 在页面上有一个隐藏的 iframe,我将设置 URL 以从授权服务器获取新的访问令牌。
  4. 从 iframe 的哈希片段中获取新令牌,然后将其存储在 SPA 中并用于所有未来的 WebAPI 请求。

如果 FedAuth cookie 被盗,我想我仍然会遇到麻烦。

上述场景有什么标准或推荐的方法吗?

【问题讨论】:

  • 这是一个有趣的问题,但与 Angularjs 几乎没有相关性。我建议你删除那个标签。
  • 访问令牌过期后多少时间?

标签: javascript oauth-2.0 jwt thinktecture-ident-server


【解决方案1】:

我了解您的问题是,当访问令牌过期时,用户将通过重定向到授权服务器的登录页面而遇到中断。但我认为至少在使用隐式授权时,您不能也不应该解决这个问题。

我相信您已经知道,implicit grant 应该由无法对其凭据保密的消费者使用。因此,授权服务器颁发的访问令牌应该有一个有限的 ttl。例如,谷歌在3600 sec 中使他们的访问令牌无效。当然你可以增加 ttl,但它永远不应该成为一个长期存在的令牌。

另外需要注意的是,在我看来,用户中断非常小,即如果实施正确,用户只需通过授权服务器进行一次身份验证。之后(例如,第一次授权应用程序访问用户控制的任何资源时)将建立一个会话(基于 cookie 或令牌)并且当消费者的访问令牌(使用隐式授权的 Web 应用程序) expires,将通知用户令牌已过期,需要重新与授权服务器进行身份验证。但由于会话已经建立,用户将立即被重定向回网络应用程序。

但是,如果这不是您想要的,我认为您应该考虑使用授权码授予,而不是使用 iframe 做复杂的事情。 在这种情况下,您需要一个服务器端 Web 应用程序,因为这样您就可以将您的凭据保密并使用刷新令牌。

【讨论】:

【解决方案2】:

听起来您需要在访问令牌过期时对请求进行排队。这或多或少是 Facebook 和 Google 的做法。使用 Angular 的一个简单方法是添加一个 HTTP 拦截器并检查 HTTP401 响应。如果返回一个,您将重新验证并将之后的任何请求排队,直到验证请求完成(即承诺)。完成后,您可以使用刷新令牌从身份验证请求中使用新返回的访问令牌处理未完成的队列。

快乐编码。

【讨论】:

  • 永远不要在 javascript 客户端中使用刷新令牌 - 请参阅我添加到答案中的评论。
【解决方案3】:

不确定我是否理解您的问题,但是,

我想写一个可以直接调用 WebAPI 的原生 javascript 单页应用,但是隐式流不提供刷新令牌。

总结事实,

刷新令牌有时被用作 A: Authorization Grant 的一部分

https://www.rfc-editor.org/rfc/rfc6749#section-1.5

正如您在隐式流程中所说,您不会取回刷新令牌,而只能在授权授予部分

https://www.rfc-editor.org/rfc/rfc6749#section-4.2.2

因此您可以在发出访问令牌时取回刷新令牌(刷新令牌始终是可选的)

https://www.rfc-editor.org/rfc/rfc6749#section-5.1

使用我的 SPA(不受信任的应用程序),我没有刷新令牌,只有一个 访问令牌。所以我改为:

  1. 确保用户已登录并单击记住决定(否则 iframe 不起作用)
  1. 调用 WebAPI,如果 401 响应尝试获取新的 通过以下步骤获取令牌...
  1. 在页面上有一个隐藏的 iframe,它 我将设置 URL 以从授权中获取新的访问令牌 服务器。
  1. 从 iframe 的哈希片段中获取新令牌,然后 将其存储在 SPA 中并用于所有未来的 WebAPI 请求。
  1. SPA(您)不知道用户是否选择了记住决定。它在 AS 方向,应该是完整的黑盒。跳过这一步。

  2. 您始终可以尝试使用访问令牌并等待结果。

  3. 如果访问令牌已过期并且您没有刷新令牌,您仍然可以创建隐藏的 iframe 并尝试获取新的访问令牌。

  4. 假设您的 AS 提供了记住决定的选项并且以后不会更改它,那么:您的 iframe 将在没有用户交互的情况下获得新的访问令牌,然后您将在某个未知的时间限制内获得结果。 可以通过 setInterval 检查读取特定 cookie 或 iframe postmessage 的结果。 如果你没有在时限内取回数据,那么就会出现以下一种情况:

  • 延迟、AS 慢、连接慢或时限太紧
  • 用户没有选择记住决定

在这种情况下:

  1. 显示带有登录信息的 iframe

如果 AS 不提供刷新令牌,我认为上述场景是一种很好的做法,但我也猜想每个这样的 AS 也不会提供记住选项。

StackOverflow Google 场景(我只能猜测)

  1. 用户登录,授权请求发生

  2. 用户登录,获取访问令牌

  3. SO 尝试使用访问令牌

  4. SO 取回结果 + 刷新令牌

  5. SO 保存刷新令牌

  6. SO 可以永久访问用户的 Google 帐户

【讨论】:

  • 除非是第一次,否则每次发出访问令牌时都不会得到刷新令牌。刷新令牌只会给一次,我们需要存储该刷新令牌。并且使用这个刷新令牌,我们可以在之后的任何时间调用新的访问令牌。请参考我上面的代码。
  • @AhammadaliPK 然后随时编辑我的答案。它已经超过一年了,我目前不在主题范围内。
【解决方案4】:

在 Google o-Auth 中,访问令牌的有效期仅为 1 小时,因此您需要每隔一小时以编程方式更新您的访问令牌,您可以创建 web api 来执行此操作,您需要刷新令牌,并且刷新令牌也不会过期,使用c#代码,我已经做到了。

 if (dateTimeDiff > 55)
            {
                var request = (HttpWebRequest)WebRequest.Create("https://www.googleapis.com/oauth2/v3/token");
                var postData = "refresh_token=your refresh token";
                postData += "&client_id=your client id";
                postData += "&client_secret=your client secrent";
                postData += "&grant_type=refresh_token";

                var data = Encoding.ASCII.GetBytes(postData);            
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = data.Length;
                request.UseDefaultCredentials = true;

                using (var stream = request.GetRequestStream())
                {
                    stream.Write(data, 0, data.Length);
                }
                var response = (HttpWebResponse)request.GetResponse();
                string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

            }

您需要将访问令牌的最后更新日期时间保存在某处(例如在数据库中),这样,每当您必须发出请求时,如果超过 60,您可以用当前日期时间减去它分钟,你需要调用webapi来获取新的token。

【讨论】:

  • 刷新令牌用于服务器端客户端。提问者的客户端在用户代理中运行,因此它应该使用刷新令牌。请参阅 Dominick Baier 的演示文稿Securing Web APIs – Patterns & Anti-Patterns,尤其是从第 45 分钟开始。
  • 是的,它不应该使用刷新令牌,刷新令牌旨在用于服务器端。当访问令牌过期时,应用程序将再次提示进行身份验证。所以上面的代码会自动从服务器端的刷新令牌创建一个新的访问令牌。用户不会再次提示登录。
  • 刷新令牌,这种方法在这种情况下不起作用,因为问题与使用隐式流的 javascript 客户端有关。
  • 刷新令牌不会暴露给客户端,它存储在数据库中,如果令牌无效,我们只会制作新的访问令牌。令牌交换在服务器端完成。上面的代码我已经提交了 c# web API 代码
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-17
  • 2017-04-14
  • 1970-01-01
  • 2014-02-26
  • 2021-07-08
  • 2020-01-09
  • 2021-08-23
相关资源
最近更新 更多