Authorization code flow with Proof Key for Code Exchange
The Authorization code flow with Proof Key for Code Exchange, or simply "Auth code flow with PKCE" is the recommended form of authenticating RingCentral users and exchanging tokens in client-side applications. It is considered a more secure version of the more widely used Authorization code flow. The flow is as follows:
}
What are the benefits of using the authorization code flow?
As you can see from the diagram above, the two protocols are nearly identical, with the following differences and benefits:
- two additional tokens are introduced to the flow to combat XSS forgeries
- the client secret is never revealed during the flow
Authorization code flow with PKCE in detail
Step 0. Generate code verifier and code challenge
Before we initiate an authorization request to RingCentral, we need to generate two strings: a code verifier and a code challenge. The code verifier in particular should be a cryptographically-random string, without the '+', '/', and '=' characters.
The code challenge is then derived from code challenge string generated above. For devices that can perform a SHA256 hash, the code challenge is a base 64, URL-encoded string of the SHA-256 hash of the code verifier. Clients that do not have the ability to perform a SHA-256 hash are permitted to use the plain code verifier string as the challenge.
The code below shows how to generate these two strings:
Javascript developers can install the crypto
modile like so:
$ npm install crypto
Then your code will look like this:
var rcsdk = new RC({
'server': process.env.RC_SERVER_URL,
'clientId': process.env.RC_APP_CLIENT_ID,
'clientSecret': process.env.RC_APP_CLIENT_SECRET,
'redirectUri': process.env.RC_REDIRECT_URI
});
var platform = rcsdk.platform();
console.log( "Login URL: ", platform.loginUrl({
"state": "1234567890",
"usePKCE": true
}) )
Python developers can install the pkce
module like so:
$ pip install pkce
Then in their code it is really simple:
import pkce
code_verifier = pkce.generate_code_verifier(length=128)
code_challenge = pkce.get_code_challenge(code_verifier)
<?php
function base64url_encode($plainText)
{
$base64 = base64_encode($plainText);
$base64 = trim($base64, "=");
$base64url = strtr($base64, '+/', '-_');
return ($base64url);
}
$random = bin2hex(openssl_random_pseudo_bytes(32));
$verifier = base64url_encode(pack('H*', $random));
$challenge = base64url_encode(pack('H*', hash('sha256', $verifier)));
?>
Ruby developers can install the pkce_challenge
Gem like so:
$ gem install pkce_challenge
Then in their code:
PkceChallenge.challenge(char_length: 128)
pkce_challenge.code_verifier
pkce_challenge.code_challenge
Example code verifier and challenge strings
When all is said and done, you will have generated two strings that look similar to the following: A cryptographically-random example string:
# Verifier
pIUgx4tiqFpaOUz0HMc_QbIyQlL901w8mRmkrmhEJ_E
# Challenge
_drLS7o5FwkfUiBhlq2hwJnK_SC6yE7sKOde5O1fdzk
Step 1. Compose a "request authorization" URL
When your application needs to access a user's data, redirect the user to the RingCentral API server. The authorization URL is same as URL from Authorization Code Flow Step 1, and PKCE flow will need additional parameters code_challenge
and code_challenge_method
in authorization URL:
Parameter | Type | Description |
---|---|---|
response_type |
string | Required. Must be set to code . |
client_id |
string | Required. The client ID of the application making the request. |
redirect_uri |
URL | Required. The URL the response will be sent to. is is a callback URI which determines where the response will be sent to. The value of this parameter must exactly match one of the URIs you provided when creating your app. |
state |
string | Optional. A pass-through value used by the client to maintain state between the request and callback. RingCentral ignore the values of this parameter. |
scope |
string | Ignored. Common to other OAuth apps, the scope parameter is ignored by RingCentral deferring to the permissions set by the developer when the app was created. It can be safely omitted. |
brandId |
integer | Optional. A number identifying what branding should be displayed on the login page. |
display |
string | This is for RingCentral-use only and can be safely omitted. |
prompt |
string | This is for RingCentral-use only and can be safely omitted. |
code_challenge |
string | Required. Generated from code challenge. |
code_challenge_method |
string | Required. The code challenge method, either plain or S256, depending on whether the challenge is the plain verifier string or the SHA256 hash of the string. If this parameter is omitted, the server assumes plain. |
Example Login URL
Below is an example login URL to initiate the PKCE authorization flow. We recommend developers use an SDK to generate this URL in a more automated fashion.
https://platform.ringcentral.com/restapi/oauth/authorize?response_type=code
&redirect_uri=<my_uri>&client_id=<client_id>&display=&prompt=
&code_challenge=<code_challenge_string>&code_challenge_method=S256
Using an SDK to generate a login URL
We recommend developers use an SDK to generate a login URL to ensure it is composed properly.
const RC = require('@ringcentral/sdk').SDK
var rcsdk = new RC({
'server': process.env.RC_SERVER_URL,
'clientId': process.env.RC_APP_CLIENT_ID,
'clientSecret': process.env.RC_APP_CLIENT_SECRET,
'redirectUri': process.env.RC_REDIRECT_URI
});
var platform = rcsdk.platform();
console.log( "Login URL: ", platform.loginUrl({
"state": "1234567890",
"usePKCE": true
}) )
Step 2. User login and consent
This step is same as its counterpart in the authorization code flow. After a user logs in and authorizes the application, RingCentral will redirect the user's browser to the redirect_uri
provided in the login URL created above. At the same time, RingCentral will append the following query parameters to the redirect URI, which your application will need in subsequent steps.
Parameter | Type | Description |
---|---|---|
code |
string | The authorization code returned for your application. |
expires_in |
integer | The remaining lifetime of the authorization code. |
state |
string | The verbatim value of the state parameter received via the login URL. |
Example OAuth redirect
HTTP/1.1 302 Found
Location: https://myapp.example.com/oauth2Callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz&expires_in=60
Step 3. Exchange auth code for access token
The 'code' your application receives at your Redirect URI is a temporary authorization code that is used to obtain an access token to call the API. If the token is not redeemed in the alotted time, the user will need to go through the login and authorization process again. This is the final step in the process before your app can call the RingCentral API.
To exchange an auth code for an access token, developers will call the RingCentral API similarly to how it is done in the authorization code flow, but with the following key differences:
- Clients do not need to transmit client authentication credentials in an
Authorization
header - Clients need to transmit an additional
code_verifier
parameter in the request body
Auth token request
HTTP Headers
Header | Value |
---|---|
Content-type |
application/x-www-form-urlencoded |
POST Parameters
Parameter | Type | Description |
---|---|---|
grant_type |
string | Required. Must be set to authorization_code for authorization code flow |
code |
string | Required. Provide your authorization code received in the previous step |
client_id |
string | Required. Enter your application client id here |
redirect_uri |
URI | Required. This is a callback URI which determines where the response is sent. The value of this parameter must exactly match one of the URIs you have provided for your app upon registration. |
access_token_ttl |
integer | Optional. Access token lifetime in seconds; the possible values are from 600 sec (10 min) to 3600 sec (1 hour). The default value is 3600 sec. If the value specified exceeds the default one, the default value is set. If the value specified is less than 600 seconds, the minimum value (600 sec) is set |
refresh_token_ttl |
integer | Optional. Refresh token lifetime in seconds. The default value depends on the client application, but as usual it equals to 7 days. If the value specified exceeds the default one, the default value is applied |
| code_verifier
| string | Required. Code verifier generated in Step 0. |
Sample Request
POST /restapi/oauth/token HTTP/1.1
Accept: application/json
Content-Type: application/x-www-form-urlencoded
code=U0pDMTFQMDFQQVMwM
XxBQUJfTVpHWk5lM29zNVFmWnNHQ01MSmJuMHJmNGlRcnRaeEptTWlPS0MzUTdYRDdSTURiaHBuWHZINGM2WTdqaWlBOE
VhRHNxRWdJVUNYQjd4dmJsWHJoVVlWQVN2SFo2YWJPanJsRkFWZk9SMm5lek0tWnF5d3h8C3AnYOPxO0flEwO6Ffoq9Tl
qs1s&grant_type=authorization_code&client_id=asdsadsadasdadsa&code_verifier=pIUgx4tiqFpaOUz0H
Mc_QbIyQlL901w8mRmkrmhEJ_E&redirect_uri=https%3A%2F%2Fmyapp.acme.com%2Foauth2redirect
Auth token response
The server responds with an access token which can presented in subsequent requests in the HTTP Authorization header to authenticate API Calls. The response will contain the following parameters:
Parameter | Type | Description |
---|---|---|
access_token |
string | Access token to pass to subsequent API requests |
expires_in |
integer | Issued access token TTL (time to live), in seconds |
refresh_token |
string | Refresh token to get a new access token, when the issued one expires |
refresh_token_expires_in |
integer | Issued refresh token TTL (time to live), in seconds |
scope |
string | List of permissions allowed with this access token, white-space separated |
token_type |
string | Type of token. The only possible value supported is 'Bearer'. This value should be used when specifying access token in Authorization header of subsequent API requests |
owner_id |
string | Extension identifier |
Sample Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token" : "U1BCMDFUMDRKV1MwMXxzLFSvXdw5PHMsVLEn_MrtcyxUsw",
"token_type" : "bearer",
"expires_in" : 7199,
"refresh_token" : "U1BCMDFUMDRKV1MwMXxzLFL4ec6A0XMsUv9wLriecyxS_w",
"refresh_token_expires_in" : 604799,
"scope" : "AccountInfo CallLog ExtensionInfo Messages SMS",
"owner_id" : "256440016"
}