private GetTokenOperation CreateOperation( VssTraceActivity traceActivity, IssuedToken failedToken, CancellationToken cancellationToken, out GetTokenOperation operationInProgress) { operationInProgress = null; GetTokenOperation operation = null; lock (m_thisLock) { if (m_operations == null) { m_operations = new List <GetTokenOperation>(); } // Grab the main operation which is doing the work (if any) if (m_operations.Count > 0) { operationInProgress = m_operations[0]; // Use the existing completion source when creating the new operation operation = new GetTokenOperation(traceActivity, this, failedToken, cancellationToken, operationInProgress.CompletionSource); } else { operation = new GetTokenOperation(traceActivity, this, failedToken, cancellationToken); } m_operations.Add(operation); } return(operation); }
private static Task <IssuedToken> PostCallback( Object state, CancellationTokenSource timeoutTokenSource) { // Make sure that we were not cancelled (timed out) before this callback is invoked. using (timeoutTokenSource) { timeoutTokenSource.CancelAfter(-1); if (timeoutTokenSource.IsCancellationRequested) { return(Task.FromResult <IssuedToken>(null)); } } GetTokenOperation thisPtr = (GetTokenOperation)state; return(thisPtr.Provider.OnGetTokenAsync(thisPtr.FailedToken, thisPtr.CancellationToken)); }
/// <summary> /// Retrieves a token for the credentials. /// </summary> /// <param name="failedToken">The token which previously failed authentication, if available</param> /// <param name="cancellationToken">The <c>CancellationToken</c>that will be assigned to the new task</param> /// <returns>A security token for the current credentials</returns> public async Task <IssuedToken> GetTokenAsync( IssuedToken failedToken, CancellationToken cancellationToken) { IssuedToken currentToken = this.CurrentToken; VssTraceActivity traceActivity = VssTraceActivity.Current; Stopwatch aadAuthTokenTimer = Stopwatch.StartNew(); try { VssHttpEventSource.Log.AuthenticationStart(traceActivity); if (currentToken != null) { VssHttpEventSource.Log.IssuedTokenRetrievedFromCache(traceActivity, this, currentToken); return(currentToken); } else { GetTokenOperation operation = null; try { GetTokenOperation operationInProgress; operation = CreateOperation(traceActivity, failedToken, cancellationToken, out operationInProgress); if (operationInProgress == null) { return(await operation.GetTokenAsync(traceActivity).ConfigureAwait(false)); } else { return(await operationInProgress.WaitForTokenAsync(traceActivity, cancellationToken).ConfigureAwait(false)); } } finally { lock (m_thisLock) { m_operations.Remove(operation); } operation?.Dispose(); } } } finally { VssHttpEventSource.Log.AuthenticationStop(traceActivity); aadAuthTokenTimer.Stop(); TimeSpan getTokenTime = aadAuthTokenTimer.Elapsed; if (getTokenTime.TotalSeconds >= c_slowTokenAcquisitionTimeInSeconds) { // It may seem strange to pass the string value of TotalSeconds into this method, but testing // showed that ETW is persnickety when you register a method in an EventSource that doesn't // use strings or integers as its parameters. It is easier to simply give the method a string // than figure out to get ETW to reliably accept a double or TimeSpan. VssHttpEventSource.Log.AuthorizationDelayed(getTokenTime.TotalSeconds.ToString()); } } }