Last update | June 2020 |
Operating System | Any |
Interpreter | Python 3.x, Postman |
Refinitiv Data Platform entitlement check is based on OAuth 2.0 specification. As described in the Introduction, the first step of an application work flow is to get a token, which will allow access to the protected resource, i.e. data REST API's. In the next tutorial, we will try to get access token using:
OAuth system defines various mechanisms to get an access token. These mechanisms are called grant type, and can be Authorization Code, Implicit Grant, Resource Owner Password Credentials (ROPC) a.k.a Password, Client Credentials, Device Code or Refresh Token. The highlighted grant types are supported by the Refinitiv Data Platform (RDP). An application can get an access token, initially through Password Grant and later on by using a Refresh Token. The Authorization Code and Implicit Grant types are used for federated authentication in a browser based application. A successful request for token will always return two tokens -
A successful authorization call generates new Access and Refresh tokens.
Initially when an application does not have any token, it must use a username/password combination to get the first set of tokens. This type of request is called Resource Owner Password Credentials or Password Grant in short. Once an application has a set of tokens, it should store the Refresh token in a persistent storage and use the Access token to get data. Upon expiry of Access token, the Refresh token must then be used to get new Access and Refresh tokens. This use of Refresh token to get subsequent tokens, avoids having to send password across the network repeatedly.
We will briefly discuss the HTTP requests/response for authorization. For Refinitiv Data Platform, the token endpoint is https://api.refinitiv.com/auth/oauth2/VERSION/token, where the <VERSION> number will change, as the API evolves.
There are few additional parameters which are required by token API, for compliance with OAuth 2.0 specification:
Parameter | Description |
---|---|
Grant Type | Are you requesting new tokens using username/password combination (password), or through an existing refresh token (refresh_token). |
Username | The username, used for "Password Grant". This is typically an email address, or a Machine ID which has been assigned to you. |
Password | Password associated with the username above. This field may contain special characters or reserved characters which may have to be escaped. The applications must be able to handle special characters. |
Client ID | Aka Application ID, aka AppKey is a unique identifier, defined for the application or the user making a request. A user can generate/manage their Application ID's using the AppkeyGenerator. The client ID parameter can be passed in the request body or as "Authorization" request header which is encoded as base64. This Authorization header method is used in the interactions below. |
Client Secret | Part of OAuth specification , this parameter is not used in RDP. |
Token Scope | A user can optionally limit the scope of generated token, so that Access token is valid only for a specific data set. See Token Scopes for explanation. |
For additional information on request parameters, please see the API documentation.
A successful authentication response from server contains following parameters:
Parameter | Description |
---|---|
access_token | The token used to invoke REST data API calls as described above |
refresh_token | Refresh token to use used for getting next access token |
expires_in | Access token validity time in seconds |
scope | A list of all the scopes this token can be used with |
token_type | "Bearer" - i.e, RDP token service has generated and sent us the Access token in the response message |
Below we describe, how the interaction would take place in HTTPS methods.
This is a Request using Password Grant, where user is logging in with their credentials. Note that for a very first time login, when a password needs to be changed, newPassword should be included in the request as well.
POST https://api.refinitiv.com/auth/oauth2/v1/token HTTP/1.1
Host: api.refinitiv.com
User-Agent: python-requests/2.12.4
Accept-Encoding: gzip, deflate
Accept: application/json
Connection: keep-alive
Content-Length: 112
Content-Type: application/x-www-form-urlencoded
Authorization: Basic RDZENz*************DNkNEREQ3NDo=
username=****&password=****&grant_type=password&scope=trapi
Also note that the client ID has been sent in the HTTP header as Basic Authentication Authorization: Basic RDZENz*************DNkNEREQ3NDo=. This could have been included in the request body as well. If our credentials were valid, the server will respond with a message:
HTTP/1.1 200 OK
Date: Thu, 05 Apr 2018 18:57:06 GMT
Content-Type: application/json
Content-Length: 1604
Connection: keep-alive
X-Tr-Requestid: dd6e2131-917d-4cd8-856e-3754ff6a6990
{
"access_token":"****",
"refresh_token":"****",
"expires_in":"300" ,
"scope":"trapi.alerts.news.crud .... trapi.streaming.pricing.read",
"token_type":"Bearer"
}
The response body is a JSON message (pretty printed here for explanation), which contains the Access and Refresh tokens as described above. At this point the Refresh token should be stored for future use.
The Access token which we just received can now be used in the REST API data calls. Typically this token is sent as a Bearer in the HTTP Authorization header in the request message. If the user tries to invoke an API call using an expired token, server will reject the call. Here is an example of a data request and a rejected response:
GET https://api.refinitiv.com/data/historical-pricing/v1/views/interday-summaries/TRI.N HTTP/1.1
Host: api.refinitiv.com
User-Agent: python-requests/2.12.4
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Authorization: Bearer ****
Response:
HTTP/1.1 401 Unauthorized
Date: Wed, 18 Dec 2019 14:45:27 GMT
Content-Type: application/json
Content-Length: 119
Connection: keep-alive
Access-Control-Allow-Origin: *
Www-Authenticate: Bearer realm="GET /data/historical-pricing/v1/views/interday-summaries/{universe}", scope="trapi.data.historical-pricing.summaries.read", error="invalid_token", error_description="token expired"
X-Tr-Requestid: 33603908-52b2-4e50-ba30-5336d116f50a
{
"error": {
"id":"33603908-52b2-4e50-ba30-5336d116f50a",
"code":"401",
"message":"token expired",
"status":"Unauthorized"
}
}
The server rejected our data API call with the message "token expired". Typically, an application would keep track of when an Access token is about to expire, and renew it before invoking a data call.
Since the previous token is no longer valid, the user/application has to get a new Access token, using the Refresh Grant:
POST https://api.refinitiv.com/auth/oauth2/v1/token
Host: api.refinitiv.com
User-Agent: python-requests/2.12.4
Accept-Encoding: gzip, deflate
Accept: application/json
Connection: keep-alive
Content-Length: 106
Content-Type: application/x-www-form-urlencoded
Authorization: Basic RDZENz*************DNkNEREQ3NDo=
refresh_token=****&grant_type=refresh_token
Response:
HTTP/1.1 200 OK
Date: Thu, 05 Apr 2018 19:24:02 GMT
Content-Type: application/json
Content-Length: 1604
Connection: keep-alive
X-Tr-Requestid: 4cbb7aaa-9492-499c-82f0-efd0f07bbf14
{
"access_token":"****",
"refresh_token":"****",
"expires_in":"300" ,
"scope":"trapi.alerts.news.crud .... trapi.streaming.pricing.read",
"token_type":"Bearer"
}
Following optional parameters can also be used in the request for a new token using Password Grant:
Parameter | Description |
---|---|
takeExclusiveSignOnControl | If set to true, force sign-out of other applications using the same credentials (invalidates refresh token of other applications) |
newPassword | Change the password associated with this username. Both current and new passwords will be required in order to authenticate and change password. |
The parameter, takeExclusiveSignOnControl, may be set to true ONLY if application sending authorization request needs all other sessions/applications to be logged out. Here are a couple of use cases when takeExclusiveSignOnControl must be set to true:
In this scenario, a user may opt to force logout, all other signed-in instances, and request a new set of tokens. A request with an additional flag takeExclusiveSignOnControl will get fulfilled in that case:
POST https://api.refinitiv.com/auth/oauth2/v1/token HTTP/1.1
Host: api.refinitiv.com
User-Agent: python-requests/2.12.4
Accept-Encoding: gzip, deflate
Accept: application/json
Connection: keep-alive
Content-Length: 112
Content-Type: application/x-www-form-urlencoded
Authorization: Basic RDZENz*************DNkNEREQ3NDo=
username=****&password=****&grant_type=password&scope=trapi&takeExclusiveSignOnControl=true
Similarly, if the user intends to change their password, the request message body will contain a string like: username=****&password=oldPassword&grant_type=password&scope=trapi&newPassword=****
Every resource (data REST API) within RDP has a permission set assigned to it. This permission set is called scope. The Access token must include the scope of the resource it is trying to access, otherwise the request will be rejected by platform.
For e.g. a timeseries REST API has a scope of trapi.data.historical-pricing.read. The Access token must include trapi.data.historical-pricing.read in addition to other scopes, to get timeseries data. This scope limiting is done when requesting the original token using Password Grant. The request parameter: Scope can contain either a root scope or a subset. Requesting the top level scope trapi will get us a token, which contains all the scopes we are entitled to. Similarly, requesting for trapi.cfs.publisher will get a token, which is entitled to Read: trapi.cfs.publisher.read and Write: trapi.cfs.publisher.write scopes.
Most applications will request a token with root scope: trapi. Following are the list of scopes defined within the Refinitiv Data Platform, and may optionally be used when requesting token: