Bỏ qua

Xác thực ứng dụng và tài khoản

Quy trình xác thực cTrader Open API dựa trên tiêu chuẩn OAuth 2.0.

Khung này cho phép các ứng dụng của bên thứ ba đạt được quyền truy cập hạn chế vào một dịch vụ, hoặc thay mặt chủ sở hữu tài nguyên bằng cách điều phối một tương tác phê duyệt giữa chủ sở hữu tài nguyên và dịch vụ, hoặc bằng cách cho phép ứng dụng của bên thứ ba đạt được quyền truy cập thay mặt chính nó.

Luồng xác thực

Theo tiêu chuẩn OAuth 2.0, xác thực tài khoản yêu cầu cả mã ủy quyền và mã truy cập.

Định nghĩa mã

Mã ủy quyền là một mã ngắn hạn được cấp cho một cTID cụ thể. Thời gian hết hạn của nó là một phút. Mã truy cập là một mã dài hạn cho phép một ứng dụng gửi và nhận tin nhắn đến và từ backend của cTrader. Sau khi nhận được mã ủy quyền, nó phải nhanh chóng được đổi lấy mã truy cập; sau đó, tất cả các tin nhắn gửi đến backend của cTrader phải được ký bằng mã truy cập đã nhận để xác thực ứng dụng của bạn. Thời gian hết hạn của mã truy cập là 2,628,000 giây (khoảng 30 ngày). Sau khi hết hạn, một mã truy cập mới có thể được tạo bằng mã làm mới. Mã làm mới không có thời gian hết hạn.

Thông thường, quy trình khi một người mới bắt đầu sử dụng ứng dụng của bạn lần đầu tiên sẽ diễn ra như sau:

1. Một người dùng với một cTID cụ thể truy cập ứng dụng của bạn ở bất kỳ nơi nào nó được triển khai; người dùng tương tác với một nút hành động hoặc tương đương.

2. Ứng dụng sử dụng trình duyệt nhúng (hoặc bất kỳ phương tiện phù hợp nào khác) để chuyển hướng người dùng đến một URI cụ thể nơi họ sẽ cấp cho ứng dụng các quyền cần thiết.

Gây dựng niềm tin

Trước khi chuyển hướng người dùng đến URI này, hãy đảm bảo rằng người dùng tin tưởng ứng dụng của bạn và nhận thức được việc họ sẽ cần cung cấp các quyền bổ sung.

3. Người dùng được chuyển đến một URL chuyển hướng được định trước. URL này chứa mã truy cập làm giá trị của tham số truy vấn code.

4. Người dùng được chuyển trở lại ứng dụng một cách tự động hoặc tự nguyện. Ứng dụng được thông báo về giá trị của tham số code.

5. Ứng dụng gửi yêu cầu đổi mã ủy quyền lấy mã truy cập đến một điểm cuối cụ thể.

6. Ứng dụng nhận được phản hồi chứa mã truy cập làm giá trị của khóa accessToken.

7. Ứng dụng gửi tin nhắn ProtoOAApplicationAuthReq chứa các trường clientIdclientSecret.

8. Ứng dụng gửi tin nhắn ProtoOAGetAccountListByAccessTokenReq chứa trường accessToken. Phản hồi (ProtoOAGetAccountListByAccessTokenRes) chứa trường lặp lại ctidTraderAccountId.

9. Sau khi nhận được phản hồi, ứng dụng gửi tin nhắn ProtoOAAccountAuthReq chứa các trường ctidTraderAccountIdaccessToken.

Sau đó, người dùng sẽ được xác thực dưới tài khoản có ctidTraderAccountId khớp với ctidTraderAccountId được cung cấp trong bước 8.

Biểu đồ dưới đây giải thích cách luồng xác thực cơ bản hoạt động từ góc nhìn của ứng dụng Open API của bạn.

graph TB
  A([Chuyển hướng người dùng <br/> đến một URI cụ thể]) ==> B([Nhận mã <br/> ủy quyền]);
  B ==> C([Gửi yêu cầu để nhận <br/> mã truy cập]);
  C ==> D([Gửi thông điệp <br/> ProOAApplicationAuthReq]);
  D ==> E([Gửi thông điệp <br/> ProtoOAAccountAuthReq]);

Trong các phần phụ dưới đây, chúng tôi giải thích chi tiết các bước chính của luồng xác thực.

Thêm URI chuyển hướng

Như đã trình bày trước đó, ứng dụng của bạn cần đạt được mã ủy quyền sau khi người dùng được gửi đến một URI chuyển hướng cụ thể. URI này sẽ bao gồm mã ủy quyền làm giá trị của tham số truy vấn code.

Để thêm một URI chuyển hướng mới, hãy thực hiện các bước sau:

1. Mở trang Ứng dụng trong cổng Open API.

2. Chọn nút Chỉnh sửa trong hàng ứng dụng mà bạn muốn thêm URL chuyển hướng.

3. Trong màn hình mới xuất hiện, cuộn xuống phần URI chuyển hướng.

4. Thêm URL chuyển hướng vào một trường nhập văn bản trống có sẵn. Nếu không có trường nào khả dụng, hãy chọn nút Thêm URI chuyển hướng.

5. Chọn nút Lưu ở cuối trang để áp dụng các thay đổi của bạn.

Quản lý URI chuyển hướng

URI chuyển hướng đầu tiên là cho dịch vụ Playground cho phép bạn kiểm tra ứng dụng của mình trước khi cấp quyền truy cập cho người dùng thông thường. Nó không bao giờ được sử dụng cho ứng dụng sản xuất. Bạn có thể thêm số lượng URI chuyển hướng không giới hạn nếu cần.

Đạt được mã ủy quyền

Để nhận mã ủy quyền cho một cTID cụ thể, ứng dụng của bạn sẽ cần gửi người dùng đến URI sau.

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

Ghi chú

Ở giai đoạn này, người dùng sẽ cần cho phép ứng dụng hoặc dịch vụ của bạn truy cập vào các tài khoản giao dịch của họ. Do đó, điều quan trọng là bạn phải cung cấp cho người dùng hiểu đầy đủ về những gì sử dụng ứng dụng hoặc dịch vụ của bạn sẽ yêu cầu liên quan đến quyền, lưu trữ dữ liệu và xử lý dữ liệu.

URI này có các tham số truy vấn sau.

Tham số Bắt buộc? Định nghĩa
client_id Định danh duy nhất của ứng dụng Open API của bạn.
redirect_uri Một URI chuyển hướng hợp lệ được chỉ định trong cài đặt của ứng dụng của bạn.
scope Phạm vi của các quyền mà bạn muốn được cấp cho ứng dụng của bạn. Các giá trị sau được chấp nhận:
accounts. Chỉ được cấp quyền truy cập xem. Ứng dụng của bạn sẽ có thể truy cập thông tin và thống kê tài khoản; thực hiện các hoạt động giao dịch sẽ không thể thực hiện được.
trading. Toàn quyền truy cập vào thông tin và thống kê tài khoản cũng như tất cả các hoạt động giao dịch được phép.
product Không Nếu tham số này được chỉ định và đặt thành web, màn hình ủy quyền sẽ không có phần đầu và chân trang, giúp nó trông đẹp hơn trên các thiết bị di động. Mặc dù tham số này là tùy chọn, chúng tôi khuyên bạn nên đặt nó thành web.

Để xem client_id của bạn, hãy truy cập trang Ứng dụng và chọn nút Xem trong cột Thông tin đăng nhập.

Sau khi người dùng được chuyển hướng đến URI trên (và miễn là cả hai tham số đều hợp lệ), họ sẽ thấy màn hình đăng nhập/đăng ký nhắc họ ủy quyền dưới cTID của họ. Sau đó, người dùng sẽ được hiển thị một hộp thoại nơi họ có thể cho phép ứng dụng truy cập vào một hoặc nhiều tài khoản được liên kết với cTID của họ.

Khi người dùng chọn nút Cho phép truy cập, họ sẽ được chuyển hướng đến redirect_uri mà bạn đã chỉ định ban đầu. Như được hiển thị trong ví dụ bên dưới, mã ủy quyền sẽ được thêm vào URI này như một tham số truy vấn.

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

Tại thời điểm này, ứng dụng của bạn sẽ cần truy cập và lưu trữ giá trị của tham số code. Nó phải được nhanh chóng trao đổi với một mã thông báo truy cập trước khi mã ủy quyền hết hạn.

Tài khoản bổ sung

Nếu người dùng tạo thêm tài khoản sau khi hoàn thành các hành động được mô tả ở trên, các tài khoản này sẽ không được ủy quyền trong ứng dụng. Thay vào đó, người dùng sẽ cần được chuyển hướng lại đến URL được cung cấp ở trên để họ có thể cấp quyền truy cập cho ứng dụng đối với bất kỳ tài khoản bổ sung nào mà họ đã tạo kể từ lần đăng nhập cuối cùng.

Xác thực qua Google

Nếu URI trên được mở qua WebView và người dùng chọn Google để đăng nhập, Google sẽ hiển thị trang Truy cập bị chặn, ngăn người dùng tiếp tục.

Để ngăn chặn điều này, hãy đảm bảo rằng chữ ký user-agent của thiết bị mở trang trông như sau.

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

Nhận mã thông báo truy cập

Để nhận mã thông báo truy cập, hãy thực hiện lệnh gọi REST API sau.

URL tương đối

URL yêu cầu là tương đối với https://openapi.ctrader.com.

Phương thức URL
GET apps/token

Yêu cầu này bao gồm các tham số truy vấn sau.

Tham số Bắt buộc? Định nghĩa
grant_type Loại mã thông báo được gửi đến điểm cuối này. Các giá trị sau được chấp nhận.
authorization_code. Một mã ủy quyền được gửi đến điểm cuối để trao đổi nó với một mã thông báo truy cập.
refresh_token. Một mã thông báo làm mới được gửi đến điểm cuối để làm mới mã thông báo truy cập hiện có.
code Không Mã thông báo được gửi đến điểm cuối. Tham số này là bắt buộc nếu grant_type=authorization_code.
redirect_uri Không Một URI chuyển hướng hợp lệ được chỉ định trong cài đặt của ứng dụng của bạn. Tham số này là bắt buộc nếu grant_type=authorization_code.
client_id Định danh duy nhất của ứng dụng Open API của bạn.
client_secret Khóa bí mật được chỉ định cho ứng dụng Open API của bạn.

Ví dụ về yêu cầu có thể được tìm thấy bên dưới.

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'

Để xem client_secret của bạn, hãy quay lại trang Ứng dụng và chọn nút Xem trong cột Thông tin đăng nhập.

Khi yêu cầu thành công, bạn sẽ nhận được phản hồi với các khóa sau.

Khóa Loại giá trị Định nghĩa
"accessToken" chuỗi Mã thông báo truy cập mà ứng dụng của bạn sẽ sử dụng để xác thực các tin nhắn được gửi đến cTrader Open API.
"tokenType" chuỗi Loại mã thông báo truy cập (bearer"" nên là giá trị của khóa này vì tiêu chuẩn REST được sử dụng để thực hiện yêu cầu).
"expiresIn" integer Số giây phải trôi qua trước khi mã thông báo truy cập hết hạn (theo mặc định, giá trị của khóa này bằng 2628000).
"refreshToken" chuỗi Mã thông báo làm mới phải được sử dụng để gia hạn mã thông báo truy cập một khi nó hết hạn. Mã thông báo làm mới không có thời gian hết hạn.
"errorCode" string/null Mã lỗi mô tả lý do tại sao một yêu cầu không thành công. Đối với một yêu cầu thành công, giá trị của khóa này nên là null.
"description" string/null Mô tả về một lỗi đã xảy ra trong một yêu cầu không thành công. Đối với một yêu cầu thành công, giá trị của khóa này nên là null.

Để xem ví dụ về phản hồi thành công, hãy xem đoạn mã bên dưới.

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

Gửi các tin nhắn ProtoBuf cần thiết

Sau khi nhận được mã truy cập hợp lệ, ứng dụng của bạn sẽ cần gửi các tin nhắn ProtoOAApplicationAuthReq, ProtoOAGetAccountListByAccessTokenReqProtoOAAccountAuthReq. Điều này có thể được thực hiện như sau.

 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)

Làm mới mã thông báo truy cập

Sau khi mã thông báo truy cập hết hạn, bạn sẽ phải gia hạn nó bằng cách sử dụng refreshToken mà bạn đã nhận được trước đó. Bạn có thể thực hiện hành động này thông qua hai phương pháp khác nhau.

Gửi yêu cầu HTTP

Để nhận mã thông báo truy cập mới (và sau đó là mã thông báo làm mới mới), bạn có thể thực hiện cùng một yêu cầu HTTP đã đề cập ở trên. Như được hiển thị trong ví dụ bên dưới, bạn sẽ phải đặt tham số truy vấn grant_type thành 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'

Bạn sẽ nhận được phản hồi chứa các giá trị mới của các khóa accessTokenrefreshToken. Bạn có thể sử dụng an toàn các giá trị mới này trong khi các giá trị cũ của cùng các khóa sẽ tự động bị vô hiệu hóa.

Gửi tin nhắn Protobuf

Ngoài ra, bạn có thể gửi tin nhắn ProtoOARefreshTokenReq và nhận tin nhắn ProtoOARefreshTokenRes chứa các mã thông báo hợp lệ mới.

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

Sử dụng Playground

Playground cung cấp một phương tiện nhanh chóng để nhận mã thông báo truy cập cho cTID của riêng bạn, cho phép bạn phát triển và kiểm tra ứng dụng của mình một cách nhanh chóng và dễ dàng trước khi phân phối nó cho đối tượng mục tiêu.

Để truy cập Playground, hãy truy cập trang Ứng dụng và chọn nút Playground. Trong cửa sổ mới xuất hiện, chọn phạm vi của ứng dụng và nhấp vào nút Nhận mã thông báo.

Sau đó, bạn sẽ thấy trang sau.

Bạn có thể sử dụng mã thông báo truy cập và mã thông báo làm mới để gửi tin nhắn đến API trong khi được xác thực dưới cTID của riêng bạn.