internal async Task<string> GetAccessTokenForRequestAsync(CancellationToken cancellationToken)
        {
            Task<TokenResponse> refreshTask;
            lock (_lock)
            {
                // If current token is not soft-expired, then return it.
                if (_token != null && !_token.IsExpired(_clock))
                {
                    return _token.AccessToken;
                }
                // Token refresh required, so start a task if not already started
                if (_refreshTask == null)
                {
                    // Task.Run is required if the refresh completes synchronously,
                    // otherwise _refreshTask is updated in an incorrect order.
                    // And Task.Run also means it can be run here in the lock.
                    _refreshTask = Task.Run(RefreshTokenAsync);

                    // Let's make sure that exceptions in _refreshTask are always observed.
                    // Note that we don't keep a reference to this new task as we don't really
                    // care about the errors, and we want calling code explicitly awaiting on _refreshTask
                    // to actually fail if there's an error. We just schedule it to run and that's enough for
                    // avoiding exception observavility issues.
                    _refreshTask.ContinueWith(LogException, TaskContinuationOptions.OnlyOnFaulted);
                }
                // If current token is not hard-expired, then return it.
                if (_token != null && !_token.IsEffectivelyExpired(_clock))
                {
                    return _token.AccessToken;
                }
                refreshTask = _refreshTask;

                async Task LogException(Task task)
                {
                    try
                    {
                        await task.ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        _logger.Debug($"An error occured on a background token refresh task.{Environment.NewLine}{ex}");
                    }
                }
            }

            refreshTask = refreshTask.WithCancellationToken(cancellationToken);
            return (await refreshTask.ConfigureAwait(false)).AccessToken;
        }
Пример #2
0
        public void Expiry(
            DateTime now, DateTime issuedAt, long?expiresInSeconds,
            string accessToken, string idToken,
            bool expectedExpired, bool expectedEffectiveExpires)
        {
            var clock = new MockClock(now);
            var token = new TokenResponse
            {
                IssuedUtc        = issuedAt,
                ExpiresInSeconds = expiresInSeconds,
                AccessToken      = accessToken,
                IdToken          = idToken
            };

            Assert.Equal(expectedExpired, token.IsExpired(clock));
            Assert.Equal(expectedEffectiveExpires, token.IsEffectivelyExpired(clock));
        }
Пример #3
0
        internal async Task <string> GetAccessTokenForRequestAsync(CancellationToken cancellationToken)
        {
            Task <TokenResponse> refreshTask;

            lock (_lock)
            {
                // If current token is not soft-expired, then return it.
                if (_token != null && !_token.IsExpired(_clock))
                {
                    return(_token.AccessToken);
                }
                // Token refresh required, so start a task if not already started
                if (_refreshTask == null)
                {
                    // Task.Run is required if the refresh completes synchronously,
                    // otherwise _refreshTask is updated in an incorrect order.
                    // And Task.Run also means it can be run here in the lock.
                    _refreshTask = Task.Run(RefreshTokenAsync);
                }
                // If current token is not hard-expired, then return it.
                if (_token != null && !_token.IsEffectivelyExpired(_clock))
                {
                    return(_token.AccessToken);
                }
                refreshTask = _refreshTask;
            }
            // Otherwise block on refresh task.
            if (cancellationToken.CanBeCanceled)
            {
                // Reasonably simple way of creating a task that can be cancelled, based on another task.
                // (It would be nice if this were simpler.)
                refreshTask = refreshTask.ContinueWith(
                    task => ResultWithUnwrappedExceptions(task),
                    cancellationToken,
                    TaskContinuationOptions.None,
                    TaskScheduler.Default);
            }
            return((await refreshTask.ConfigureAwait(false)).AccessToken);
        }