Skip to main content

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

  1. Ένα token rotates → το νέο token + bumped TokenVersion persists.
  2. Το CopyEngineSupervisor στο hosting node re-reads το plan κάθε reconcile cycle και υπολογίζει ένα token signature (access tokens + versions). Μια αλλαγή σημαίνει rotation.
  3. Αντί να σπάσει το host και να ξεκινήσει (που θα ρίξει το master's execution stream), το supervisor pushes το νέο token στο running host.
  4. Το 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 — TokenVersion bumps σε Refresh; host performs μία in-place swap χωρίς restart; cross-cID invalidation swaps source και destination tokens; ένα invalidated destination token raises CopyTokenInvalidated και auto-recovers στο επόμενο token push (M1); το watchdog IsHostDead decision restarts ένα completed/faulted host και leaves ένα reassigned profile alone (M2).
  • Integration — TokenVersion persists + increments through EF σε real Postgres; το token signature αλλάζει σε ένα version bump ακόμη κι αν το string είναι unchanged.