public async Task <TokenResponse> GetAccessTokenAsync(string chatId)
        {
            _logger.Debug("fetching access token from database for chat {chatId} ...", chatId);
            TokenResponse tokenResponse = await _accessTokenStore.GetAsync <TokenResponse>(chatId);

            if (tokenResponse == null) // its the first time spreadsheet access for this chat id
            {
                _logger.Debug("access token is not present in databse for chat {chatId}", chatId);

                string accessCode = await _accessCodeStore.GetCodeAsync(chatId);

                if (accessCode == null) //user never authorized
                {
                    _logger.Debug("access code is also not preset for chatId {chatId}. This user is not authorized", chatId);
                    throw new UnauthorizedChatException(chatId);
                }
                else //user authenticatedd but access token was never generated
                {
                    //generate access token for the first time
                    _logger.Debug("generating access token for chat id - {chatId}", chatId);

                    tokenResponse = await AuthorizationCodeFlow.ExchangeCodeForTokenAsync(
                        chatId,
                        accessCode,
                        _botConfig.AuthCallbackUrl,
                        CancellationToken.None);

                    _logger.Information("access token generated successfully for chat id - {chatId}", chatId);

                    if (string.IsNullOrEmpty(tokenResponse.RefreshToken))
                    {
                        _logger.Warning("the newly generated access token does not have refresh token in it");
                    }
                }
            }
            else if (tokenResponse.IsExpired(AuthorizationCodeFlow.Clock))
            {
                if (string.IsNullOrEmpty(tokenResponse.RefreshToken))
                {
                    _logger.Warning("the access token fetched from database does not have a refresh token. " +
                                    "it will not be able to refresh");
                }
                else
                {
                    _logger.Debug("access token fetched from databse is expired... refreshing it...");
                }

                tokenResponse = await AuthorizationCodeFlow.RefreshTokenAsync(chatId, tokenResponse.RefreshToken,
                                                                              CancellationToken.None);

                _logger.Information("token refreshed successfully");

                if (string.IsNullOrEmpty(tokenResponse.RefreshToken))
                {
                    _logger.Warning("the newly refreshed access token does not have refresh token in it");
                }
            }

            return(tokenResponse);
        }
 /// <summary>
 /// Determines the need for retrieval of a new authorization code, based on the given token and the
 /// authorization code flow.
 /// </summary>
 public bool ShouldRequestAuthorizationCode(TokenResponse token)
 {
     // TODO: This code should be shared between this class and AuthorizationCodeWebApp.
     // If the flow includes a parameter that requires a new token, if the stored token is null or it doesn't
     // have a refresh token and the access token is expired we need to retrieve a new authorization code.
     return(Flow.ShouldForceTokenRetrieval() || token == null || (token.RefreshToken == null &&
                                                                  token.IsExpired(flow.Clock)));
 }
Пример #3
0
 private bool DoWeHaveUsefulToken(TokenResponse token)
 {
     if (Instance.ShouldForceTokenRetrieval() || token == null)
     {
         return(true);
     }
     if (token.RefreshToken == null)
     {
         return(token.IsExpired(SystemClock.Default));
     }
     return(false);
 }
        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;
        }
Пример #5
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));
        }
Пример #6
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);
        }
Пример #7
0
 private bool IsValidToken(TokenResponse token)
 {
     // If the token is expired but we have a non-null RefreshToken, we can assume the token will be
     // automatically refreshed when we query Google Blogger and is therefore valid.
     return(token != null && (!token.IsExpired(SystemClock.Default) || token.RefreshToken != null));
 }
Пример #8
0
        public void IsExpired()
        {
            var issued = DateTime.UtcNow;
            var newNow = DateTime.UtcNow.AddSeconds(100);

            var mockClock = new MockClock
            {
                UtcNow = newNow
            };

            const int ExpectedRefreshTimeSeconds = 6 * 60;

            // Issued not set.
            var response = new TokenResponse();

            Assert.True(response.IsExpired(mockClock));

            // ExpiresInSeconds is not set.
            response = new TokenResponse()
            {
                IssuedUtc = issued
            };
            Assert.True(response.IsExpired(mockClock));

            response = new TokenResponse()
            {
                AccessToken = "a", ExpiresInSeconds = 1, IssuedUtc = issued
            };
            Assert.True(response.IsExpired(mockClock));

            response = new TokenResponse()
            {
                AccessToken = "a", ExpiresInSeconds = 100, IssuedUtc = issued
            };
            Assert.True(response.IsExpired(mockClock));

            response = new TokenResponse()
            {
                AccessToken = "a", ExpiresInSeconds = 100 + ExpectedRefreshTimeSeconds - 2, IssuedUtc = issued
            };
            Assert.True(response.IsExpired(mockClock));

            response = new TokenResponse()
            {
                AccessToken = "a", ExpiresInSeconds = 100 + ExpectedRefreshTimeSeconds - 1, IssuedUtc = issued
            };
            Assert.True(response.IsExpired(mockClock));

            response = new TokenResponse()
            {
                AccessToken = "a", ExpiresInSeconds = 100 + ExpectedRefreshTimeSeconds, IssuedUtc = issued
            };
            Assert.True(response.IsExpired(mockClock));

            response = new TokenResponse()
            {
                AccessToken = "a", ExpiresInSeconds = 100 + ExpectedRefreshTimeSeconds + 1, IssuedUtc = issued
            };
            Assert.False(response.IsExpired(mockClock));

            response = new TokenResponse()
            {
                AccessToken = "a", ExpiresInSeconds = 100 + ExpectedRefreshTimeSeconds + 2, IssuedUtc = issued
            };
            Assert.False(response.IsExpired(mockClock));
        }
Пример #9
0
        /// <summary>
        /// Performs the initial authentication and the subsequient reauthentication
        /// </summary>
        /// <returns></returns>
        public bool PerformAuthentication()
        {
            if (m_service != null && m_credentialToken != null && !m_credentialToken.IsExpired(SystemClock.Default))
            {
                return(true);
            }

            try
            {
                UserCredential credential;
                byte[]         secrets = Resources.client_secret;

                Log.Write("Performing Google authentication");

                using (var stream = new MemoryStream(secrets))
                {
                    var cancel = new CancellationTokenSource(DEFAULT_CANCEL_TIME_OUT);

                    credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                        GoogleClientSecrets.Load(stream).Secrets,
                        m_scopes,
                        "user",
                        cancel.Token,
                        m_fileDataStore)
                                 .Result;

                    m_credentialToken = credential.Token;

                    Log.Write("Credential file saved to: " + m_workingDirectory);
                }

                // Create Google Calendar API service.
                m_service = new CalendarService(new BaseClientService.Initializer()
                {
                    HttpClientInitializer = credential,
                    ApplicationName       = APPLICATION_NAME
                });

                return(true);
            } catch (GoogleApiException ex)
            {
                Log.Write(ex);
                MessageBox.Show(
                    "There has been an error when trying to authenticate the user. Please review the error log for more information.",
                    "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return(false);
            } catch (AggregateException ex)
            {
                Log.Write(ex);

                if (ex.InnerException != null && ex.InnerException.GetType() == typeof(TaskCanceledException))
                {
                    MessageBox.Show("The authorization timed out. Please try again.", "Oh no", MessageBoxButtons.OK,
                                    MessageBoxIcon.Warning);
                }
                else
                {
                    MessageBox.Show(
                        "Access to the calendar was denied by Google. You may need to reauthenticate this application.",
                        "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                return(false);
            }
        }