Настройка OAuth 2 авторизации¶
Протокол авторизации OAuth 2 обеспечивает передачу права доступа к данным пользователя GBooking стороннему приложению. Используя данный протокол вы можете разработать веб или мобильное приложение с использованием системы GBooking как серверной платформы или предоставлять вашим клиентам их данные, хранящиеся на платформе GBooking.
В данной статье описана настройка авторизации в мобильном приложении и веб приложении используя авторизационный сервер Gbooking.
{% hint style="info" %}
Для онлайн записи не требуется авторизация.
Если вам нужна только онлайн запись без получения данных пользователя вам нет необходимости настраивать авторизацию.
{% endhint %}
Вы можете посмотреть тестовый пример взаимодействия по OAuth 2 для веб приложения – исходный код.
Определения¶
CLIENT_ID¶
Уникальный идентификатор клиентского приложения, использующего GBooking API
REDIRECT_URI¶
Адрес url, на который происходит редирект в случае успешной авторизации на веб приложении
TOKEN¶
Уникальная строка, сгенерированная в процессе обмена (exchange
) с временем жизни, равным 15 минутам
USERID¶
Уникальный идентификатор пользователя на сервере авторизации
CRED¶
Пара значений user
и token
– сессия пользователя, передающаяся в каждом запросе в параметре cred
{
"token": "TOKEN",
"user": "USER"
}
REFRESH_TOKEN¶
Уникальная строка, сгенерированная для каждого токена, использующаяся для того, чтобы обновить токен (refresh
), когда истечет его время жизни. Время жизни самого REFRESH_TOKEN равно 1 месяцу. В течение этого времени пользователь может не перелогиниваться.
Регистрация приложения и получение CLIENT_ID¶
Для регистрации клиентского приложения вам нужно обратиться к специалистам службы поддержки по e-mail [email protected]
. Вам нужно будет сообщить информацию о вашем приложении, цели, для чего вашим пользователям нужны данные. В письме вы можете указать следующую информацию:
- Название вашей компании
- Идентификатор бизнеса/нетворка, если уже зарегистрированы или напишите "не зарегистрированы в системе GBooking/MedMe"
- Название вашего приложения (оно будет вставлено в стандартный диалог входа)
- Описание вашего приложения; что оно будет делать с данными
- URL вашего приложения, на который произойдет редирект в случае успешной авторизации
- Логотип вашего приложения (он будет вставлен в стандартный диалог входа)
- Ваш телефон, скайп или whatsapp для обратной связи
Шаблон письма
Тема письма: Запрос на регистрацию OAuth 2 приложения
Получатель: [email protected]
Тело письма:
Зарегистрируйте OAuth 2 приложение в системе GBooking/MedMe.
Компания – <название вашей компании>
Идентификатор бизнеса/нетворка – не зарегистрированы в системе GBooking/MedMe
Название приложения – <название вашего приложения>
Описание работы с данными – <описание работы с данными>
OAuth 2 callback Url – https://myapp12345.com/oauth2_callback
Логотип приложения – https://myapp12345.com/images/logo.png
Спасибо!
Менеджер по работе с клиентами/разработчик/другое Иванов Петр Васильевич
Тел./Whatsapp +79110123456
Skype <your skype nickname>
В случае успешного согласования, вам зарегистрируют приложение на сервере авторизации Gbooking и вышлют CLIENT_ID
.
Протокол OAuth 2¶
На серверах Gbooking реализован стандартный механизм OAuth 2 авторизации.
Протокол предполагает 3 целевых действия
- Авторизация – вход (
login
) - Завершение сессии – выход (
logout
) - Обновление токена (
refresh
)
Механизм входа выглядит следующим образом.
Пользователь переходит на ресурс /authorize
. Если он имеет действующую сессию на сервере авторизации, то он сразу переходит на страницу редиректа, указанную в настройках приложения.
Если же он заходит в первый раз или сессия пользователя на стороне OAuth 2 сервера истекла, то пользователю предложат ввести свои логин и пароль. Когда пользователь их введет и отправит, он будет так же переправлен на страницу редиректа.
В обоих случаях при редиректе будет передан параметр запроса code
. Он нужен, чтобы затем выдать по нему параметры доступа уже на странице приложения. Иначе пришлось бы передавать их при переходе из домена авторизационного сервера на URL приложения в открытом виде.
По параметру code
и ресурсу, на который был выполнен редирект (redirect_uri
) можно получить параметры доступа к серверу api. Для этого служит запрос на обмен токенов – exchange
. Он принимает полученный ранее параметр code
и redirect_uri
. В ответ он вернет следующие параметры – user
, token
, refreshToken
. Это и есть параметры доступа к api – их нужно сохранить в локальном хранилище приложения.
Сессия OAuth 2 имеет время жизни, причем оно достаточно коротное (измеряется часами). По умолчанию оно равно 12 часам.
Для удобства пользователя, чтобы каждый раз после истечения сессии не вводить заново свои данные, используется запрос на обновление сессии (refresh
).
Завершение сессии выполняется с использованием запроса /logout
с передачей параметров user
, token
в него.
Детальную информацию смотрите на сайте https://oauth.net/2/ и на странице спецификации протокола.
Так же, рекомендую следующие статьи для понимания механизма авторизации OAuth 2 – 1 и 2.
Рассмотрим каждый шаг детальнее.
1. Перенаправление на приложение¶
Пример запроса:
GET https://oauthv2.gbooking.ru/authorize?type=web_server&client_id=5e4bd655a719ad41e8bcbf79&redirect_uri=http://gbooking.local/welcome.html&response_type=code
Параметры запроса:
Название | Описание |
---|---|
type | Должно быть передано значение web_server |
redirect_uri | Данный параметр в точности должен быть равен uri ресурсу, который был указан при регистрации приложения. |
response_type | В каком виде должен возвращаться токен. Значение code означает, что он должен вернуть параметр запроса с таким именем. |
client_id | Идентификатор OAuth 2 приложения |
Данный запрос выполняет перенаправление со стартовой страницы авторизации на страницу логина https://oauthv2.gbooking.ru/login?client_id=5e4bd655a719ad41e8bcbf79
.
В случае успешного логина, пользователь будет перенаправлен на страницу, указанную в параметре redirect_uri
. Так же, если на сервере OAuth 2 есть сессия пользователя, он будет перенаправлен на приложение минуя страницу логина. В обоих случаях при редиректе передается параметра запроса code
.
2. Exchange¶
Далее на странице приложения, куда мы только что перешли, нужно проверить параметр code
и, если он передан, отправить запрос на exchange
. Возвращенные от этого запроса данные необходимо сохранить в локальном хранилище.
Так же, после выполнения этого запроса нужно убрать параметр code
, поскольку в противном случае при повторном обращении по данному url, снова будет попытка повторить этот запрос с тем же самым параметром code
, что приведет к ошибке.
Пример кода:
// We can get the token from the "code" query
// param, available in the browsers "location" global
const query = window.location.search.substring(1)
const code = query.split('code=')[1]
if (code)
exchangeToken(code)
.then(cred => {
// Save session object to localStorage or cookies
// Remove "code" query from current URL
localStorage.cred = JSON.stringify(cred);
location.href = '/welcome.html';
});
function exchangeToken(code) {
return fetch('https://oauthv2.gbooking.ru/exchange', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
code,
client_id: CLIENT_ID,
redirect_uri: 'http://gbooking.local/welcome.html'
})
})
// Parse the response as JSON
.then(res => res.json())
}
3. Refresh¶
Запрос на обновления токена refresh
применяется, когда нужно продолжить доступ пользователя к api без повторного ввода логина/пароля. Стандартное время жизни токена равно 12 часов поэтому это необходимый шаг для OAuth 2 авторизации. Время жизни токена обмена равно 1 месяц.
Туда отправляются token
и специальный refreshToken
, который были выданы при первичном получении параметров доступа (смысл параметра refreshToken
– он не светится в других запросах, кроме запроса на обновление токена). В ответ данный запрос возвращает новые значения параметров token
и refreshToken
, которыми нужно заменить устаревшие в локальном хранилище.
Пример кода (исходный код):
function refreshToken(token, refresh) {
return fetch('https://oauthv2.gbooking.ru/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
token,
refresh
})
})
// Parse the response as JSON
.then(res => res.status !== 200 ?
res.text().then(text => ({code: res.status, error: text})) :
res.json())
// User is authorized when there is user_id property in the response
.then(res => {
if (res && res.error) {
console.error('refresh token', res.error);
return false;
}
if (!res || !res.refresh_token)
return false;
const cred = JSON.parse(localStorage.cred);
cred.token = res.access_token;
cred.refreshToken = res.refresh_token;
localStorage.cred = JSON.stringify(cred);
return true;
})
}
В случае, если обмен токенов refresh
закончился неудачно, необходимо перенаправить пользователя на страницу логина:
refreshToken(cred.token, cred.refreshToken)
.then(tokenRefreshed => {
if (!tokenRefreshed) {
// Refresh token was expired so user should fill login form again
loginForm();
}
})
Так же, когда на запрос к api возвращается код ошибки AUTH_TOKEN_DATE_EXPIRED = -18007
вы можете вызвать обмен токенов:
profile(cred.user, cred.token)
.then(res => {
if (tokenWasExpiredResponseError(res)) {
// When session expired send refresh token request.
// If refresh token was expired redirect user to login form.
return refreshToken(cred.token, cred.refreshToken);
}
});
function tokenWasExpiredResponseError(res) {
return res && res.error && res.error.code === -18007;
}
Выход¶
Данный метод применяется для удаления параметров доступа к api.
Пример кода (исходный код):
fetch('https://oauthv2.gbooking.ru/logout', {
method: 'POST',
body: JSON.stringify({
client_id: CLIENT_ID,
userid: cred.user,
token: cred.token
})
})
.then(() => {
delete localStorage.cred;
});
Сценарий авторизации¶
Общий сценарий авторизации выглядит следующим образом:
- Проверяем, есть ли переданный параметр
code
. Если есть, то выполняем фазу обменаexchange
. В случае успешного запроса, удаляем параметрcode
. - Если параметра
code
нет и нет локально сохраненных параметров доступа, то редиректим на стартовую страницу авторизации (со ссылкой на/authorize
). - Если же у нас есть локально сохраненные параметры доступа, то проверяем их актуальность через запрос на получение профиля пользователя
- Если получаем в ответ ошибку "токен истек", то вызываем запрос на обновление токена
refresh
. В случае успешного ответа обновляем токеныtoken
,refreshToken
в локальном хранилище. В случае ошибки – редирект на стартовую страницу авторизации.
В результате получилась функция авторизации, которая возвращает профиль пользователя через прамис:
function authorize() {
// We can get the token from the "code" query
// param, available in the browsers "location" global
const query = window.location.search.substring(1)
const code = query.split('code=')[1]
const waitUntilRedirect = new Promise(() => {});
// Call exchange token phase when "code" query is passed.
if (code)
return exchangeToken(code)
.then(cred => {
// Save session object to localStorage or cookies.
// Remove "code" query from current URL
// for avoid call exchange token phase on reload this page.
// It should redirect page and this function will goes by another "if" branch (see below).
localStorage.cred = JSON.stringify(cred);
location.href = '/welcome.html';
return waitUntilRedirect;
});
if (!localStorage.cred) {
// User go to login form when local session object not found
loginForm();
return waitUntilRedirect;
}
let cred = JSON.parse(localStorage.cred);
let credIsCorrupted = cred && cred.user && cred.token && cred.refreshToken;
if (!credIsCorrupted) {
// User go to login form when local session object corrupted
// throw Error('Expect valid session');
loginForm();
return waitUntilRedirect;
}
// Here checking if session not expired.
return profile(cred.user, cred.token)
.then(res => {
if (res && res.result)
return res;
if (tokenWasExpiredResponseError(res)) {
// When session expired send refresh token request.
// If refresh token was expired redirect user to login form.
return refreshToken(cred.token, cred.refreshToken)
.then(tokenRefreshed => {
if (!tokenRefreshed) {
// Refresh token was expired so user should fill login form again
loginForm();
return waitUntilRedirect;
}
return null;
});
}
return null;
});
}
Исходный код тестового приложения MedMe OAuth 2. Вы можете запустить его у себя локально на компьютере или использовать как базовый проект для начала разработки.