API-интерфейсы RingCentral используют OAuth 2.0 для авторизации. Но какой поток грантов является наилучшей практикой для клиентских приложений, таких как настольные, мобильные и веб-приложения (одностраничные приложения)? Ответ на это - код авторизации с помощью Proof Key для обмена кодами. В этой статье я представлю и покажу вам, как реализовать код авторизации с потоком PKCE в одностраничных приложениях.

Полезные ссылки

  1. Справочник по RingCentral API: Авторизация в RingCentral API
  2. Ссылка IETF: Пробный ключ для обмена кодом общедоступными клиентами OAuth

Код авторизации и неявный поток предоставления

Поток предоставления кода авторизации:

Мы можем увидеть полные этапы процесса предоставления кода авторизации на следующей диаграмме. Стороннему приложению потребуется идентификатор клиента RingCentral и секрет клиента для обмена и обновления токена доступа. Стороннее приложение останется авторизованным, если оно обновит токен доступа RingCentral до истечения срока действия токена обновления, и получит новый токен обновления и токен доступа при обновлении.

Неявный поток грантов

Мы можем увидеть полные шаги неявного потока предоставления на следующей диаграмме. Он разработан для клиентского приложения. Стороннему приложению нужен только идентификатор клиента. Сервер входа в систему вернет токен доступа браузеру напрямую. Стороннее приложение не может обновить токен доступа. Чтобы получить новый токен, пользователю необходимо снова посетить сервер входа в систему. Мы можем использовать скрытый iframe для реализации потока обновления токенов. Сервер входа в систему использует сеанс браузера для запоминания статуса входа пользователя. Сеанс браузера истечет через полчаса. Поэтому, если стороннее приложение не обновится в течение получаса, стороннее приложение будет неавторизованным после истечения срока действия токена доступа.

Зачем нам нужен код авторизации с PKCE

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

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

Код авторизации с потоком предоставления PKCE - это новое решение для клиентского приложения. Это повышение безопасности для потока кода авторизации. Стороннему приложению потребуется только идентификатор клиента. Но когда пользователь запускает авторизацию, он генерирует криптографически случайную строку code_verifier. И зашифруйте code_verifier в code_challenge. Приложение передаст code_challenge на веб-страницу входа в RingCentral. На этапе обмена кодом приложение будет использовать код авторизации и code_verifier для обмена токеном доступа и обновления токена. Сервер RingCentral API закодирует code_verifier и сверит его с code_challenge, переданным ранее. code_verifier будет изменяться для каждого запроса авторизации.

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

Реализация кода авторизации с потоком PKCE с помощью RingCentral JS SDK

Мы реализовали код авторизации с потоком PKCE в RingCentral JS SDK v4.

Чтобы использовать поток PKCE, просто передайте usePKCE в функцию `loginUrl`, и все будет таким же, как код авторизации или неявный поток:

var rcsdk = new RingCentral.SDK({
    server: RingCentral.SDK.server.production,
    clientId: 'yourClientId',
    redirectUri: 'yourRedirectURI'
});
// generate login url with PKCE
var loginUrl = rcsdk.loginUrl({ usePKCE: true }); 
rcsdk.loginWindow({url: loginUrl}) // will popup login window
    .then(function (loginOptions){
        return rcsdk.login(loginOptions);
    })
    .then(...)
    .catch(...);

SDK поможет сгенерировать code_verifier и code_challenge, а также передаст их на сервер RingCentral.

Вы можете получить демонстрационный код здесь.

Как мы реализуем PKCE из потока кода авторизации:

Во-первых, нам нужно сгенерировать code_verifier. code_verifier - это просто случайная строка:

import {randomBytes, createHash} from 'crypto';
function _generateCodeVerifier() {
 let codeVerifier: any = randomBytes(32);
    codeVerifier = codeVerifier
        .toString('base64')
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=/g, '');
    return codeVerifier;
}

Нам нужно убедиться, что строка не включает «+», «/» и «=».

code_challenge - это закодированное значение sha256 из code_verifier:

this._codeVerifier = '';
if (usePKCE) {
    this._codeVerifier = this._generateCodeVerifier();
    query.code_challenge = createHash('sha256')
        .update(this._codeVerifier)
        .digest()
        .toString('base64')
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=/g, '');
    query.code_challenge_method = 'S256';
}
return this.createUrl(`${this._authorizeEndpoint}?${qs.stringify(query)}`, {addServer: true});

code_verifier необходимо генерировать для каждого запроса авторизации. Мы обновляем его, когда создаем URI авторизации. И сохраните его в локальном, и сгенерируйте code_challenge из code_verifier. code_challenge и code_challenge_method необходимо добавить в запрос URI авторизации.

После успешного входа пользователя в URI авторизации мы получим код авторизации из URI перенаправления.

Затем нам нужно обменять токен доступа на «код авторизации» и code_verifier:

body.grant_type = 'authorization_code';
body.code = code;
body.redirect_uri = this._redirectUri;
if (this._codeVerifier && this._codeVerifier.length > 0) {
    body.code_verifier = this._codeVerifier;
    body.client_id = this._clientId;
    skipAuthHeader = true;
}
response = await this._tokenRequest(tokenEndpoint, body, skipAuthHeader);

Нам также необходимо передать идентификатор клиента в теле запроса и пропустить заголовок авторизации.

Поскольку мы можем обмениваться токенами только с `кодом авторизации` и` code_verifier`, поэтому он может сохранить безопасность, даже если ваш `код авторизации` утек.

Чтобы получить полный код для реализации PKCE, перейдите здесь.

Заключение

В этой статье мы рассказали, как авторизовать API-интерфейсы RingCentral с помощью потока кода авторизации с помощью PKCE, и сравнили его с кодом авторизации и неявным потоком предоставления. Если вы реализуете клиентское (настольное, мобильное и одностраничное веб-приложение) приложение с RingCentral API, рекомендуется использовать поток PKCE, который может сделать ваше приложение более стабильным и безопасным.

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

Хотите быть в курсе и узнавать о новых API и функциях? Присоединяйтесь к нашей программе Game Changer и получайте отличные награды за развитие своих навыков и изучение RingCentral больше!