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))); }
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; }
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)); }
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); }
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)); }
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)); }
/// <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); } }