@@ -25,6 +25,15 @@ namespace QuantConnect.Brokerages.Authentication
2525 /// </summary>
2626 public class LeanOAuthTokenHandler : LeanOAuthTokenHandler < LeanTokenCredentials >
2727 {
28+ /// <summary>
29+ /// Initializes a new instance of the <see cref="LeanOAuthTokenHandler"/> class with default token credentials type.
30+ /// </summary>
31+ /// <param name="apiClient">The API client used to communicate with the Lean platform.</param>
32+ /// <param name="request">The request model used to generate the access token.</param>
33+ /// <param name="tokenLifetime">
34+ /// The expected lifetime of a fetched token. A 1-minute safety buffer is applied before expiry.
35+ /// Must be provided explicitly — each brokerage has a different token lifetime.
36+ /// </param>
2837 public LeanOAuthTokenHandler ( ApiConnection apiClient , OAuthTokenRequest request , TimeSpan tokenLifetime )
2938 : base ( apiClient , request , tokenLifetime )
3039 {
@@ -71,14 +80,13 @@ public class LeanOAuthTokenHandler<T> : LeanTokenHandler<T>
7180
7281 /// <summary>
7382 /// Stores the current access token and its type used for authenticating requests to the Lean platform.
74- /// Written inside <see cref="_lock"/>; read outside the lock via volatile fast path .
83+ /// Always accessed inside <see cref="_lock"/>.
7584 /// </summary>
7685 private T _tokenCredentials ;
7786
7887 /// <summary>
7988 /// The UTC timestamp after which the cached token should be refreshed.
80- /// Always written inside <see cref="_lock"/> before <see cref="_tokenCredentials"/> is set,
81- /// so that a volatile read of <see cref="_tokenCredentials"/> guarantees visibility of this field.
89+ /// Always accessed inside <see cref="_lock"/>.
8290 /// </summary>
8391 private DateTime _tokenExpiresAt ;
8492
@@ -106,21 +114,14 @@ public LeanOAuthTokenHandler(ApiConnection apiClient, OAuthTokenRequest request,
106114 /// <summary>
107115 /// Retrieves a valid access token from the Lean platform.
108116 /// Caches and reuses tokens until expiration to minimize unnecessary requests.
109- /// Retries up to <see cref="MaxRetryCount"/> times on failure, and is thread -safe via double-checked locking .
117+ /// Retries up to <see cref="MaxRetryCount"/> times on failure. Thread -safe via a lock .
110118 /// </summary>
111119 /// <param name="cancellationToken">A token used to observe cancellation requests.</param>
112- /// <returns>A <see cref="TokenCredentials "/> containing the token type and access token string.</returns>
120+ /// <returns>A <see cref="LeanTokenCredentials "/> instance containing the token type and access token string.</returns>
113121 public override T GetAccessToken ( CancellationToken cancellationToken )
114122 {
115- // Fast path: return cached token without acquiring the lock
116- if ( _tokenCredentials != null && DateTime . UtcNow < _tokenExpiresAt )
117- {
118- return _tokenCredentials ;
119- }
120-
121123 lock ( _lock )
122124 {
123- // Second check: another thread may have refreshed while we waited for the lock
124125 if ( _tokenCredentials != null && DateTime . UtcNow < _tokenExpiresAt )
125126 {
126127 return _tokenCredentials ;
@@ -136,8 +137,6 @@ public override T GetAccessToken(CancellationToken cancellationToken)
136137 {
137138 if ( response . Success )
138139 {
139- // Write expiry before credentials — the volatile write of _tokenCredentials
140- // acts as a release fence, ensuring the fast-path reader sees _tokenExpiresAt.
141140 _tokenExpiresAt = DateTime . UtcNow + _tokenLifetime - OffsetBeforeExpiration ;
142141 return _tokenCredentials = response ;
143142 }
0 commit comments