Oauth2 Implicit Flow使用单页面应用程序刷新访问令牌

时间:2014-05-07 12:33:50

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

我使用的是Thinktecture AuthorizationServer(AS),它运行良好。

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

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

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

提议的解决方案

下面的场景是否合理(假设可以使用iframe):

我的SPA指示我到AS,我通过Implicit Flow获得一个令牌。在AS中,我点击允许Read data范围,然后点击Remember decision,然后点击Allow按钮。

由于我点击了Remember decision按钮,每当我点击一个令牌的AS时,就会自动传回一个新令牌,而我无需登录(我可以看到FedAuth cookie记住了我的决定,相信这是使其能够正常工作)。

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

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

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

4 个答案:

答案 0 :(得分:4)

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

我相信您已经知道,implicit grant应该由不能保密的消费者使用3600 sec。因此,授权服务器发出的访问令牌应具有有限的ttl。例如,Google会在protocol relative URLs中使其访问令牌无效。当然你可以增加ttl,但它永远不应该成为一个长期存在的令牌。

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

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

答案 1 :(得分:3)

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

快乐编码。

答案 2 :(得分:2)

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

  

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

总结事实,

刷新令牌有时被用作A:授权授权

的一部分

https://tools.ietf.org/html/rfc6749#section-1.5

正如您在隐式流中所说,您不会获得刷新令牌,但仅限于授权授予部分

https://tools.ietf.org/html/rfc6749#section-4.2.2

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

https://tools.ietf.org/html/rfc6749#section-5.1

  

使用我的SPA(不受信任的应用),我没有刷新令牌   访问令牌。所以相反我:

     

1)确保用户已登录并单击记住决定(否则   iframe不会工作)

     

2)调用WebAPI,如果401响应尝试并获得新的   通过以下步骤令牌......

     

3)在页面上有一个隐藏的iframe   我将设置URL以从授权中获取新的访问令牌   服务器。

     

4)然后从iframe的哈希片段中获取新令牌   将其存储在SPA中,并用于所有未来的WebAPI请求。

1)SPA(你)不知道用户是否选择了记住决定。它在AS方向,应该是完整的黑盒子。跳过此步骤。

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

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

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

  • 滞后,AS缓慢,连接缓慢或时间限制太紧
  • 用户没有选择记住决定

在这种情况下:

5)使用登录

显示iframe

如果AS不提供刷新令牌,我认为上面的场景是好的做法,但我也猜测每个AS都不会提供记忆选项。

StackOverflow< ---> Google方案(我只能猜)

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

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

3)SO尝试使用访问令牌

4)SO获取结果+刷新令牌

5)SO保存刷新令牌

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

答案 3 :(得分:-1)

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