コンテンツにスキップ

アプリと口座の認証

cTrader Open APIの認証プロセスはOAuth 2.0標準に基づいています。

このフレームワークにより、サードパーティアプリケーションは、リソース所有者の代わりに、リソース所有者とサービスの間で承認インタラクションを調整するか、またはサードパーティアプリケーションが独自にアクセスを取得することを許可することで、サービスへの限定されたアクセスを取得できます。

認証フロー

OAuth 2.0標準に従い、口座認証には認証コードとアクセストークンの両方が必要です。

トークンの定義

認証コードは、個々のcTIDに対して発行される短期トークンです。 その有効期間は1分です。 アクセストークンは、アプリケーションがcTraderバックエンドとの間でメッセージを送受信することを可能にする長期トークンです。 これを受け取った後、認証コードを迅速にアクセストークンと交換する必要があります。その後、cTraderバックエンドに送信されるすべてのメッセージは、受け取ったアクセストークンで署名され、アプリケーションを認証する必要があります。 アクセストークンの有効期間は2,628,000秒(約30日)です。 有効期限が切れた後、リフレッシュトークンを使用して新しいアクセストークンを生成できます。 リフレッシュトークンには有効期限がありません。

通常、誰かが初めてあなたのアプリケーションを使用し始めるプロセスは次のようになります:

1. 特定のcTIDを持つユーザーが、アプリケーションがデプロイされている場所にアクセスします。ユーザーはアクションボタンまたは同等のものと対話します。

2. アプリケーションは、埋め込みブラウザ(または他の適切な手段)を使用して、ユーザーを特定のURIにリダイレクトし、そこでアプリに必要な権限を付与します。

信頼を得る

ユーザーをこのURIにリダイレクトする前に、ユーザーがあなたのアプリを信頼し、追加の権限を提供する必要があることを認識していることを確認してください。

3. ユーザーは事前に定義されたリダイレクトURLに転送されます。 このURLには、codeクエリパラメータの値としてアクセストークンが含まれています。

4. ユーザーは、自分自身でまたは自動的にアプリに戻されます。 アプリはcodeパラメータの値を通知されます。

5. アプリは、認証コードをアクセストークンと交換するリクエストを特定のエンドポイントに送信します。

6. アプリは、accessTokenキーの値としてアクセストークンを含む応答を受け取ります。

7. アプリは、clientIdclientSecretフィールドを含むProtoOAApplicationAuthReqメッセージを送信します。

8. アプリは、accessTokenフィールドを含むProtoOAGetAccountListByAccessTokenReqメッセージを送信します。 応答(ProtoOAGetAccountListByAccessTokenRes)には、繰り返しのctidTraderAccountIdフィールドが含まれています。

9. 応答を受け取った後、アプリはctidTraderAccountIdaccessTokenフィールドを含むProtoOAAccountAuthReqメッセージを送信します。

その後、ユーザーは、ステップ8で提供されたctidTraderAccountIdと一致するctidTraderAccountIdを持つ口座で認証されるべきです。

以下の図は、基本的な認証フローがあなたのOpen APIアプリケーションの観点からどのように機能するかを説明しています。

graph TB
  A([ユーザーを特定の <br/> URIにリダイレクト]) ==> B([認証コードを <br/> 受け取る]);
  B ==> C([アクセスコード取得のための <br/> リクエストを送信]);
  C ==> D([ProOAApplicationAuthReq <br/> メッセージを <br/> 送信]);
  D ==> E([ProtoOAAccountAuthReq <br/> メッセージを <br/> 送信]);

以下のサブセクションでは、認証フローの主要なステップを詳細に説明します。

リダイレクトURIを追加する

前述のように、ユーザーが特定のリダイレクトURIに送信された後、アプリは認証コードを取得する必要があります。 このURIには、codeクエリパラメータの値として認証コードが含まれます。

新しいリダイレクトURIを追加するには、次の手順を実行します:

1. Open APIポータルのアプリケーションページを開きます。

2. リダイレクトURLを追加したいアプリケーションの行の編集ボタンを選択します。

3. 新しく表示された画面で、リダイレクトURIセクションまでスクロールします。

4. 利用可能な空のテキスト入力フィールドにリダイレクトURLを追加します。 利用可能なフィールドが表示されていない場合は、リダイレクトURIを追加ボタンを選択します。

5. ページの下部にある保存ボタンを選択して変更を適用します。

リダイレクトURIを管理する

最初のリダイレクトURIは、通常のユーザーにアクセスを許可する前にアプリケーションをテストできるPlaygroundサービスのためのものです。 これは、本番アプリケーションに使用してはなりません。 必要に応じて、無制限の数のリダイレクトURIを追加できます。

認証コードを取得する

特定のcTIDの認証コードを受け取るために、アプリはユーザーを次のURIに送信する必要があります。

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

注意

この段階で、ユーザーはあなたのアプリケーションまたはサービスが彼らの取引口座にアクセスすることを許可する必要があります。 その結果、ユーザーにあなたのアプリケーションまたはサービスの使用が権限、データストレージ、およびデータ処理に関して何を必要とするかを完全に理解させることが重要です。

このURIには次のクエリパラメータがあります。

パラメータ 必須ですか? 定義
client_id お客様のOpen APIアプリケーションの一意の識別子。
redirect_uri お客様のアプリケーションの設定で指定された有効なリダイレクトURI。
scope お客様のアプリケーションに付与したい権限の範囲。 以下の値が受け入れられます:
accounts。 閲覧のみのアクセスが許可されます。 お客様のアプリケーションは口座情報と統計にアクセスできますが、取引操作を実行することはできません。
trading. 口座情報と統計、および許可されたすべての取引操作に対する完全なアクセスが許可されます。
product 不可 このパラメータが指定され、webに設定されている場合、認証画面にはヘッダーとフッターがなく、モバイルデバイスでの表示が向上します。 このパラメータはオプションですが、webに設定することをお勧めします。

client_idを表示するには、アプリケーションページに移動し、認証情報列の表示ボタンを選択してください。

ユーザーが上記のURIに送信されると(両方のパラメータが有効である限り)、ログイン/サインアップ画面が表示され、cTIDで認証するよう促されます。 その後、ユーザーにはダイアログが表示され、アプリケーションがcTIDにリンクされた1つ以上の口座にアクセスすることを許可できます。

ユーザーがアクセスを許可ボタンを選択すると、最初に指定したredirect_uriにリダイレクトされます。 以下の例に示すように、認証コードがクエリパラメータとしてこのURIに追加されます。

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

この時点で、お客様のアプリケーションはcodeパラメータの値にアクセスして保存する必要があります。 認証コードが期限切れになる前に、アクセストークンと迅速に交換する必要があります。

追加の口座

ユーザーが上記のアクションを完了した後に追加の口座を作成した場合、これらの口座はアプリケーション内で認証されません。 代わりに、ユーザーは上記のURLにリダイレクトされ、最後のログイン以降に作成した追加の口座にアプリケーションがアクセスできるようにする必要があります。

Google経由の認証

上記のURIがWebView経由で開かれ、ユーザーがGoogleでサインインすることを選択した場合、Googleはアクセスがブロックされましたページを表示し、ユーザーが先に進むのを防ぎます。

これを防ぐためには、ページを開くデバイスのuser-agentシグネチャが以下のようになっていることを確認してください。

'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'

アクセストークンを取得する

アクセストークンを取得するには、以下のREST API呼び出しを実行してください。

相対URL

リクエストURLはhttps://openapi.ctrader.comに対する相対パスです。

メソッド URL
GET apps/token

このリクエストには以下のクエリパラメータが含まれます。

パラメータ 必須ですか? 定義
grant_type このエンドポイントに送信されるトークンのタイプ。 以下の値が受け入れられます。
authorization_code. 認証コードがエンドポイントに送信され、アクセストークンと交換されます。
refresh_token. リフレッシュトークンがエンドポイントに送信され、既存のアクセストークンを更新します。
code 不可 エンドポイントに送信されるトークン。 grant_type=authorization_codeの場合、このパラメータは必須です。
redirect_uri 不可 お客様のアプリケーションの設定で指定された有効なリダイレクトURI。 grant_type=authorization_codeの場合、このパラメータは必須です。
client_id お客様のOpen APIアプリケーションの一意の識別子。
client_secret お客様のOpen APIアプリケーションに割り当てられたシークレットキー。

リクエスト例は以下にあります。

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'

client_secretを表示するには、アプリケーションページに戻り、認証情報列の表示ボタンを選択してください。

リクエストが成功すると、以下のキーを含むレスポンスが返されます。

キー 値のタイプ 定義
"accessToken" 文字列 お客様のアプリケーションがcTrader Open APIに送信するメッセージを認証するために使用するアクセストークン。
"tokenType" 文字列 アクセストークンのタイプ(bearer""がこのキーの値であるべきです。リクエストを行うためにREST標準が使用されます)。
"expiresIn" 整数 アクセストークンが期限切れになる前に経過する必要がある秒数(デフォルトでは、このキーの値は2628000です)。
"refreshToken" 文字列 アクセストークンの有効期限が切れた後に、それを更新するために使用する必要があるリフレッシュトークン。 リフレッシュトークン自体には有効期限はありません。
"errorCode" string/null リクエストが失敗した理由を説明するエラーコード。 リクエストが成功した場合、このキーの値はnullである必要があります。
"description" string/null 失敗したリクエスト中に発生したエラーの説明。 リクエストが成功した場合、このキーの値はnullである必要があります。

成功したレスポンスの例については、以下のスニペットを参照してください。

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

必要なProtoBufメッセージを送信する

有効なアクセスコードを受け取った後、アプリケーションはProtoOAApplicationAuthReqProtoOAGetAccountListByAccessTokenReqProtoOAAccountAuthReqメッセージを送信する必要があります。 これは以下のように行うことができます。

 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)

アクセストークンを更新する

アクセストークンの有効期限が切れた後、以前に受け取ったrefreshTokenを使用してそれを更新する必要があります。 このアクションは2つの異なる方法で実行できます。

HTTPリクエストを送信する

新しいアクセストークン(および、新しいリフレッシュトークン)を取得するために、前述のと同じHTTPリクエストを実行できます。 以下の例に示すように、grant_typeクエリパラメータを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'

新しいaccessTokenrefreshTokenキーの値を含むレスポンスを受け取ります。 これらの新しい値を安全に使用でき、同じキーの古い値は自動的に無効になります。

Protobufメッセージを送信する

または、ProtoOARefreshTokenReqを送信し、新しい有効なトークンを含むProtoOARefreshTokenResメッセージを受け取ることができます。

 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")

Playgroundを使用する

Playgroundは、自分のcTIDのアクセストークンを取得するための迅速な手段を提供し、アプリをターゲットオーディエンスに配布する前に迅速かつ簡単に開発およびテストすることができます。

Playgroundにアクセスするには、Applicationsページに移動し、Playgroundボタンを選択します。 新しく表示されたウィンドウで、アプリケーションのスコープを選択し、トークンを取得ボタンをクリックします。

その後、以下のページが表示されます。

自分のcTIDで認証されている間、APIにメッセージを送信するためにアクセストークンとリフレッシュトークンを使用できます。