如何在Javascript中实现安全的OAuth2消费?

如何在Javascript中实现安全的OAuth2消费?,javascript,api,oauth-2.0,angularjs,consumer,Javascript,Api,Oauth 2.0,Angularjs,Consumer,我正在用PHP设计一个将使用OAuth2.0的API。我的最终目标是用javascript(使用AngularJS)构建一个直接访问此API的前端应用程序。我知道传统上没有办法用javascript保护事务,因此直接访问API是不可行的。前端需要与服务器代码通信,而服务器代码又直接与API通信。然而,在研究OAuth2时,用户代理流似乎是为了在这种情况下提供帮助而设计的 我需要的帮助是在javascript中实现OAuth2用户代理流(如果可能的话,特别是AngularJS,因为这是我在前端使用

我正在用PHP设计一个将使用OAuth2.0的API。我的最终目标是用javascript(使用AngularJS)构建一个直接访问此API的前端应用程序。我知道传统上没有办法用javascript保护事务,因此直接访问API是不可行的。前端需要与服务器代码通信,而服务器代码又直接与API通信。然而,在研究OAuth2时,用户代理流似乎是为了在这种情况下提供帮助而设计的


我需要的帮助是在javascript中实现OAuth2用户代理流(如果可能的话,特别是AngularJS,因为这是我在前端使用的)。我还没有找到任何这样的例子或教程。我真的不知道从哪里开始,我不想通读整个OAuth2规范,至少看不到我将要看的示例。因此,任何示例、教程、链接等都将不胜感激。

这里有一个从OAuth服务器获取令牌的
授权码授予方法的示例。我使用jQuery(
$
)进行了一些操作

首先,将用户重定向到授权页面

var authServerUri = "http://your-aouth2-server.com/authorize",
authParams = {
  response_type: "code",
  client_id: this.model.get("clientId"),
  redirect_uri: this.model.get("redirectUri"),
  scope: this.model.get("scope"),
  state: this.model.get("state")
};

// Redirect to Authorization page.
var replacementUri = authServerUri + "?" + $.param(authParams);
window.location.replace(replacementUri);
当授予授权通行证以获取令牌时:

var searchQueryString = window.location.search;
if ( searchQueryString.charAt(0) === "?") {
  searchQueryString = searchQueryString.substring(1);
}
var searchParameters = $.deparam.fragment(searchQueryString);

if ( "code" in searchParameters) {
  // TODO: construct a call like in previous step using $.ajax() to get token.
}
您可以使用jQuery或纯XMLHttpRequest以相同的方式实现
资源所有者密码凭据授予
,并且不进行任何重定向,因为在每次重定向时,您都会丢失应用程序的状态

对我来说,我使用HTML5本地存储来持久化我的应用程序的状态,以保存不太可能构成安全威胁的数据。

(您称之为用户代理流)正是要走的路:

隐式授权是一种简化的授权代码流,针对使用JavaScript等脚本语言在浏览器中实现的客户端进行了优化

为了理解流程,Google for的文档是一个非常好的起点。请注意,他们建议您采取额外的令牌验证步骤以避免

下面是使用Soundcloud API和jQuery实现流的一个简短示例,摘自:


$(函数(){
var extractToken=函数(散列){
var match=hash.match(/access_-token=([\w-]+)/);
return!!匹配并匹配[1];
};
var CLIENT\u ID=您的客户ID;
var授权\u端点=”https://soundcloud.com/connect";
var资源_端点=”https://api.soundcloud.com/me";
var token=extractToken(document.location.hash);
如果(令牌){
$('div.authenticated').show();
$('span.token').text(token);
$.ajax({
url:RESOURCE\u端点
,在发送之前:函数(xhr){
setRequestHeader('授权',“OAuth”+令牌);
setRequestHeader('Accept',“application/json”);
}
,成功:功能(响应){
var容器=$('span.user');
如果(答复){
container.text(response.username);
}否则{
text(“发生错误”);
}
}
});
}否则{
$('div.authenticate').show();
var authUrl=授权\终端+
“?响应类型=令牌”+
“&client_id=“+clientId+
“&redirect_uri=“+window.location;
$(“a.connect”).attr(“href”,authUrl);
}
});
.隐藏{
显示:无;
}

您正在使用令牌
[无代币]。

您的SoundCloud用户名为 [没有用户名]。

要使用AngularJS发送(jQuery中的
ajax()
函数所做的),请参阅他们的文档


如果要保留状态,在将用户发送到授权端点时,请检查参数。

通过相关问题,我找到了这个JS库,它似乎可以简化和处理我正在尝试的操作。它看起来很小,没有使用过。另外,这并不能完全解决我的问题。你发现了吗?@DavidMyers你在使用Javascript时遇到CORS错误了吗。您是如何解决这个问题的?在这种情况下,与隐式授权流相比,使用授权代码流不会提供额外的好处。此外,如果API支持更多(隐藏机密)和更少(公开机密)受信任客户端的不同授权流,则服务器可以区分它们,并执行其他检查或限制范围等操作。此外,在这种情况下不应使用资源所有者密码凭据流,因为资源所有者不能从第三方网站获取到JS客户端代码。您指的是哪种重定向?到授权端点的那个?这种重定向也是隐式授权流的一部分,这是个坏主意。如果您重视用户和您自己的安全,则不应在移动设备或客户端应用程序上使用授权授予。使用此系统,您必须将OAuth2客户端机密存储在应用程序中,黑客可以轻松找到它(例如,在源代码中)。如果要在请求中包含刷新令牌,然后,您可能会将用户数据的基本永久密钥交给任何只具备少量技能的黑客。@MichaelOryl您不需要根据规范为密码授予提供客户端密码。您的oauth服务器应使用白名单URL保护客户端,以防非法使用客户端。+1因为最近授权了验证码有文档记录的客户端代码最佳实践:要使用最新的SoundCloud api实现此功能,请将regexp更改为
/access\u token=([\w-]+)/
。他们的令牌现在包括破折号字符。谢谢,我编辑了答案。可能还允许使用其他字符,但我不确定是否在任何地方记录了这些字符。@Ben隐式和身份验证代码授权的优点之一是客户端应用程序(无论是第一个还是第二个)
<script type="text/javascript" charset="utf-8">
  $(function () {
    var extractToken = function(hash) {
      var match = hash.match(/access_token=([\w-]+)/);
      return !!match && match[1];
    };

    var CLIENT_ID = YOUR_CLIENT_ID;
    var AUTHORIZATION_ENDPOINT = "https://soundcloud.com/connect";
    var RESOURCE_ENDPOINT = "https://api.soundcloud.com/me";

    var token = extractToken(document.location.hash);
    if (token) {
      $('div.authenticated').show();

      $('span.token').text(token);

      $.ajax({
          url: RESOURCE_ENDPOINT
        , beforeSend: function (xhr) {
            xhr.setRequestHeader('Authorization', "OAuth " + token);
            xhr.setRequestHeader('Accept',        "application/json");
          }
        , success: function (response) {
            var container = $('span.user');
            if (response) {
              container.text(response.username);
            } else {
              container.text("An error occurred.");
            }
          }
      });
    } else {
      $('div.authenticate').show();

      var authUrl = AUTHORIZATION_ENDPOINT + 
        "?response_type=token" +
        "&client_id="    + clientId +
        "&redirect_uri=" + window.location;

      $("a.connect").attr("href", authUrl);
    }
  });
</script>
<style>
  .hidden {
    display: none;
  }
</style>

<div class="authenticate hidden">
  <a class="connect" href="">Connect</a>
</div>

<div class="authenticated hidden">
  <p>
    You are using token
    <span class="token">[no token]</span>.
  </p>

  <p>
    Your SoundCloud username is
    <span class="user">[no username]</span>.
  </p>
</div>