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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11440398/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 05:49:34  来源:igfitidea点击:

How do I implement secure OAuth2 consumption in Javascript?

javascriptapioauth-2.0angularjsconsumer

提问by David Myers

I'm in the process of designing an API in PHP that will use OAuth2.0. My end goal is to build a front-end application in javascript (using AngularJS) that accesses this API directly. I know that traditionally there's no way to secure transactions in javascript and so directly accessing an API isn't feasible. The front-end would need to communicate with server code that in turn communicated with the API directly. However, in researching OAuth2 it looks as if the User-Agent Flow is designed to help in this situation.

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

What I need help with is implementing the OAuth2 User-Agent Flow in javascript (particularly AngularJS if possible as that's what I'm using for my front-end). I haven't been able to find any examples or tutorials that do this. I really have no idea where to start and don't want to read through the entire OAuth2 spec without at least seeing an example of what I'll be looking at doing. So any examples, tutorials, links, etc would be greatly appreciated.

我需要帮助的是在 javascript 中实现 OAuth2 User-Agent Flow(如果可能,特别是 AngularJS,因为这是我用于前端的)。我一直无法找到执行此操作的任何示例或教程。我真的不知道从哪里开始,也不想通读整个 OAuth2 规范,至少没有看到我将要做什么的例子。因此,将不胜感激任何示例、教程、链接等。

回答by Jan Gerlinger

The Implicit Grant flow(the one you're referring to as User-Agent Flow) is exactly the way to go:

隐格兰特流(你指的是作为一个用户代理流量)正好是要走的路:

The implicit grant is a simplified authorization code flow optimized for clients implemented in a browser using a scripting language such as JavaScript.

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

To understand the flow, the documentation from Google for client-side applicationsis a really good place to start. Note that they recommend you to take an additional token validationstep to avoid confused deputy problems.

要了解流程,Google 提供的客户端应用程序文档是一个非常好的起点。请注意,他们建议您采取额外的令牌验证步骤以避免混淆代理问题

Here is a short example implementation of the flow using the Soundcloud API and jQuery, taken from this answer:

这是使用 Soundcloud API 和 jQuery 的流程的简短示例实现,取自此答案

<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>

For sending XMLHttpRequests(what the ajax()function does in jQuery) using AngularJS, refer to their documentation of the $httpservice.

要使用 AngularJS发送XMLHttpRequests(该ajax()函数在 jQuery 中的作用),请参阅他们的$http服务文档。

If you want to preserve state, when sending the user to the authorization endpoint, check out the stateparameter.

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

回答by Artem Oboturov

There's an example of Authorization Code Grantapproach to get a token from OAuth server. I used jQuery ($) to make some operations.

Authorization Code Grant一个从 OAuth 服务器获取令牌的方法示例。我使用 jQuery ( $) 进行了一些操作。

First, redirect user to authorization page.

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

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);

When one gave authorization pass to get token:

当有人提供授权通行证以获取令牌时:

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.
}

You could implement the Resource Owner Password Credentials Grantin the same manner using jQuery or pure XMLHttpRequest and don't make any redirects - because on each redirect you'll loose state of your application.

您可以Resource Owner Password Credentials Grant使用 jQuery 或纯 XMLHttpRequest 以相同的方式实现,并且不要进行任何重定向 - 因为在每次重定向时,您都会丢失应用程序的状态。

For me, I used HTML5 local storage to persist state of my application for data which were not likely to pose a security threat.

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