Кроссдоменный postMessage или как браузеры поддерживают стандарты

в 2:49, , рубрики: javascript, oauth, postMessage, браузеры, Веб-разработка, метки: ,

Кроссдоменный postMessage или как браузеры поддерживают стандартыВо время прикручивания облачных хранилищ к скрипту для бэкапа, встала необходимость использовать OAuth 2 авторизацию, для использования с разными облачными API. В принципе с самой авторизацией никаких сложностей не возникло, но проблема возникла в немного неожиданном месте.

Учитывая аудиторию использующую софтину, было решено отказаться от поддержки древних браузеров, и всё затачивалась под современные браузеры, использующие HTML5, которые казалось бы уже вполне неплохо и одинаково поддерживают страндарты.

Но не тут-то было…

Толкование postMessage разными браузерами

Как оказалось кроссдоменное (когда страницы с разных доменов) общение между окнами/вкладками браузера с помощью postMessage возможно только в Chrome и Firefox (проверено в Chrome 30 и Firefox 25).

В IE postMessage работает только при использовании frame/iframe (включая IE 11).

Opera неожиданно удивила. В версии 12 всё работает нормально. А вот в Opera 17, которая сделана на том же движке, что и Chrome, ведет себя по другому. Если домены отличаются, то postMessage работает только в frame/iframe, и не работает в отдельных окнах/вкладках. Если домены, точнее origin (т.е. протокол + домен + порт) совпадают, то тогда работают сообщения, как между фреймами, так и между окнами/вкладками.

Казалось бы, если все браузеры поддерживают кроссдоменное общение между фреймами, так почему бы сразу в приложении не открыть окно для авторизации в iframe? Но тут подножку ставит заботливый CORS – практически на всех OAuth серверах запрещено открытие страницы авторизации в фрейме.

Прокси-фрейм

Не смотря на страшное название, это обычный iframe загруженный с сайта приложения, и его задача передавать полученные токены в окно приложения, так как напрямую нельзя передать из-за ограничений в Opera и IE.

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

Для удобства используем следующие определения:

  1. Приложение (web-приложение расположено на домене пользователя, о котором заранее неизвестно)
  2. Прокси-фрейм (промежуточный фрейм, расположенный на сервере приложения, встроенный с помощью iframe в приложение)
  3. Сайт приложения (сайт на который переадресуется пользователь после OAuth авторизации)
  4. OAuth-сервер (сервер на который нужно получить доступ)

Итого у нас используется 3 домена: domain1 – где находится приложение пользователя, domain2 — прокси-фрейм и сайт приложения, domain3 — OAuth-сервер.

Процедура получения токена выглядит так:

  1. На странице приложения встраивается прокси-фрейм.
    Кроссдоменный postMessage или как браузеры поддерживают стандарты
  2. После нажатия кнопки расположенной в прокси-фрейме, открывается страница авторизации OAuth-сервера в новом окне/вкладке.
    Кроссдоменный postMessage или как браузеры поддерживают стандарты
  3. После согласия пользователя, OAuth-сервер отправляет пользователя на Сайт приложения, с кодом авторизации в параметрах запроса.
  4. Сайт приложения делает POST запрос к OAuth-серверу, и обменивает код авторизации и пароль приложения на токен для доступа и/или токен для обновления.
    Кроссдоменный postMessage или как браузеры поддерживают стандарты
  5. Полученный токен вставляется в текстовое поле (на случай если не удастся скопировать его автоматически). После чего токен отправляется в прокси-фрейм, с помощью изменения location.hash (этот работает во всех браузерах, так как у прокси-фрейма и сайта приложения одинаковый протокол, домен, порт). По сути, использование location.hash нужно только для IE (в том числе в IE 11), так как в нем postMessage не работает в разных окнах.
  6. Прокси-фрейм с помощью события onhashchange сразу после изменения хэша отправляет токен уже с помощью postMessage (так как домены теперь разные) и закрывает открытое окно/вкладку, открытое для авторизации.
  7. Приложение с помощью события onmessage получает токен и уже использует по назначению.
    Кроссдоменный postMessage или как браузеры поддерживают стандарты

Схематическое изображение

Кроссдоменный postMessage или как браузеры поддерживают стандарты

Не спец я, правда, по красивым схемам. Но надеюсь будет понятно.

Демонстрация

На этой странице вы можете посмотреть живой пример, разве что сама OAuth-авторизация имитируется, чтобы не задергали реальный сервер.

В обычном режиме передача токена происходит так быстро, что страницы с текстовым полем не видно (она быстро закрывается). Поэтому, кто хочет увидеть более подробно, вот пример, в котором окно закрывается с задержкой в 5 секунд.

Что еще почитать про postMessage

learn.javascript.ru/cross-window-messaging-with-postmessage
javascript.ru/ajax/cross-origin-2
html5demos.com/postmessage2
habrahabr.ru/post/120336/
caniuse.com/#search=postmessage

P.S. Если заинтересует могу выложить в архиве исходники скриптов из демки.

Автор: zapimir

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js