30

我正在使用 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 被盗,我仍然会遇到麻烦。

上述情况的任何标准或推荐方式?

4

4 回答 4

6

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

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

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

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

于 2015-07-18T16:29:44.910 回答
3

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

快乐编码。

于 2016-03-16T02:19:28.893 回答
2

不知道我是否理解你的问题,但是,

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

总结事实,

刷新令牌有时用作 A 的一部分:授权授予

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。如果您没有按时取回数据,则出现以下情况之一:

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

在这种情况下:

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

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

StackOverflow <---> Google 场景(我只能猜测)

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

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

  3. SO尝试使用访问令牌

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

  5. SO保存刷新令牌

  6. SO 拥有对用户 Google 帐户的永久访问权限

于 2015-06-01T10:38:59.143 回答
-2

在 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 来获取新的令牌。

于 2016-03-21T14:39:14.263 回答