Создание гаджетов, использующих OAuth

В этом документе описано, как создавать гаджеты, использующие для аутентификации функцию OAuth. OAuth используется вместе с функцией makeRequest(). Общее описание использования функции makeRequest() для получения удаленного содержания см. в разделе Получение удаленного содержания.

Этот документ основан в основном на простейшем случае использования: создание гаджетов, работающих на iGoogle и использующих личные данные пользователя с любого веб-сайта, поддерживающего протокол OAuth. Но это только один из способов использования OAuth. В принципе любой гаджет OpanSocial, работающий в любом контейнере сможет использовать методы, описанные в этом документе, для доступа к данным, защищенным OAuth. Как контейнер iGoogle предлагает простейшую модель, потому что гаджеты, добавляемые пользователем на iGoogle, видны только этому пользователю. Все несколько сложнее в контейнерах социальных сетей, где человек, просматривающий гаджет, может не быть владельцем данных, добавившим гаджет. В документе Гаджеты, работающие с OAuth, в социальных сетях OpenSocial описано, как поставщики служб поддерживают OAuth в контейнерах социальных сетей.

Что такое прокси OAuth?

Многие службы в Интернете начинают предлагать API REST, дающие доступ к личным данным пользователей, хранимым от их лица этими интернет-службами. Например, интернет-службы могут давать пользователям доступ к их фотографиям, почте, адресной книге, записям о здоровье и так далее. OAuth – это стандарт, позволяющий владельцу аккаунта с личными данными разрешить службе хостинга обеспечивать доступ другому веб-сайту (или гаджету) к эти данным. Прокси OAuth разработан для того, чтобы гаджетам было проще использовать стандарт OAuth.

Примечание. Прокси OAuth поддерживается только в API gadgets.* для гаджетов, работающих в контейнерах OpenSocial. Он не поддерживается API для старых гаджетов. На настоящий момент iGoogle – это контейнер гаджетов, поддерживающий эту функцию. Но есть еще проект с открытым исходным кодом под названием Shindig – реализация спецификации гаджетов и функции прокси OAuth. Множество других веб-сайтов планирует запустить эту технологию.

Аудитория

Данный документ предназначен для разработчиков гаджетов, использующих протокол OAuth при обмене данными с пользователями. Здесь предполагается, что вы знакомы со службами, которыми пользуетесь, и знаете о возможных проблемах доступа/аутентификации. Для использования прокси-службы аутентификации гаджетов нужно располагать сведениями о конкретной службе.

Содержание

  1. Что такое прокси OAuth?
  2. Какие службы в Интернете имеют API REST, поддерживающие OAuth?
  3. Основные принципы
  4. Выбор поставщика служб
    1. Поставщик служб Google
    2. Поставщики служб, не связанные с Google
  5. Выбор конечной точки
  6. Разбор примера гаджета
  7. Реализация гаджета OAuth
    1. Пример гаджета
    2. Раздел OAuth
    3. Реализация потока одобрения
    4. Подробнее о makeRequest()
  8. API данных Google: альтернативный подход
  9. Пропуск всплывающего окна
    1. Реализация URL предварительного одобрения
    2. Добавление настроек пользователя
  10. Другие примеры
  11. Дополнительные советы по OAuth

Какие службы в Интернете имеют API REST, поддерживающие OAuth?

OAuth поддерживается всеми API REST, предоставляемыми Google (см. документацию Аутентификация веб-приложений с помощью OAuth и API данных Google), и API доступности данных на MySpace. В wiki сообщества OAuth перечислены и другие поставщики служб, предоставляющие API, работающие с OAuth.

Основные принципы

В таблице ниже перечислены определения основных терминов OAuth. Дополнительные сведения можно найти в Спецификации OAuth и Аутентификация веб-приложений с помощью OAuth.

Термин Определение
Поставщик служб Веб-приложение,обеспечивающее доступ через OAuth. Например, Google или MySpace.
Пользователь Лицо, имеющее аккаунт у поставщика служб, для которого у поставщика служб могут быть данные, доступные через OAuth. Например, гаджет может пользоваться данными календаря Google пользователя через OAuth.
Потребитель Веб-сайт или приложение, использующее OAuth для доступа к поставщику служб от лица пользователя. В этом контексте, гаджет, использющий OAuth для доступа к данным пользователя, будет потребителем.
Защищенные ресурсы Данные, контролируемые поставщиком служб, которые потребитель (гаджет) может использовать после аутентификации.
Контейнер Контейнер – это среда OpenSocial, в которую встроены гаджеты. Например, iGoogle – это контейнер. Контейнер ответственен за показ гаджета и элементов управления и поддерживает различные функции гаджетов. Гаджет OAuth может работать только в контейнере, поддерживающем OAuth. Если гаджет использует OAuth, то именно контейнер выполняет протокол OAuth от имени гаджета, обеспечивая все цифровые подписи, требуемые протоколом.
Ключ потребителя Значение, которое гаджет использует для представления себя поставщику служб. Соответствует параметру oauth_consumer_key. Подробности см. в Спецификации OAuth.
Секрет потребителя Секрет, используемый гаджетом для присвоения ключа потребителя.
Идентификатор запроса Значение, используемое гаджетом для получения авторизации от пользователя, обменивается для идентификатор доступа.
Идентификатор доступа Значение, используемое гаджетом для получения доступа к защищенным ресурсам от имени пользователя, используется вместо учетных данных пользователя в системе поставщика служб.
Секрет идентификатора Секрет, используемый гаджетом для присвоения данного идентификатора.

Например, допустим, что у нас есть гаджет, получающий доступ к личным данным календаря пользователя через API данных Google. В этом случае Google – это поставщик служб, а гаджет – потребитель. Пользователь – это тот человек, который пользуется гаджетом для доступа к личным данным календаря.

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

Выбор поставщика служб

Первый этап создания гаджета OAuth – выбор поставщика служб. Есть два случая использования OAuth:

  • Запрос данных с Данных Google или другого поставщика служб Google.
  • Запрос данных у поставщика служб, кроме Google.

Это подробно рассматривается ниже.

Поставщик служб Google

Если вы получаете доступ к API данных Google, все, что нужно – определить конечную точку API REST данных Google, к которой нужен доступ. Конечная точка определяется в разделе <OAuth> (в разделе <ModulePrefs>) и в коде гаджета, использующем конечную точку для запроса данных.

Поставщики служб, не связанные с Google

Доступ к конечной точке, не связанной с Google, обычно требует регистрации приложения у поставщика служб, не связанного с Google (например, регистрация MySpace). Если компания хочет открыть собственную конечную точку поставщика служб OAuth, даже если это только для своих гаджетов, может быть полезно почитать поддерживаемый Google сайт Дополнительные советы по OAuth

После регистрации приложения у поставщика служб, не связанного с Google, этот поставщик дает вам секрет потребителя OAuth, используемый для цифровой подписи всех запросов вашего приложения к поставщику служб. Гаджеты общедоступны, поэтому в них не стоит хранить эти секреты. Вместо этого можно зарегистрировать секрет потребителя OAuth на веб-сайте, предоставляющем контейнер для вашего гаджета, если веб-сайт предоставляет прокси OAuth.

В случае iGoogle дял регистрации своего секрета потребителя OAuth можно отправить письмо по адресу oauthproxyreg@google.com со следующими сведениями:

  • URL гаджета.
  • Ключ потребителя OAuth, выданный вам поставщиком служб. Ключ потребителя OAuth – это домен, определяющий стороннее веб-приложение. Это домен, использующийся при регистрации приложения у поставщика служб, чтобы тот мог узнавать запросы от вашего приложения.
  • Секрет потребителя OAuth, выданный вам поставщиком служб. Этот секрет используется для цифровой подписи всех запросов вашего приложения поставщику служб.
  • Следует ли использовать с этим поставщиком служб симметричные или асимметричные подписи (или укажите,что не знаете).

Пока секрет потребителя OAuth не зарегистрирован, гаджет работать не будет. При изменении URL гаджета нужно заново зарегистрировать секрет для этого гаджета.

Многие поставщики служб OAuth требуют предоставить URL для обратного вызова OAuth при запросе ключа и секрета потребителя OAuth. Можно определить URL обратного вызова http://oauth.gmodules.com/gadgets/oauthcallback для своего гаджета.

Примечание для поставщиков служб OAuth

Если вы хотите помочь своему сообществу разработчиков избежать этого шага ручной регистрации, можно расширить параметры OAuth так, чтобы принимались цифровые подписи непосредственно с iGoogle. На настоящий момент iGoogle использует метод подписи RSA_SHA1 (по определению встандарте OAuth) и следующий публичный ключ, представленный здесь как самостоятельно подписанный сертификат, который легко импортировать в библиотеки OAuth, реализованные в различных языках программирования:

-----BEGIN CERTIFICATE-----
MIIDBDCCAm2gAwIBAgIJAK8dGINfkSTHMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV
BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEG
A1UEChMKR29vZ2xlIEluYzEXMBUGA1UEAxMOd3d3Lmdvb2dsZS5jb20wHhcNMDgx
MDA4MDEwODMyWhcNMDkxMDA4MDEwODMyWjBgMQswCQYDVQQGEwJVUzELMAkGA1UE
CBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBJ
bmMxFzAVBgNVBAMTDnd3dy5nb29nbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GN
ADCBiQKBgQDQUV7ukIfIixbokHONGMW9+ed0E9X4m99I8upPQp3iAtqIvWs7XCbA
bGqzQH1qX9Y00hrQ5RRQj8OI3tRiQs/KfzGWOdvLpIk5oXpdT58tg4FlYh5fbhIo
VoVn4GvtSjKmJFsoM8NRtEJHL1aWd++dXzkQjEsNcBXwQvfDb0YnbQIDAQABo4HF
MIHCMB0GA1UdDgQWBBSm/h1pNY91bNfW08ac9riYzs3cxzCBkgYDVR0jBIGKMIGH
gBSm/h1pNY91bNfW08ac9riYzs3cx6FkpGIwYDELMAkGA1UEBhMCVVMxCzAJBgNV
BAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUg
SW5jMRcwFQYDVQQDEw53d3cuZ29vZ2xlLmNvbYIJAK8dGINfkSTHMAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAYpHTr3vQNsHHHUm4MkYcDB20a5KvcFoX
gCcYtmdyd8rh/FKeZm2me7eQCXgBfJqQ4dvVLJ4LgIQiU3R5ZDe0WbW7rJ3M9ADQ
FyQoRJP8OIMYW3BoMi0Z4E730KSLRh6kfLq4rK6vw7lkH9oynaHHWZSJLDAp17cP
j+6znWkN9/g=
-----END CERTIFICATE-----

iGoogle использует черновик Расширения OAuth для гаджетов для предоставления URL гаджета, от имени которого выполняется запрос к поставщикам служб.

Выбор конечной точки

Конечная точка – это URL, который гаджет использует для доступа к личным данным через поставщика служб. В документации вашего поставщика служб должны быть указаны доступные конечные точки. Если гаджет запрашивает данные через службу Google, поддерживающую API данных Google, конечные точки, поддерживаемые разными службами, можно найти в документации API данных Google. Для некоторых конечных точек нужна аутентификация (получаемая через OAuth), для некоторых – нет. Вот несколько примеров конечных точек данных Google:

  • http://www.google.com/m8/feeds/contacts/default/full/ – получить фид, содержащий список контаков вошедшего в систему пользователя.
  • http://gdata.youtube.com/feeds/api/users/default/uploads?alt=json – получить фид в формате JSON, содержащий видео YouTube, добавленные вошедшим в систему пользователем.
  • http://gdata.youtube.com/feeds/api/users/username/uploads – получить фид, содержащий видео YouTube для пользователя по имени пользователя. Этот запрос не требует аутентификации.

Гаджет может получить доступ к данным только в определенном слое. Если гаджет запрашивает доступ к scope=http://www.google.com/m8/feeds/, он может использовать конечные точки (фиды) только из API Contacts. Один гаджет может запрашивать несколько слоев.

Разбор примера гаджета

В этом документе работа гаджетов OAuth описана на примере простого гаджета. Гаджет в примере получает и отображает список контактов вошедшего в систему пользователя. Прочтите этот разбор, чтобы представить себе, как работают гаджеты OAuth.

Гаджеты OAuth должны быть основаны на API gadgets.* и работать в контейнере OpenSocial, поддерживающем OAuth. Гаджеты OAuth можно развертывать на iGoogle.

Для добавление гаджета на iGoogle сделайте следующее.

  1. Зайдите на страницу http://www.google.com/ig.
  2. Нажмите кнопку Настроить эту страницу. Вы попадете в каталог iGoogle.
  3. Нажмите ссылку добавить фид или гаджет внизу левой навигационной панели.
  4. Введите URL примера гаджета в текстовое поле (http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/oauth-contacts.xml) и нажмите Добавить.
  5. Для возвращения в iGoogle и просмотра примера гаджета нажмите ссылку Назад на главную страницу iGoogle в правом левом углу.

На добавленном примере гаджета видна ссылка "Настроить этот гаджет". Эта ссылка содержит идентификатор запроса. Для того, чтобы дать доступ к своим данным, выполните следующее.

  1. Нажмите ссылку "Настроить этот гаджет".
  2. Если у вас несколько аккаунтов, может появиться страница аккаунтов Google. На этой странице написано "Сторонняя служба просит разрешения получить доступ к вашему аккаунту Google". Выберите аккаунт, данные которого нужно использовать.
  3. Затем вы увидите страницу запроса доступа к аккаунту Google. На ней будет написано что-то вроде " Сайт www.google.ru запрашивает доступ к вашему аккаунту Google для продукта (-ов), указанного (-ых) ниже". Перечислены продукты, данные которых хочет использовать сайт (контейнер). Нажмите Дать доступ. При этом идентификатор запроса обменивается на идентификатор доступа, который позволяет гаджету получать ваши данные из указанной службы.
  4. Вернитесь на iGoogle. Гаджет должен показывать ваши данные. Подробности см. в разделе Реализация потока одобрения.

Начиная с этого момента, если вы не удалите гаджет или не отмените доступ через аккаунт Google, гаджет сможет постоянно пользоваться вашими данными. Доступ нужно давать только один раз.

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

  • Попытаться получить данные пользователя.
  • Если попытка оказывается успешной, отображать данные.
  • Если нет, попросить пользователя дать доступ с помощью всплывающего окна.
  • После закрытия всплывающего окна попытаться еще раз получить данные пользователя. После связи с гаджетом идентификатора доступа данные получаются автоматически при каждом запуске гаджета.

Проект Shindig предоставляет библиотеки JavaScript, которые можно использовать для создания всплывающего окна и контроля момента закрытия этого окна. Дополнительные сведения об этом есть в разделе Реализация потока одобрения.

Реализация гаджета OAuth

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

Кроме обычных функций гаджетов, гаджет OAuth должен включать следующее.

  • Упомянутого поставщика служб.
  • Конечную точку.
  • Раздел <OAuth...> в разделе гаджета<ModulePrefs>, определяющий сведения о любых службах и конечных точках, используемых гаджетом.
  • Механизм определения, дал ли пользователь доступ к данным. Если пользователь еще не дал доступа, гаджет должен обеспечить ему возможность перейти к поставщику служб, например, вывести для пользователя ссылку на URL авторизации OAuth на сайте поставщика служб. Поставщик служб поможет пользователю пройти процесс аутентификации и одобрения. После одобрения пользователем доступа к своим данным гаджет сможет ими пользоваться.
  • Вызов к функции gadgets.* makeRequest() с соответствующими параметрами OAuth для получения данных после аутентификации.
  • Код для обработки возвращенных данных, подходящий в контексте этого конкретного гаджета.

В этом разделе мы рассмотрим пример гаджета OAuth по разделам, чтобы понять, как он работает.

Пример гаджета

Этот пример гаджета получает список контактов вошедшего в систему пользователя через API данных Google Contacts. Он использует конечную точку http://www.google.com/m8/feeds/contacts/default/full, которая предписывает серверу вернуть список знакомых для пользователя, чьи учетные данные включены в запрос. Сведения о контактах возвращаются как результат ATOM XML в формате JSON (http://www.google.com/m8/feeds/contacts/default/full?alt=json). Дополнительная информация – в разделе Использование JSON с API данных Google.

Вот законченный гаджет. Ниже он обсуждается подробнее.

<?xml version="1.0" encoding="UTF-8" ?> 
<Module>
  <ModulePrefs title="OAuth Contacts" scrolling="true">
    <Require feature="opensocial-0.8" />
    <Require feature="locked-domain"/> 
    <OAuth>
      <Service name="google">
        <Access url="https://www.google.com/accounts/OAuthGetAccessToken" method="GET" /> 
        <Request url="https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www.google.com/m8/feeds/" method="GET" /> 
        <Authorization url="https://www.google.com/accounts/OAuthAuthorizeToken?oauth_callback=http://oauth.gmodules.com/gadgets/oauthcallback" /> 
      </Service>
    </OAuth>
  </ModulePrefs>
  <Content type="html">
  <![CDATA[ 
 
  <!-- shindig oauth popup handling code -->
  <script src="http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/popup.js"></script>

  <style>
  #main {
    margin: 0px;
    padding: 0px;
    font-size: small;
  }
  </style>

  <div id="main" style="display: none">
  </div>

  <div id="approval" style="display: none">
    <img src="http://gadget-doc-examples.googlecode.com/svn/trunk/images/new.gif">
    <a href="#" id="personalize">Personalize this gadget</a>
  </div>

  <div id="waiting" style="display: none">
    Please click
    <a href="#" id="approvaldone">I've approved access</a>
    once you've approved access to your data.
  </div>

  <script type="text/javascript">
    // Display UI depending on OAuth access state of the gadget (see <divs> above).
    // If user hasn't approved access to data, provide a "Personalize this gadget" link
    // that contains the oauthApprovalUrl returned from makeRequest.
    //
    // If the user has opened the popup window but hasn't yet approved access, display
    // text prompting the user to confirm that s/he approved access to data.  The user
    // may not ever need to click this link, if the gadget is able to automatically
    // detect when the user has approved access, but showing the link gives users
    // an option to fetch their data even if the automatic detection fails.
    //
    // When the user confirms access, the fetchData() function is invoked again to
    // obtain and display the user's data.
    function showOneSection(toshow) {
      var sections = [ 'main', 'approval', 'waiting' ];
      for (var i=0; i < sections.length; ++i) {
        var s = sections[i];
        var el = document.getElementById(s);
        if (s === toshow) {
          el.style.display = "block";
        } else {
          el.style.display = "none";
        }
      }
    }
      
    // Process returned JSON feed to display data.
    function showResults(result) {
      showOneSection('main');

      var titleElement = document.createElement('div');
      var nameNode = document.createTextNode(result.feed.title.$t);
      titleElement.appendChild(nameNode);
      document.getElementById("main").appendChild(titleElement);
      document.getElementById("main").appendChild(document.createElement("br"));

      list = result.feed.entry;

      for(var i = 0; i < list.length; i++) {
        entry = list[i];
        var divElement = document.createElement('div');
        divElement.setAttribute('class', 'name');
        var valueNode = document.createTextNode(entry.gd$email[0].address);
        divElement.appendChild(nameNode);
        divElement.appendChild(valueNode);
        document.getElementById("main").appendChild(divElement);
      }
    }

    // Invoke makeRequest() to fetch data from the service provider endpoint.
    // Depending on the results of makeRequest, decide which version of the UI
    // to ask showOneSection() to display. If user has approved access to his
    // or her data, display data.
    // If the user hasn't approved access yet, response.oauthApprovalUrl contains a
    // URL that includes a Google-supplied request token. This is presented in the 
    // gadget as a link that the user clicks to begin the approval process.     
    function fetchData() {
      var params = {};
      url = "http://www.google.com/m8/feeds/contacts/default/base?alt=json";
      params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
      params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.OAUTH;
      params[gadgets.io.RequestParameters.OAUTH_SERVICE_NAME] = "google";
      params[gadgets.io.RequestParameters.OAUTH_USE_TOKEN] = "always";
      params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;

      gadgets.io.makeRequest(url, function (response) { 
        if (response.oauthApprovalUrl) {
          // Create the popup handler. The onOpen function is called when the user
          // opens the popup window. The onClose function is called when the popup
          // window is closed.
          var popup = shindig.oauth.popup({
            destination: response.oauthApprovalUrl,
            windowOptions: null,
            onOpen: function() { showOneSection('waiting'); },
            onClose: function() { fetchData(); }
          });
          // Use the popup handler to attach onclick handlers to UI elements.  The
          // createOpenerOnClick() function returns an onclick handler to open the
          // popup window.  The createApprovedOnClick function returns an onclick 
          // handler that will close the popup window and attempt to fetch the user's
          // data again.
          var personalize = document.getElementById('personalize');
          personalize.onclick = popup.createOpenerOnClick();
          var approvaldone = document.getElementById('approvaldone');
          approvaldone.onclick = popup.createApprovedOnClick();
          showOneSection('approval');
        } else if (response.data) {
          showOneSection('main');
          showResults(response.data);
        } else {
          // The response.oauthError and response.oauthErrorText values may help debug
          // problems with your gadget.
          var main = document.getElementById('main');
          var err = document.createTextNode('OAuth error: ' +
            response.oauthError + ': ' + response.oauthErrorText);
          main.appendChild(err);
          showOneSection('main');
        }
      }, params);
    }
    // Call fetchData() when gadget loads.
    gadgets.util.registerOnLoadHandler(fetchData);
  </script>
  ]]> 
  </Content>
</Module>

Примечание. Мы рекомендуем включить функцию locked-domain, вставив в гаджет строку <Require feature="locked-domain"/>. Кроме того, советуем держать у себя копию popup.js.

Раздел <OAuth>

В первую очередь нужно включить в гаджет раздел <OAuth> в разделе <ModulePrefs>:

<?xml version="1.0" encoding="UTF-8" ?> 
<Module>
  <ModulePrefs title="OAuth Contacts" scrolling="true">
    <Require feature="opensocial-0.8" /> 
    <Require feature="locked-domain"/> 
    <OAuth>
      <Service name="google">
        <Access url="https://www.google.com/accounts/OAuthGetAccessToken" method="GET" /> 
        <Request url="https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www.google.com/m8/feeds/" method="GET" /> 
        <Authorization url="https://www.google.com/accounts/OAuthAuthorizeToken?oauth_callback=http://oauth.gmodules.com/gadgets/oauthcallback" /> 
      </Service>
    </OAuth>
  </ModulePrefs>

Раздел OAuth предоставляет контейнер конфигурацией службы OAuth для гаджета следующим образом:

Элемент Описание
/ModulePrefs/OAuth/Service Этот элемент – одна конфигурация службы OAuth.

Атрибут:
  • name – название службы, используемой для ссылки на службы OAuth в среде выполнения. Это необязательный параметр. Если он не указан, по умолчанию равняется "". Разработчики гаджетов указывают, какую службу OAuth нужно использовать, передавая название службы как параметр makeRequest().
/ModulePrefs/OAuth/Service/Request
/ModulePrefs/OAuth/Service/Access
Эти элементы предствляют идентификатор запроса OAuth и URL-ссылки идентификатора доступа. Подробности см. в Спецификации OAuth и разделе Аутентификация веб-приложений с помощью OAuth.

Атрибуты:
  • url – URL конечной точки.
  • method – операция HTTP, используемая для создания запроса. Это необязательный параметр. Если он не указан, по умолчанию имеет значение POST.
/ModulePrefs/OAuth/Service/Authorization URL авторизации OAuth. Если гаджету нужно одобрение пользователя для доступа к его данным, он открывает всплывающее окно по этому URL .

URL авторизации включает параметр запроса oauth_callback . Поставщик службы OAuth перенаправляет пользователя на этот URL после того, как пользователь одобрил доступ. Следует указать http://oauth.gmodules.com/gadgets/oauthcallback в качестве своего URL oauth_callback. На странице oauthcallback содержится сниппет JavaScript, автоматически закрывающий всплывающее окно. Гаджет может определить, когда закрывается всплывающее окно и попытаться снова получить данные пользователя.

Реализация потока одобрения

Гаджет OAuth получает данные для аутентифицированного пользователя. Для этого пользователь должен дать службе (в этом примере, службе данных Google Contacts) разрешение передавать гаджету данные пользователя.

Например, посмотрим на пример гаджета для списка контактов. Последовательнсть событий такова.

  1. При запуске гаджета он вызывает gadgets.util.registerOnLoadHandler(fetchData), вызывающий функцию fetchData().
  2. Функция fetchData() вызывает makeRequest().
  3. Функция makeRequest() определяет параметр обратного вызова. Параметр обратного вызова передается объекту JavaScript с несколькими полями, особенными для OAuth, помимо обычных значений, возвращаемых makeRequest().

Вот функция fetchData():

function fetchData() {
  var params = {};
  url = "http://www.google.com/m8/feeds/contacts/default/base?alt=json";
  params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
  params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.OAUTH;
  params[gadgets.io.RequestParameters.OAUTH_SERVICE_NAME] = "google";
  params[gadgets.io.RequestParameters.OAUTH_USE_TOKEN] = "always";
  params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;

  gadgets.io.makeRequest(url, function (response) { 
    if (response.oauthApprovalUrl) {
      // Create the popup handler. The onOpen function is called when the user
      // opens the popup window. The onClose function is called when the popup
      // window is closed.
      var popup = shindig.oauth.popup({
        destination: response.oauthApprovalUrl,
        windowOptions: null,
        onOpen: function() { showOneSection('waiting'); },
        onClose: function() { fetchData(); }
      });
      // Use the popup handler to attach onclick handlers to UI elements.  The
      // createOpenerOnClick() function returns an onclick handler to open the
      // popup window.  The createApprovedOnClick function returns an onclick 
      // handler that will close the popup window and attempt to fetch the user's
      // data again.
      var personalize = document.getElementById('personalize');
      personalize.onclick = popup.createOpenerOnClick();
      var approvaldone = document.getElementById('approvaldone');
      approvaldone.onclick = popup.createApprovedOnClick();
      showOneSection('approval');
    } else if (response.data) {
      showOneSection('main');
      showResults(response.data);
    } else {
      // The response.oauthError and response.oauthErrorText values may help debug
      // problems with your gadget.
      var main = document.getElementById('main');
      var err = document.createTextNode('OAuth error: ' +
        response.oauthError + ': ' + response.oauthErrorText);
      main.appendChild(err);
      showOneSection('main');
    }
  }, params);
}

При обработке функции обратного вызова она сперва проверяет, не нулевое ли значение у response.oauthApprovalUrl. Если пользователь еще не дал доступа к своим данным, response.oauthApprovalUrl содержит URL, по которому пользователь должен перейти, чтобы дать гаджету доступ к данным. Часть этого URL – идентификатор запроса, выданный поставщиком служб (в этом случае Google).

Сперва гаджет создает объект shindig.oauth.popup для управления всплывающим окном. Объект shindig.oauth.popup принимает несколько параметров.

var popup = shindig.oauth.popup({
  destination: response.oauthApprovalUrl,
  windowOptions: null,
  onOpen: function() { showOneSection('waiting'); },
  onClose: function() { fetchData(); }
});

Параметр destination определяет URL, который откроется во всплывающем окне.

Параметр windowOptions определяет, какие параметр следует передать функции window.open браузера. Можно контролировать размер, положение и оформление всплывающего окна. Разные браузеры поддерживают разные параметры window.open. Например, посмотрите документацию Internet Explorer и Firefox.

Когда пользователь нажмет на ссылку, открываюшую всплывающее окно, будет вызвана функция onOpen. Гаджет вызывает showOneSection('waiting'), чтобы показывать соответствующее сообщение, ожидая одобрения доступа пользователем.

Функция onClose вызывается при закрытии всплывающего окна. После того, как всплывающее окно закрылось, гаджет региструрует вызов к fetchData() для получения данных пользователя.

После создания объекта всплывающего окна нужно связать обработчики onclick с элементами DOM, чтобы открывать окно. Для того, чтобы открыть всплывающее окно, гаджет устанавливает обработчик onclick на HREF personalize в approval <div>:

var personalize = document.getElementById('personalize');
personalize.onclick = popup.createOpenerOnClick();

Гаджет также настраивает HREF approvaldone в waiting <div> на обработчик popup.createApprovedOnClick(). При нажатии HREF approvaldone гаджет закрывает всплывающее окно и пытается снова получить данные пользователя. Скорее всего, пользователю не придется нажимать ссылку Одобрение выдано, но на всякий случай стоит присоединить обработчик onclick:

var approvaldone = document.getElementById('approvaldone');
approvaldone.onclick = popup.createApprovedOnClick();

Гаджет отображает approval <div>, чтобы попросить пользовталя нажать ссылку, открывающую всплывающее окно. Многие браузеры используют программы блокирования всплывающих окон, чтобы не показывать нежелательные всплывающие окна. Для того, чтобы не столкнуться с программой блокирования всплывающих окон, гаджет не должен открывать всплывающее окно, пока пользователь не нажал на кнопку или ссылку.

showOneSection('approval');

Гаджет использует <div> и функцию showOneSection(), чтобы показывать подходящие элементы пользовательского интерфейса в зависимости от состояния одобрения гаджета. Для каждого из трех возможных состояний гаджета есть свой <div>.

  • approval – если пользователь еще не дал доступа, гаджет использует approval <div> для отображения интерфейса, содержащего ссылку "Настроить этот гаджет", в которую входит идентификатор запроса. Для начала процесса одобрения пользователь нажимает на ссылку.
  • waiting – гаджет отображает этот <div>, если пользователь открыл всплывающее окно, но еще не дал одобрения. Гаджет отображает текст, предлагающий пользователю подтвердить, что он дал доступ к данным. Пользователю, скорее всего, не придется нажимать эту ссылку, если гаджет может автоматически определить, что пользователь одобрил доступ, но если эта ссылка есть, пользователь сможет получить данные, даже если автоматическое определение не сработало. В этом случае гаджет показывает сообщение "Нажмите Я одобрил доступ после одобрения доступа к вашим данным". Когда пользователь нажимает на эту ссылку, гаджет получает данные пользователя, вызвав fetchData().
  • main – когда идентификатор доступа установлен, гаджет использует main <div> для отображения данных пользователя при каждом запуске. Этот <div> используется также для отображения любых ошибок.
<div id="main" style="display: none">
  </div>

  <div id="approval" style="display: none">
    <img src="http://gadget-doc-examples.googlecode.com/svn/trunk/images/new.gif">
    <a href="#" id="personalize">Personalize this gadget</a>
  </div>

  <div id="waiting" style="display: none">
    Please click
    <a href="#" id="approvaldone">I've approved access</a>
    once you've approved access to your data.
  </div>

  <script type="text/javascript">
    function showOneSection(toshow) {
      var sections = [ 'main', 'approval', 'waiting' ];
      for (var i=0; i < sections.length; ++i) {
        var s = sections[i];
        var el = document.getElementById(s);
        if (s === toshow) {
          el.style.display = "block";
        } else {
          el.style.display = "none";
        }
      }
    }

Подробнее о makeRequest()

Функция makeRequest() подробно описана в разделе Получение удаленного содержания. Ее можно использовать для получения и обработки удаленного содержания в Интернете. Она принимает следующие аргументы.

  • String url – URL, где расположено содержимое
  • Function callback – функция, которую следует вызвать с данными из URL после создания выборки
  • Map.<gadgets.io.RequestParameters, Object> opt_params – дополнительные параметры для прохождения запроса

Если opt_params[gadgets.io.RequestParameters.AUTHORIZATION] установлено как gadgets.io.AuthorizationType.OAUTH, контейнер должен использовать OAuth для доступа к ресурсу, указанному в запросе. Это может потребовать получения гаджетом пользовательского содержания за счет направления пользователя к поставщику служб для обеспечения доступа.

Дополнительные параметры

Эти дополнительные параметры OAuth можно определить в opt_params:

Параметр Описание
gadgets.io.RequestParameters.OAUTH_SERVICE_NAME Название, которое гаджет использует для элемента OAuth <Service> из своей спецификации XML. Название службы можно также определить в разделе XML /ModulePrefs/OAuth/Service под <ModulePrefs>. Если оно не указано ни там, ни там, по умолчанию равняется "".
gadgets.io.RequestParameters.OAUTH_TOKEN_NAME Название, используемое гаджетом для идентификатора OAuth, дающего доступ к определенному ресурсу. Если он не указан, по умолчанию равняется "". Гаджеты могут использовать несколько названий идентификаторов, если у них есть доступ к нескольким ресурсам одного поставщика услуг. Например, гаджет с доступом к списку знакомых и календарю может использовать название идентификатора "знакомые", чтобы использовать идентификатор списка знакомых и название идентификатора "календарь", чтобы использовать идентификатор календаря.
gadgets.io.RequestParameters.OAUTH_REQUEST_TOKEN Поставщик услуг может автоматически выдававть гаджету идентификатор запроса, заранее одобренный для доступа к ресурсу. Гаджет может использовать этот идентификатор с параметром . Это необязательный параметр.
gadgets.io.RequestParameters.OAUTH_REQUEST_TOKEN_SECRET Секрет, соответствующий заранее одобренному идентификатору запроса. Это необязательный параметр.
gadgets.io.RequestParameters.OAUTH_USE_TOKEN

Этот параметр может иметь значения "never", "if_available" и "always". API некоторых поствщиков служб не требуют идентификатора доступа OAuth. Такие API аутентифициуют вызывающее приложение через ключ потребителя OAuth и затем позволяют выполнить запрос. Чтобы избежать ненужных запросов к пользователю на получение доступа, для таких API можно установить OAUTH_USE_TOKEN на "never".

Некоторые API OAuth предоставляют ограниченные данные, если пользователь не дал разрешения, но предлагают дополнительные функции, если пользователь дал вызывающему приложению идентификатор доступа. Если вы используете API, который может принимать идентифкатор доступа OAuth, но не требует индентификатора, можно установить OAUTH_USE_TOKEN на "if_available". Если пользователь уже одобрил доступ, будет отправлен идентификатор запроса. Если пользователь не дал доступа, запрос продолжится без идентификатора доступа.

Многие API OAuth работают только, если отправлен идентификатор доступа. Перед использованием такого API нужно попросить разрешения пользователя. Установите OAUTH_USE_TOKEN на "always" для того, чтобы сделать наличие идентификатора доступа обязательным. Если идентификатора доступа нет, гаджету будет возвращен URL для одобрения.

Если вы устанавливаете AUTHORIZATION на SIGNED, значение OAUTH_USE_TOKEN по умолчанию – "never". Если вы устанавливаете AUTHORIZATION на OAUTH, значение OAUTH_USE_TOKEN по умолчанию – "always".

При использовании OAuth контейнер должен выполнять протокол OAuth от имени гаджета. Как описано в Примечании для поставщиков служб OAuth, если гаджет не зарегистрировал ключ потребителя для использования на iGoogle, iGoogle использует ключ подписи RSA по умолчанию.

Параметр callback-функции

Параметр callback-функции makeRequest() передается объекту JavaScript с несколькими полями, особенными для OAuth, помимо обычных значений, возвращаемых makeRequest():

Поле OAuth Описание
oauthApprovalUrl Если определено это значение, оно содержит URL, включающий идентификатор запроса, выданный поставщиком служб. Если указано это значение, пользователь должен зайти на внешнюю страницу и одобрить запрос гаджета на доступ к данным. Рекомендуется использовать всплывающее окно для перенаправления пользователя на внешнюю страницу. После одобрения пользователем доступа, гаджет может повторить вызов makeRequest() для получения данных.
oauthError Это значение означает, что произошла ошибка, связанная с OAuth.
oauthErrorText Это значение означает, что произошла ошибка, связанная с OAuth. Это значение может предоставлять разрабочтикам гаджетов сведения для отладки. Параметры:
  • String url – URL, где расположено содержимое.
  • Function callback – функция, которую следует вызвать с данными из URL после получения данных.
  • Map.<gadgets.io.RequestParameters, Object> opt_params – дополнительные параметры запроса или параметры запроса прокси.

Расширения OpenSocial для OAuth

Контейнеры OpenSocial могут предоставлять сайту поставщика службы OAuth дополнительные сведения о контексте запроса, передавая дополнительные подписанные параметры в запросе OAuth. Дополнительные параметры, например opensocial_owner_id и opensocial_app_url, описаны в gadgets.io.makeRequest документации. Поставщики служб, поддерживающие OpenSocial, могут использовать эти параметры для возвращения пользователю дополнительных данных. Например, поставщик служб может вернуть сведения не только о просматривающем гаджет, но и о его друзьях.

API данных Google: альтернативный подход

В примере выше для получения данных используется метод gadgets.* makeRequest(). Если вы используете API данных Google, можно воспользоваться библиотекой JavaScript данных Google, которая работает с прокси OAuth. Она вызывает makeRequest() в фоновом режиме, поэтому конечный результат в гаджете тот же.

Подробности и пример можно найти в статье Создание гаджетов данных Google.

Пропуск всплывающего окна

Если вы – поставщик службы OAuth, вы можете улучшить для пользователей работу с гаджетом, исключив стадию всплывающего окна. При добавлении пользователями гаджета с вашего сайта, можно передать iGoogle заранее одобренный идентификатор запроса OAuth, который нужно использовать для получения доступа к данным пользователя.

Результат получается такой.

  1. Пользователь заходит на сайт поставщика служб.
  2. Сайт поставщика служб содержит ссылку Добавить на Google. Поставщикам служб следует использовать для этой ссылки обычный логотип "Добавить на Google".
  3. Цель ссылки – специальный URL на сайте поставщика услуг, который может выдавать заранее одобренные идентификаторы запросов OAuth. Из соображений безопасности этот URL должен быть защищен от атак CSRF (подделка межсайтовых запросов).
  4. URL предварительного одобрения поставщика служб аутентифицирует пользователя и создает предварительно одобренный идентифиикатор запроса OAuth, дающий доступ к данным этого пользователя. Затем URL предварительного одобрения отправляет пользователю перенаправление на URL addmodule iGoogle. URL перенаправления включает идентификатор запроса и (дополнительно) секрет идентификатора запроса.
  5. iGoogle просит пользователя подтвержить, что он хочет добавить гаджет.
  6. После подтверждения предварительно одобренный идентификатор запроса обменивается на идентификатор доступа OAuth и сразу после этого данные пользователя становятся доступны.

Реализация URL предварительного одобрения

URL предварительного одобрения OAuth должен создать предварительно одобренный идентификатор запроса OAuth и затем перенаправить пользовтаеля на URL addmodule для использования этого идентификатора. То, как создавать предварительно одобренный идентификатор OAuth, зависит от поставщика службы OAuth, но в целом такие идентификаторы должны обладать следующими свойствами.

  • Они должны определять пользователя, к чьим данным осуществляется доступ.
  • Они должны быть короткоживущими и действовать не больше нескольких минут.
  • Они должны быть одноразовыми, чтобы после использования никто не мог украсть идентификатор и получить доступ к данным пользователя.

После создания предварительно одобренного идентификатора запроса сайтом поставщика служб, сайт должен отправить пользователю перенаправление на URL iGoogle addmodule, включающий в запрос предварительно одобренный идентификатор запроса (и дополнительно – секрет идентификатора). Перенаправление выглядит примерно так.

http://www.google.com/ig/add?moduleurl=<URL OF GADGET>&up_request_token=<REQUEST TOKEN>&up_request_token_secret=<REQUEST TOKEN SECRET>

Как и в любом URL перенаправления, параметры запроса должны быть переведены в URL.

Параметры, имеющие префикс up_, считаются пользовательскими настройками для гаджета. Значение параметра up_rt станет значением rt пользовательских настроек гаджета. Заметьте, что iGoogle допускает для этих настроек пользователя только буквенно-числовые символы, символ точки (.) и пробелы. Если идентификаторы запроса или секреты идентификаторов запроса содержат другие символы, может понадобится закодировать их, прежде чем iGoogle разрешить автоматически добавлять их к гаджетам.

Добавление настроек пользователя

Для использования предварительно одобренных идентификаторов запроса гаджет нужно немного изменить. Во-первых, специцификация гаджета должна требовать наличия функции setprefs и объявлять две новых настройки пользователя для идентификатора запроса и секрета идентификатора запроса:

...
<ModulePrefs title="Preapproved OAuth Gadget" scrolling="true">
  <Require feature="setprefs" />
    <OAuth>
      <Service>
        ...
      </Service>
    </OAuth>
 </ModulePrefs>
 <UserPref name="request_token" required="false" datatype="hidden"></UserPref>
 <UserPref name="request_token_secret" required="false" datatype="hidden"></UserPref>
 ...

После добавления в спецификацию гаджета этих параметров сценарий гаджета должен передать новые настройки пользователя функции gadgets.io.makeRequest(). Например:

params.AUTHORIZATION = "OAUTH";
var prefs = new gadgets.Prefs();
var requestToken = prefs.getString("request_token");
if (requestToken !== "") {
  // We have a preapproved request token
  params.OAUTH_REQUEST_TOKEN = requestToken;
  params.OAUTH_REQUEST_TOKEN_SECRET = prefs.getString("request_token_secret");
  // Preapproved request tokens are only good once
  prefs.set("request_token", "");
  prefs.set("request_token_secret", "");
}
gadgets.io.makeRequest(url, callback, params);
 

Сперва код проверяет, следует ли использовать настройку пользователя request_token:

var prefs = new gadgets.Prefs();
var requestToken = prefs.getString("request_token");
if (requestToken !== "") {
  ...
}

Если настройка пользователя request_token не пуста, гаджет пытается использовать предварительно одобренный идентификатор запроса, устанавливая значения params.OAUTH_REQUEST_TOKEN и params.OAUTH_REQUEST_TOKEN_SECRET:

if (requestToken !== "") {
  // We have a preapproved request token
  params.OAUTH_REQUEST_TOKEN = requestToken;
  params.OAUTH_REQUEST_TOKEN_SECRET = prefs.getString("request_token_secret");
  // Preapproved request tokens are only good once
  prefs.set("request_token", "");
  prefs.set("request_token_secret", "");
}

Предварительно одобренные идентификаторы запроса можно использовать только один раз. Для того, чтобы гаджет не пытался использовать его еще раз, настройки должны быть очищены вызовами к prefs.set() после копирования в параметры makeRequest():

prefs.set("request_token", "");
prefs.set("request_token_secret", "");

Последний этап – вызов gadgets.io.makeRequest() с обновленными параметрами. Если предварительно одобренный идентификатор запроса действителен, гаджет получает доступ к данным пользователя и может не показывать всплывающее окно, чтобы получить одобрение пользователя.

Даже если вы рассчитываете, что гаджет будет использовать для доступа к данным эти предварительно одобренные идентификаторы запроса, следует, тем не менее, предоставить пользователям возможность дать разрешение с помощью всплывающего окна. Если пользователи добавляют гаджет из каталога iGoogle, предварительно одобренного идентификатора запроса не будет.

Другие примеры

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

  • Гаджет MySpace. Этот гаджет использует в качестве поставщика служб MySpace. Он выводит список обновлений у друзей.
  • Гаджет YouTube. Этот гаджет использует API данных Google YouTube для отображения ссылок, добавленных вошедшим в систему пользователем.
  • Гаджет Blogger. Этот гаджет – пример, использованный в статье Создание гаджетов данных Google.

Дополнительные советы по OAuth

Google разрабатывает несколько улучшений для этой функции.  Если вы хотите знать об этих улучшениях и посмотреть другие дополнительные советы по использованию OAuth, зайдите на поддерживаемый Google сайт Дополнительные советы по OAuth.