Open API token lifecycle
Το cTrader's Open API επιτρέπει ένα έγκυρο access token ανά cTrader ID (cID) κάθε φορά. Τη στιγμή που εκδίδεται νέο token — μια scheduled refresh, ή re-authorization όταν ο χρήστης συνδέει ένα άλλο account στο ίδιο cID — το προηγούμενο access token δεν ισχύει πλέον. Ένα copy engine που τρέχει σε remote node κρατάει αυτό το νεκρό token, ώστε το νέο token πρέπει να φτάσει χωρίς να ρίξει τη live connection.
Model
OpenApiAuthorizationείναι το aggregate που κρατάει τα encrypted access + refresh tokens ενός cID. Ένα unique index στο(UserId, CtidUserId)επιβάλλει ακριβώς μία authorization ανά cID ανά user.TokenVersion— ένας μονοτονικός counter που αυξάνεται κάθε φορά που το token rotates (Refresh(), που καλύπτει επίσης το re-auth path όταν συνδέεται ένα άλλο account στο ίδιο cID). Είναι το version marker για το single-valid-token rule και είναι αυτό που ένας running host χρησιμοποιεί για να εντοπίσει μία αλλαγή ακόμη κι αν δύο token strings τυχαίνει να συγκρούονται.- Τα tokens κρυπτογραφούνται στο rest μέσω
ISecretProtector(EncryptionPurposes.OpenApiAccessToken/OpenApiRefreshToken). Ποτέ δεν καταγράφονται ή αποθηκεύονται σε plaintext.
Propagation (graceful in-place swap)
- Ένα token rotates → το νέο token + bumped
TokenVersionpersists. - Το
CopyEngineSupervisorστο hosting node re-reads το plan κάθε reconcile cycle και υπολογίζει ένα token signature (access tokens + versions). Μια αλλαγή σημαίνει rotation. - Αντί να σπάσει το host και να ξεκινήσει (που θα ρίξει το master's execution stream), το supervisor pushes το νέο token στο running host.
- Το host re-authenticates το affected account στο existing socket
(
ProtoOAAccountAuthReqξανά) μέσωSwapAccessTokenAsync, τότε κάνει ένα light reconcile. Το παλιό token πεθαίνει; το copy stream ποτέ δεν σταματάει.
Αυτό είναι που κάνει το cross-cID case ασφαλές: ένας χρήστης που προσθέτει ένα δεύτερο account από το ίδιο cID mid-run δεν ισχύει πλέον το παλιό token, και το running copy profile συνεχίζει στο νέο.
Refresh
OpenApiTokenRefreshService (background) proactively ανανεώνει τις authorizations πριν expiry;
OpenApiAuthorization.IsExpiring(threshold, now) το πύλη. Το cTrader rotates το refresh token
σε κάθε refresh, ώστε το νέο refresh token persists αμέσως; ένα read-only cache που δεν μπορεί να
persists θα self-invalidate (σχετικό με το in-cluster test Job, που mount ένα writable copy
του secret).
Failure escalation
Ένα failed refresh δεν είναι σιωπηλό. OpenApiAuthorization.MarkRefreshFailed(reason, now, criticalWindow)
records RefreshFailedAt, increments ConsecutiveRefreshFailures, και πάντα raises
AccessTokenRefreshFailed (warning). Όταν το token είναι τώρα εντός App:OpenApi:TokenRefreshCriticalWindow
(default 6h) του expiry και η refresh είναι ακόμα failing, escalates μία φορά με μία
AccessTokenRefreshCritical domain event + Critical log ώστε ο owner να μπορεί να re-authorize πριν
copy/prop-firm operations χάσουν το token. Ο failure counter και escalation latch reset σε το επόμενο
successful Refresh. Το service συνεχίζει την retry κάθε TokenRefreshInterval, ώστε ένα provider/maintenance
outage self-heals όταν το refresh endpoint επιστρέφει.
Invalidation alert & auto-recovery (M1)
Ένα partial/again-authorization σε ένα cID δεν ισχύει πλέον το token που ένας running copy host κρατάει. Όταν μια
trading call απορρίπτεται με OpenApiErrorKind.TokenInvalid, το host raises ένα διακριτό
CopyTokenInvalidated alert (log 1078) — όχι ένα generic failure — ώστε το notification channel ξέρει ότι ένα
token χρειάζεται προσοχή. Η ανάκαμψη είναι αυτόματη: το supervisor re-reads την authorization κάθε cycle και,
όταν το refreshed token αλλάζει το token signature, το pushes στο running host για ένα in-place
swap — η copying συνεχίζει χωρίς manual re-add. Ένα NotLinkable profile (token/auth προσωρινά
unresolvable) αξιολογείται επίσης κάθε supervisor cycle και hosted τη στιγμή που το plan του κατασκευάζεται ξανά.
Host liveness watchdog (M2)
Το supervisor παρακολουθεί το run task κάθε hosted profile. Αν ένα host exits ή faults ενώ το profile του είναι
ακόμα assigned σε αυτό το node, το watchdog cancels και restarts το επόμενο cycle (log
CopyHostRestarted), ώστε ένα wedged host self-heals αντί να χρειάζεται ένα manual restart — και ένα failure του profile ποτέ δεν stalls τα άλλα (per-profile isolation).
Tests
- Unit —
TokenVersionbumps σεRefresh; host performs μία in-place swap χωρίς restart; cross-cID invalidation swaps source και destination tokens; ένα invalidated destination token raisesCopyTokenInvalidatedκαι auto-recovers στο επόμενο token push (M1); το watchdogIsHostDeaddecision restarts ένα completed/faulted host και leaves ένα reassigned profile alone (M2). - Integration —
TokenVersionpersists + increments through EF σε real Postgres; το token signature αλλάζει σε ένα version bump ακόμη κι αν το string είναι unchanged.