/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using RestSharp; using QuantConnect.Api; using System.Threading; namespace QuantConnect.Brokerages.Authentication { /// /// Handles OAuth token retrieval and caching by interacting with the Lean platform. /// Implements retry and expiration logic for secure HTTP communication. /// /// The request type used to acquire the access token. /// The response type containing access token metadata. public sealed class OAuthTokenHandler : TokenHandler where TRequest : AccessTokenMetaDataRequest where TResponse : AccessTokenMetaDataResponse { /// /// The serialized JSON body representing the token request model. /// private readonly string _jsonBodyRequest; /// /// Stores metadata about the Lean access token and its expiration details. /// private TResponse _accessTokenMetaData; /// /// API client for communicating with the Lean platform. /// private readonly ApiConnection _apiClient; /// /// Stores the current access token and its type used for authenticating requests to the Lean platform. /// private TokenCredentials _tokenCredentials; /// /// Initializes a new instance of the class. /// /// The API client used to communicate with the Lean platform. /// The request model used to generate the access token. public OAuthTokenHandler(ApiConnection apiClient, TRequest modelRequest) { _apiClient = apiClient; _jsonBodyRequest = modelRequest.ToJson(); } /// /// Retrieves a valid access token from the Lean platform. /// Caches and reuses tokens until expiration to minimize unnecessary requests. /// /// A token used to observe cancellation requests. /// A tuple containing the token type and access token string. public override TokenCredentials GetAccessToken(CancellationToken cancellationToken) { if (_accessTokenMetaData != null && DateTime.UtcNow < _accessTokenMetaData.Expiration) { return _tokenCredentials; } try { var request = new RestRequest("live/auth0/refresh", Method.POST); request.AddJsonBody(_jsonBodyRequest); if (_apiClient.TryRequest(request, out var response)) { if (response.Success && !string.IsNullOrEmpty(response.AccessToken)) { _accessTokenMetaData = response; _tokenCredentials = new(response.TokenType, response.AccessToken); return _tokenCredentials; } } throw new InvalidOperationException(string.Join(",", response.Errors)); } catch (Exception ex) { throw new InvalidOperationException($"{nameof(OAuthTokenHandler)}.{nameof(GetAccessToken)}: {ex.Message}"); } } } }