Как реализовать безопасное использование OAuth2 в Javascript?

Я занимаюсь разработкой API на PHP, который будет использовать OAuth2.0. Моя конечная цель — создать внешнее приложение на javascript (используя AngularJS), которое напрямую обращается к этому API. Я знаю, что традиционно нет способа защитить транзакции в javascript, поэтому прямой доступ к API невозможен. Внешний интерфейс должен будет взаимодействовать с серверным кодом, который, в свою очередь, напрямую связывается с API. Однако при исследовании OAuth2 создается впечатление, что User-Agent Flow призван помочь в этой ситуации.

Мне нужна помощь с реализацией потока агента пользователя OAuth2 в javascript (особенно AngularJS, если это возможно, поскольку это то, что я использую для своего внешнего интерфейса). Я не смог найти никаких примеров или руководств, которые делают это. Я действительно понятия не имею, с чего начать, и не хочу читать всю спецификацию OAuth2, не увидев хотя бы пример того, что я буду делать. Поэтому любые примеры, учебные пособия, ссылки и т. д. будут очень признательны.


person David Myers    schedule 11.07.2012    source источник
comment
Через связанные вопросы по SO я нашел эту библиотеку JS, которая упрощает и обрабатывает то, что я пытаюсь сделать. github.com/andreassolberg/jso Похоже, он довольно маленький и неиспользуемый. К тому же это не решает мою проблему полностью.   -  person David Myers    schedule 12.07.2012
comment
Вы разобрались?   -  person Abdelghani Roussi    schedule 15.05.2015
comment
@DavidMyers Вы получили ошибку CORS при выполнении этого Javascript. Как вы решили это   -  person Sourav Das    schedule 20.10.2016


Ответы (2)


неявный поток предоставления грантов (один вы имеете в виду User-Agent Flow) именно так:

Неявное предоставление — это упрощенный поток кода авторизации, оптимизированный для клиентов, реализованных в браузере с использованием языка сценариев, такого как JavaScript.

Чтобы понять процесс, документация от Google для клиентских приложений действительно хорошее место для начала. Обратите внимание, что они рекомендуют вам выполнить дополнительный шаг проверки токена, чтобы избежать запутанных проблем с заместителем< /а>.

Вот краткий пример реализации потока с использованием 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>

Для отправки XMLHttpRequest (что делает функция ajax() в jQuery) с помощью AngularJS, обратитесь к их документации по $http служба.

Если вы хотите сохранить состояние при отправке пользователя в конечную точку авторизации, ознакомьтесь с state.

person Jan Gerlinger    schedule 17.07.2012
comment
Чтобы это работало с последним API SoundCloud, измените регулярное выражение на /access_token=([\w-]+)/. Их токен теперь включает символ тире. - person Björn Lindqvist; 14.03.2013
comment
Спасибо, я отредактировал ответ. Вероятно, разрешены и другие символы, но я не уверен, что это где-либо задокументировано. - person Jan Gerlinger; 18.03.2013
comment
Насколько я понимаю, «неявный» тип гранта предназначен для сторонних приложений. На сервере ожидается шаг перенаправления «разрешить аутентификацию». Вы бы не хотели этого в стороннем приложении, то есть в своем собственном клиенте, с точки зрения пользовательского интерфейса (это могло бы сбить с толку). Поэтому единственным вариантом является предоставление типа «Учетные данные владельца ресурса» или разработка «прокси-сервиса» на сервере для получения маркера носителя. - person Onshop; 10.04.2015
comment
@Ben Одно из преимуществ предоставления неявного кода и кода аутентификации заключается в том, что клиентское приложение (неважно, первое или стороннее) не должно беспокоиться о сохранении учетных данных пользователя в виде простого текста. Это типичный компромисс между безопасностью и удобством использования. - person Jan Gerlinger; 13.04.2015
comment
@Ян. Спасибо за разъяснения. Имеет ли значение, если некоторые общие учетные данные клиента хранятся в клиенте в виде простого текста, если имя пользователя и пароль пользователя также необходимы для полной аутентификации (и они не будут храниться или передаваться в виде простого текста)? - person Onshop; 26.04.2015
comment
@Ben Ну, вы рискуете, что какой-нибудь злонамеренный клиент использует ваши учетные данные клиента, чтобы обмануть пользователей. Зависит от вашего варианта использования, если это имеет значение. Дополнительные сведения можно найти в документах по модели угроз OAuth 2. - person Jan Gerlinger; 27.04.2015
comment
Разве это не должно быть xhr.setRequestHeader('Authorization', "Bearer " + token)? - person Emile Bergeron; 08.08.2016
comment
@EmileBergeron Если это то, что в настоящее время работает с API Soundcloud, не стесняйтесь предлагать редактирование ответа. Документы API Soundcloud в любом случае, похоже, теперь поддерживают передачу параметра запроса с токеном. Не уверен, зачем им это?! - person Jan Gerlinger; 09.08.2016
comment
Клиенты НЕ ДОЛЖНЫ использовать неявное предоставление и любой другой тип ответа, заставляющий сервер авторизации выдавать токен доступа в ответе авторизации. Вместо этого клиентам СЛЕДУЕТ использовать код типа ответа (также известный как тип предоставления кода авторизации): tools.ietf.org/html/ - person ecoe; 26.04.2020

Вот пример подхода Authorization Code Grant для получения токена с сервера 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.
}

Вы можете реализовать Resource Owner Password Credentials Grant таким же образом, используя jQuery или чистый XMLHttpRequest, и не делать никаких перенаправлений, потому что при каждом перенаправлении вы теряете состояние своего приложения.

Я использовал локальное хранилище HTML5 для сохранения состояния моего приложения для данных, которые вряд ли могли представлять угрозу безопасности.

person Artem Oboturov    schedule 15.07.2012
comment
В этом случае использование потока кода авторизации не дает дополнительных преимуществ по сравнению с потоком неявного предоставления. Кроме того, если API поддерживает разные потоки авторизации для большего (сокрытие секрета) и меньшего (раскрытие секрета) доверенных клиентов, сервер может различать их и выполнять, например. дополнительные проверки или ограничение объема. Кроме того, в этом случае не следует использовать поток учетных данных владельца ресурса, поскольку у владельца ресурса не может быть высокая степень доверия к коду клиента JS со стороннего веб-сайта. - person Jan Gerlinger; 17.07.2012
comment
Какое перенаправление вы имеете в виду? Тот, что к конечной точке авторизации? Это перенаправление также является частью процесса неявного предоставления. - person Jan Gerlinger; 17.07.2012
comment
Это было бы плохой идеей. Вы никогда не должны использовать предоставление авторизации на мобильном устройстве или в клиентском приложении, если вы цените безопасность своих пользователей и свою собственную. В этой системе вам пришлось бы хранить свой секрет клиента OAuth2 в приложении где-то, где хакер мог бы легко его найти (например, в исходном коде). Если бы вы включили токены обновления в запрос, вы потенциально передали бы практически постоянные ключи к данным пользователя любому хакеру с минимальными навыками. - person Michael Oryl; 22.12.2015
comment
@MichaelOryl Вам не нужно иметь секрет клиента для предоставления пароля в соответствии со спецификацией. Ваш сервер oauth должен защищать клиента с помощью URL-адресов из белого списка, чтобы предотвратить незаконное использование клиента. - person jle; 16.11.2016
comment
+1, так как предоставление кода аутентификации недавно задокументировано как лучшая практика для клиентского кода: tools.ietf.org/html/ - person ecoe; 26.04.2020