Skip to content

App and Account Authentication

The cTrader Open API authentication process is based on the OAuth 2.0 standard.

This framework allows third-party applications to attain limited access to a service either on behalf of the resource owner, by orchestrating an approval interaction between the resources owner and the service, or by allowing a third-party application to attain access on its own behalf.

Authentication Flow

As per the OAuth 2.0 standard, account authentication requires both an authorization code and an access token.

Defining Tokens

An authorization code is a short-term token that is issued for an individual cTID. Its expiration time is one minute. An access token is a long-term token that allows an application to send and receive messages to and from the cTrader backend. After receiving it, an authorization code must be quickly exchanged with an access token; afterward, all messages sent to the cTrader backend have to be signed with the received access token to authenticate your application. The expiration time of an access token is 2,628,000 seconds (approximately 30 days). After it is expired, a new access token can be generated using a refresh token. The refresh token does not have an expiration time.

Typically, the process of someone new starting to use your application for the first time looks as follows.

1. A user with a certain cTID accesses your application wherever it is deployed; the user interacts with an action button or equivalent.

2. The application uses an embedded browser (or any other suitable means) to redirect the user to a specific URI where they will give the app the necessary permissions.

Gaining Trust

Before redirecting the user to this URI, make sure that the user trusts your app and is aware of the fact that they will need to provide additional permissions.

3. The user is transferred to a predefined redirect URL. This URL contains the access token as the value of the code query parameter.

4. The user is transferred back to the app either of their own accord or automatically. The app is informed of the value of the code parameter.

5. The app sends a request for exchanging the authorization code with an access token to a specific endpoint.

6. The app receives a response containing the access token as the value of the "accessToken" key.

7. The app sends the ProtoOAApplicationAuthReq message containing the clientId and clientSecret fields.

8. The app sends the ProtoOAGetAccountListByAccessTokenReq message containing the accessToken field. The response (ProtoOAGetAccountListByAccessTokenRes) contains the repeated ctidTraderAccountId field.

9. After receiving a response, the app sends the ProtoOAAccountAuthReq message containing the ctidTraderAccountId and accessToken fields.

Afterward, the user should be authenticated under the account the ctidTraderAccountId of which matches the ctidTraderAccountId provided during Step 8.

The below diagram explains how the basic authentication flow works from the perspective of your Open API application.

graph TB
  A([Redirect the user to a specific URI]) ==> B([Receive the authorization code]);
  B ==> C([Send a request for attaining an access code]);
  C ==> D([Send the ProOAApplicationAuthReq message]);
  D ==> E([Send the ProtoOAAccountAuthReq message]);

In the sub-sections below, we explain the key steps of the authentication flow in detail.

Adding a Redirect URI

As shown previously, your app needs to attain an authorization code after a user is sent to a certain redirect URI. This URI will include the authorization code as the value of the code query parameter.

To add a new redirect URI, do the following.

1. Open the 'Applications' page in the Open API portal.

2. Click on the 'Edit' button in the row with the application to which you would like to add a redirect URL.

3. In the newly appeared screen, scroll down to the 'Redirect URIs' section.

4. Add a redirect URL in an available empty text input field. In case no available fields are shown, click on the 'Add Redirect URI' button.

5. Click on the 'Save' button at the bottom of the page to apply your changes.

Managing Redirect URIs

The very first redirect URI is for the playground service that allows you to test your application before giving regular users access to it. Note that it must never be used for a production application. You can add an unlimited number of redirect URIs if required.

Attaining the Authorization Code

To receive an authorization code for a specific cTID, your app will need to send a user to the following URI.

https://id.ctrader.com/my/settings/openapi/grantingaccess/?client_id={clientId}&redirect_uri={your_redirectURI}&scope={scope}&product=web

Note

At this stage, the user will need to permit your application/service to access their trading accounts. As a result, it is imperative you provide the user with full understanding of what using your application/service will entail in terms of permissions and data storage/processing.

This URI has the following query parameters.

Parameter Required? Definition
client_id Yes The unique identifier of your Open API application.
redirect_uri Yes A valid redirect URI specified in the settings of your application.
scope Yes The scope of the permissions that you would like to be given to your application. The following values are accepted.
accounts. 'View only' access is given. Your application will be able to access account information and statistics; performing trading operations will be impossible.
trading. Full access is given to account information and statistics as well as all permitted trading operations.
product No If this parameter is specified and set to web, the authorization screen lacks the header and footer, which makes it look better on mobile devices. While the parameter is optional, we recommend setting it to web.

To view your client_id, go to the 'Applications' page and click on the 'View' button in the 'Credentials' column.

After a user is sent to the URI above (and as long as both parameters are valid), they should see a login/signup screen prompting them to authorize under their cTID. Following that, a user will be shown a dialog where they will be able to permit the application to gain access to one or more accounts linked to their cTID.

When the user clicks on the 'Allow Access' button, they will be redirected to the redirect_uri you had specified originally. As shown in the below example, the authorization code will be added to this URI as a query parameter.

https://my-redirect-uri.com/?code=123ASDASffdg_as234_GCllkoq14adRB7_tvN

At this point, your application would need to access and store the value of the code parameter. It must be quickly exchanged with an access token before the authorization code expires.

Additional Accounts

If a user creates additional accounts after completing the actions described above, these accounts will not be authorized inside the application. Instead, the user would need to be redirected back to the URL provided above so that they could give the application access to any additional accounts that they have created since last login.

Authentication via Google

If the above URI is opened via WebView and the user chooses Google to sign in, Google will display an 'Access Blocked' page, preventing the user from proceeding further.

To prevent this, make sure that the user-agent signature of the device opening the page looks as follows.

'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'

Attaining an Access Token

To get an access token, perform the following REST API call.

Relative URL

The request URL is relative to https://openapi.ctrader.com.

Method URL
GET apps/token

This request has the below query parameters.

Parameter Required? Definition
grant_type Yes The type of the token send to this endpoint. The following values are accepted.
authorization_code. An authorization code is sent to the endpoint to exchange it with an access token.
refresh_token. A refresh token is sent to the endpoint to refresh an existing access token.
code No The token sent to the endpoint. This parameter is mandatory if grant_type=authorization_code.
redirect_uri No A valid redirect URI specified in the settings of your application. This parameter is mandatory if grant_type=authorization_code.
client_id Yes The unique identifier of your Open API application.
client_secret Yes The secret key assigned to your Open API application.

The request example can be found below.

curl -X GET 'https://openapi.ctrader.com/apps/token?grant_type=authorization_code&code=0ssdgds98as9_QSF56FVC_22dfdf&redirect_uri=https://spotware.com&client_id=5430012&client_secret=012sds23dlkjQsd' -H 'Accept: application/json' -H 'Content-Type: application/json'

To view your client_secret, return to the 'Applications' page and press the 'View' button in the 'Credentials' column.

Upon a successful request, you should receive a response with the following keys.

Key Value Type Definition
"accessToken" string The access token that your application will use to authenticate the messages sent to the cTrader Open API.
"tokenType" string The type of the access token ("bearer" should be the value of this key as the REST standard is used to make the request).
"expiresIn" integer The number of seconds that must pass before the access token expires (by default, the value of this key equals 2628000).
"refreshToken" string The refresh token that must be used for renewing the access token once it expires. The refresh token has no expiry time itself.
"errorCode" string/null The error code that describes why a request was unsuccessful. For a successfull request, the value of this key should be null.
"description" string/null The descroption of an error that has occured during an unsuccessful request. For a successful request, the value of this key should be null.

For an example of a successful response, see the below snippet.

{
    "accessToken":"mos8Bw3D4EG0fRPd4Eqq0JxaFT4zjd8e4YijNezh_ag",
    "tokenType":"bearer",
    "expiresIn":2628000,
    "refreshToken":"VCuafFhy81AFZjsWkbuEzdOhhRj5YTWz8fWUwHam7KM",
    "errorCode":null,
    "description":null,
}

Sending the Required ProtoBuf Messages

After receiving a valid access code, your application will need to send the ProtoOAApplicationAuthReq, ProtoOAGetAccountListByAccessTokenReq, and ProtoOAAccountAuthReq messages. This can be done as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private static RepeatedField<ProtoOACtidTraderAccount> _ctidTraderAccounts;

List<IDisposable> _disposables = new();
_disposables.Add(_client.OfType<ProtoOAGetAccountListByAccessTokenRes>().Subscribe(OnAccountIdReceived));

private static void OnAccountIdReceived(ProtoOAGetAccountListByAccessTokenRes response)
{

    Console.WriteLine($"\nMessage Received:\n{response}");

    _ctidTraderAccounts = response.CtidTraderAccount;

    var accountAuthReq = new ProtoOAAccountAuthReq
    {
        CtidTraderAccountId = (long)_ctidTraderAccounts[0].CtidTraderAccountId,
        AccessToken = _token.AccessToken,
    };

    _client.SendMessage(accountAuthReq);

}

var _client = new OpenClient(host, ApiInfo.Port, TimeSpan.FromSeconds(10), useWebSocket: false);
var applicationAuthReq = new ProtoOAApplicationAuthReq
{
    ClientId = {your_client_id}, // Add your client ID here
    ClientSecret = {your_app_secret}, // Add your app secret here
};
await _client.SendMessage(applicationAuthReq);
await Task.Delay(5000);
var getAccountListByAccessToken = new ProtoOAGetAccountListByAccessTokenReq 
{
    AccessToken = {access_token} // Add your access token here
}
var accountAuthReq = new ProtoOAAccountAuthReq 
{
    ctidTraderAccountId = {account_id}, // Add the ctidTraderAccountId of the account you want to authorize here
    AccessToken = {access_token} // Add your access token here
};
await _client.SendMessage(accountAuthReq);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
currentAccountId = None
client = Client(EndPoints.PROTOBUF_LIVE_HOST if hostType.lower() == "live" else EndPoints.PROTOBUF_DEMO_HOST, EndPoints.PROTOBUF_PORT, TcpProtocol)

def connected(client): # Callback for client connection
    print("\nConnected")
    request = ProtoOAApplicationAuthReq()
    request.clientId = appClientId
    request.clientSecret = appClientSecret
    deferred = client.send(request)



def disconnected(client, reason): # Callback for client disconnection
    print("\nDisconnected: ", reason)

def onMessageReceived(client, message): # Callback for receiving all messages
    global currentAccountId
    if message.payloadType in [ProtoOASubscribeSpotsRes().payloadType, ProtoOAAccountLogoutRes().payloadType, ProtoHeartbeatEvent().payloadType]:
        return
    elif message.payloadType == ProtoOAApplicationAuthRes().payloadType:
        print("API Application authorized\n")
        if currentAccountId is not None:
            sendProtoOAAccountAuthReq()
            return
        accountListReq = ProtoOAGetAccountListByAccessTokenReq()
        accountListReq.accessToken = accessToken
        deferred = client.send(accountListReq)
    elif message.payloadType == ProtoOAGetAccountListByAccessTokenRes().payloadType:
        protoOAGetAccountListByAccessTokenRes = Protobuf.extract(message)
        currentAccountId = int(protoOAGetAccountListByAccessTokenRes.ctidTraderAccount[0].ctidTraderAccountId)
        accountAuthReq = ProtoOAAccountAuthReq()
        accountAuthReq.ctidTraderAccountId = currentAccountId
        accountAuthReq.accessToken = accessToken
        deferred = client.send(accountAuthReq)
    elif message.payloadType == ProtoOAAccountAuthRes().payloadType:
        protoOAAccountAuthRes = Protobuf.extract(message)
        print(f"Account {protoOAAccountAuthRes.ctidTraderAccountId} has been authorized\n")
        print("This acccount will be used for all future requests\n")
    else:
        print("Message received: \n", Protobuf.extract(message))
    reactor.callLater(3, callable=executeUserCommand)

def onError(failure): # Call back for errors
    print("Message Error: ", failure)
    reactor.callLater(3, callable=executeUserCommand)

Refreshing the Access Token

After an access token expires, you will have to renew it using the refreshToken you have gotten earlier. You can perform this action via two different ways.

Sending an HTTP Request

To attain a new access token (and, subsequently, a new refresh token), you can perform the same HTTP request covered above. As shown in the below example, you would have to set the grant_type query parameter to refresh_token.

curl -X POST 'https://openapi.ctrader.com/apps/token?grant_type=refresh_token&refresh_token=asdXCVCVAS_218kjashf_asdasd2ASD_223x&client_id=5430012&client_secret=012sds23dlkjQsd' -H 'Accept: application/json' -H 'Content-Type: application/json'

You will receive a response containing the new values of the "accessToken" and "refreshToken" keys. You can safely use these new values while the old values of the same keys are automatically invalidated.

Sending a Protobuf Message

Alternatively, you can send the ProtoOARefreshTokenReq and receive the ProtoOARefreshTokenRes message containing new valid tokens.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
_disposables.Add(_client.OfType<ProtoOARefreshTokenRes>().Subscribe(OnRefreshTokenResponse));
private static void OnRefreshTokenResponse(ProtoOARefreshTokenRes response) 
{
    _token = new Token 
    {
        AccessToken = response.AccessToken,
        RefreshToken = response.RefreshToken,
        ExpiresIn = DateTimeOffset.FromUnixTimeMilliseconds(response.ExpiresIn),
        TokenType = response.TokenType,
    }
}
var refreshTokenReq = new ProtoOARefreshTokenReq
    {
        refreshToken = {your_refresh_token}, // Add your refresh token here
    };
await _client.SendMessage(applicationAuthReq);
1
newToken = auth.refreshToken("refresh_Token")

Using the Playground

The playground provides a quick means for attaining an access token for your own cTID, allowing you to quickly and easily develop and test your app before distributing it among its target audience.

To access the playground, go to the 'Applications' page and click on the 'Playground' button. In the newly appeared window, select the scope of the application and press the 'Get Token' button.

Afterward, you should see the following page.

You can use the access token and the refresh token for sending messages to the API while authenticated under your own cTID.