Token-Based Authorization
Introduction
Purpose
This document is an addition to dxFeed Java/C/C#/JavaScript/Python APIs and describes a connection using token-based authorization.
Scope
To provide your customers with dxFeed market data, you can connect to our market data feeds with token-based authentication. It ensures that each request to a server is accompanied by a signed token, which the server verifies for authenticity and only then responds to the request. Thus, your clients get access to allowed market data feeds for a specified period. We do not collect your clients' personal details.
Note
We recommend generating a new token every 24 hours.
Token
Token format
Self-signed token format: token := encoded-payload "."
signature
encoded-payload := BASE64(UTF8(payload)) signature := BASE64(HMAC-SHA256(encoded-payload, UTF8(secret))) payload := issuer "," subject "," not-before-time "," expiration-time "," issued-at-time "," message,
where:
secret
- the key used for signature validationissuer
- principal that issued this tokensubject
- subject of this tokennot-before-time
- time when token will be validexpiration-time
- expiration time of the token, after which the token will not be validissued-at-time
- when this token was issuedmessage
- any string (or<user ID>, <filter_1>;…;<filter_n>
if specified by dxFeed team)
All time variables are specified in seconds from the epoch, in UTC.
For additional information, please check:
Java implementation
Set classpath to the folder with auther-api.jar. You get access to the library:
com.devexperts.mdd.auth.entitle.EntitleLoginHandlerFactory - login handler factory that plugs into dxFeed API via manifest (by implementing com.devexperts.qd.qtp.auth.QDLoginHandlerFactory service interface - see META-INF/ folder).
com.devexperts.mdd.auth.entitle.SampleClient - sample client code to test the connection. In order to use login handler factory, connection string must specify login=entitle.
com.devexperts.mdd.auth.util.SignedToken - it is used to create tokens signed by a shared secret.
To generate the token, please use the following code:
return SignedToken.newBuilder() .setIssuer(issuer) .setSubject(sessionType) .setMessage(user) .setIssuedNow() .setExpirationFromNow(Duration.ofDays(1)) .toToken() .signToken(secret);
where
issuer
- principal that issued this tokensessionType
- session provided by dxFeed teamuser
- user ID or<user ID>,<filter_1>;…;<filter_n>
if feeds are specified for your connection by dxFeed team (e.g., 'testuser,opra;cme')secret
- the key used for signature validation
Error handling
You can handle errors from the server. For this, please change EntitleLoginHandlerFactory.AutherLoginHandler#login(String)
to react to errors from dxFeed by analyzing the reason parameter. To find session duplicate events, search for Duplicate session text in the error description.
C# implementation
For C#, please generate a token with this code:
using System; using System.Security.Cryptography; using System.Text; namespace AutherTokenGen { internal class Program { private const string DefaultIssuer = "<name>"; private const string DefaultSessionType = "<session>"; private const string DefaultMessage = "<message>"; private const string DefaultSecret = "<secret>"; private const int DefaultDays = 1; private static readonly UTF8Encoding Encoding = new UTF8Encoding(); private static byte[] GetUtf8Bytes(string s) { return new UTF8Encoding().GetBytes(s); } private static string ToBase64(string s) { return ToBase64(Encoding.GetBytes(s)); } private static string ToBase64(byte[] bytes) { return Convert .ToBase64String(bytes) .TrimEnd('=') .Replace('+', '-') .Replace('/', '_'); } public static void Main(string[] args) { const string help = "AutherTokenGen <issuer> <sessionType> <user|message> <secret> <days>"; var issuer = DefaultIssuer; var sessionType = DefaultSessionType; var message = DefaultMessage; var secret = DefaultSecret; var days = DefaultDays; switch (args.Length) { case 4: case 5: { issuer = args[0]; sessionType = args[1]; message = args[2]; secret = args[3]; if (args.Length == 5) { if (!int.TryParse(args[4], out days)) { days = DefaultDays; } } break; } case 0: Console.WriteLine($"Usage: \n{help}\n"); break; default: Console.WriteLine($"Usage: \n{help}\n"); return; } Console.WriteLine( $"Issuer = {issuer}, SessionType = {sessionType}, User|Message = {message}, Secret = {secret}, Days = {days}\n"); var now = DateTime.UtcNow; var expiration = now.AddDays(days); var nowSeconds = new DateTimeOffset(now).ToUnixTimeSeconds(); var expirationSeconds = new DateTimeOffset(expiration).ToUnixTimeSeconds(); var payload = $"{issuer},{sessionType},,{expirationSeconds},{nowSeconds},{message}"; Console.WriteLine($"Payload: {payload}\n"); var encodedPayload = ToBase64(payload); var hmac = new HMACSHA256(GetUtf8Bytes(secret)); var signature = ToBase64(hmac.ComputeHash(GetUtf8Bytes(encodedPayload))); var token = $"{encodedPayload}.{signature}"; Console.WriteLine($"Token: {token}\n"); } } }
JavaScript implementation
For JavaScript, please generate a token with this code:
/** Dependencies */ import CryptoJS from 'crypto-js'; /** * @description Generate dxFeed API Self-Signed Token */ export const createAuthToken = (secret, issuer, session, message, days) => { try { const notBeforeTime = ''; const expirationDate = new Date(); expirationDate.setDate(expirationDate.getDate() + days); const expirationTime = expirationDate.getTime(); const issuedAtTime = Date.now(); const payload = [issuer, session, notBeforeTime, expirationTime, issuedAtTime, message].join(','); const encodedPayload = Buffer.from(encodeURI(payload)).toString('base64').split('=')[0]; const hash = CryptoJS.HmacSHA256(encodedPayload, encodeURI(secret)); const signature = CryptoJS.enc.Base64.stringify(hash) .split('=')[0] .replace(/\+/g, '-') .replace(/\//g, '_'); return `${encodedPayload}.${signature}`; } catch (err) { return Promise.reject(err) } }
Python implementation
For python, please generate a token with this code:
# imports for generate_token function from time import time from base64 import b64encode from urllib.parse import quote import hmac from hashlib import sha256 def generate_token(issuer: str, session: str, secret: str, message: str, days: int) -> str: """ Generate dxFeed API Self-Signed Token Parameters ---------- issuer: str Principal that issued this token. session: str Session provided by dxFeed team. secret: str The key used for signature validation. message: str Any string (or '<user ID>, <filter_1>;…;<filter_n>' if specified by dxFeed team). days: str Number of days the token is valid for. Returns ------- token: str Ready-to-use token """ # get the required timestamps not_before_time = '' expiration_time = int(time() + (int(days) * 86400)) # number of sec in day issued_at_time = int(time()) # generate the payload string payload = quote( f'{issuer},{session},{not_before_time},{expiration_time},{issued_at_time},{message}', safe="~@#$&()*!+=:;,.?/\'") # encode and hash the various parts to create the final token. encoded_payload = b64encode(payload.encode()).decode().split('=')[0] signature_hash = hmac.new( bytes(quote(secret, safe="~@#$&()*!=:;,.?/'"), 'latin-1'), msg=bytes(encoded_payload, 'latin-1'), digestmod=sha256).digest() # format the signature hash signature = b64encode(signature_hash).decode().split('=')[0]\ .replace('+', '-').replace('/', '_') # join the encoded payload and signature to complete the token token = '.'.join([encoded_payload, signature]) return token
Token generation sample
To test a token generation, you can use auther.jar in command line mode:
java -cp auther.jar com.devexperts.mdd.auth.entitle.SignedTokenGenerator <name> <session> test uithoophaivahG3aa2uS2eu9eich6aef2JaeTh2rus7Vaec7SeeNgunaexaefini
Output:
ZnhzdHJlZXQscmVhbHRpbWUsLDE1NTkyMzA5MzMsMTU1OTE0NDUzMyx0ZXN0.DIkBUkhgiNa0Bsmbgo0vGhp78KIjPGT80PlG3W7f3IY
To test a token generation with multiple feeds specified:
Issuer=terminal SessionType=terminal-pro or terminal-nonpro Message=testuser,termCMEGROUP;termCME;termCBOT;termNYMEX;termCOMEX;termEUREX;termCTAUTP;termBISTfut (you can specify any amount of your feeds here) Secret=secret for current session type provided
Establishing connection
Specify the generated token when establishing a connection. Use the following functions:
For Java API
String address = "127.0.0.1:7501[login=entitle:" + token + "]";
Or (preferably) set a global variable and use a shorter connection string
AutherLoginHandlerFactory.setAppToken(token); address = "127.0.0.1:7501[login=entitle]";
For C API
dxf_create_connection_auth_bearer() function
For C# API
NativeConnection(string address, string token, Action<IDxConnection> disconnectListener)
constructor of com.dxfeed.native.NativeConnection.NativeConnection class
For JavaScript API
dx.feed.setAuthToken(token);
For Python API
dxfeed.core.DXFeedPy.dxf_create_connection_auth_bearer()
The function creates connection to dxeed given URL address and token
Parameters: |
|
---|---|
Returns: | cc – Cython ConnectionClass with information about connection |
Return type: |
For more information please read the documentation.
For web service
For WebSocket (on ws-handshake):
{ ext: { "com.devexperts.auth.AuthToken": <token> }}
For REST:
Specify token in the HTTP-header:
Authorization: Bearer <token>
(Deprecated workaround) If a header cannot be set, use URL-parameter:
<REST-method>.json?access_token=<token>
Testing
To test token generation and data request, please use this command (for Java):
java -Dentitle=<issuer>,<session-name>,<user-id> -DentitleSecret=<session-secret> -jar auther-api.jar <host:port>[login=entitle] Quote IBM,GOOG,AAPL
where:
issuer
- principal that issued this tokensession-name
- session provided by dxFeed teamuser-id
- end-user identificationsecret
- the key used for signature validation-jar auther-api.jar
- path to auther-api (auther-api/lib/auther-api.jar)<host:port>[login=entitle]
- endpoint provided by dxFeed support team
Example
java -Dentitle=acme,demo,1234 -DentitleSecret=0123456789 -jar auther-api.jar localhost:7501[login=entitle] Quote IBM,GOOG,AAPL