Skip to content

Conversation

@EzrealJ
Copy link
Collaborator

@EzrealJ EzrealJ commented Dec 5, 2025

No description provided.

@coderabbitai
Copy link

coderabbitai bot commented Dec 5, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@EzrealJ EzrealJ requested review from Copilot and xljiulang December 5, 2025 14:04
Copilot finished reviewing on behalf of EzrealJ December 5, 2025 14:07
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a token refresh window feature for OAuth token management (community proposal #276 by qoder). The feature allows tokens to be refreshed proactively before they expire, preventing race conditions where tokens might expire between validation and actual use.

  • Adds configurable refresh window with three strategies: fixed seconds, percentage-based, and auto (minimum of both)
  • Provides multiple configuration approaches: inline via HttpApiOptions, standalone via IOptionsMonitor, and from configuration files
  • Includes comprehensive unit tests for the token expiration logic

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
docs/guide/6_auth-token-extension.md Documents the new token refresh window feature with configuration examples
WebApiClientCore.Test.csproj Adds project reference to OAuths extension for testing
TokenResultTest.cs Comprehensive test suite for token expiration and refresh window logic
TokenResult.cs Adds overloaded IsExpired method accepting refresh window parameter
TokenProvider.cs Implements refresh window logic with configuration fallback chain
RefreshWindowStrategy.cs Defines enum for refresh window calculation strategies
OAuthTokenOptions.cs Configuration class for refresh window settings
HttpApiOptionsExtensions.cs Extension methods to configure OAuth tokens via HttpApiOptions.Properties
OAuthTokenOptionsExtensions.cs Dependency injection extensions for standalone configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +146 to +149
if (httpApiOptions != null)
{
return httpApiOptions.GetOAuthTokenOptions();
}
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The null check on line 146 is redundant. The IOptionsMonitor<T>.Get(string name) method never returns null - it creates a new default instance if no configuration exists. This check should be removed.

Suggested change
if (httpApiOptions != null)
{
return httpApiOptions.GetOAuthTokenOptions();
}
return httpApiOptions.GetOAuthTokenOptions();

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +179
/// <summary>
/// 判断是否应该刷新Token
/// 混合方案:优先从独立配置读取,其次从 HttpApiOptions.Properties 读取,最后使用默认行为
/// </summary>
/// <param name="token">token结果</param>
/// <returns></returns>
private bool ShouldRefreshToken(TokenResult token)
{
// 获取配置
var tokenOptions = this.GetTokenRefreshOptions();

if (!tokenOptions.UseTokenRefreshWindow)
{
// 如果禁用刷新窗口,使用原有行为
return token.IsExpired();
}

// 计算刷新窗口
var refreshWindow = this.CalculateRefreshWindow(token, tokenOptions);
return token.IsExpired(refreshWindow);
}

/// <summary>
/// 获取Token刷新配置
/// 优先级:独立配置(IOptionsMonitor&lt;OAuthTokenOptions&gt;) > HttpApiOptions.Properties > 默认配置
/// </summary>
/// <returns>Token刷新配置</returns>
private OAuthTokenOptions GetTokenRefreshOptions()
{
// 优先从独立配置读取 (方案2:独立配置类)
var oauthOptionsMonitor = this.services.GetService<IOptionsMonitor<OAuthTokenOptions>>();
if (oauthOptionsMonitor != null)
{
try
{
var options = oauthOptionsMonitor.Get(this.Name);
// 检查是否为默认配置,如果不是则使用
if (options != null)
{
return options;
}
}
catch
{
// 如果获取失败,继续尝试下一种方式
}
}

// 其次从 HttpApiOptions.Properties 读取 (方案1:Properties 字典)
var httpApiOptionsMonitor = this.services.GetService<IOptionsMonitor<HttpApiOptions>>();
if (httpApiOptionsMonitor != null)
{
try
{
var httpApiOptions = httpApiOptionsMonitor.Get(this.Name);
if (httpApiOptions != null)
{
return httpApiOptions.GetOAuthTokenOptions();
}
}
catch
{
// 如果获取失败,使用默认配置
}
}

// 最后使用默认配置
return new OAuthTokenOptions();
}

/// <summary>
/// 根据配置策略计算刷新窗口
/// </summary>
/// <param name="token">Token结果</param>
/// <param name="options">配置选项</param>
/// <returns>刷新窗口时间</returns>
private TimeSpan CalculateRefreshWindow(TokenResult token, OAuthTokenOptions options)
{
var fixedWindow = TimeSpan.FromSeconds(options.RefreshWindowSeconds);
var percentageWindow = TimeSpan.FromSeconds(token.Expires_in * options.RefreshWindowPercentage);

return options.RefreshWindowStrategy switch
{
RefreshWindowStrategy.FixedSeconds => fixedWindow,
RefreshWindowStrategy.Percentage => percentageWindow,
RefreshWindowStrategy.Auto => fixedWindow < percentageWindow ? fixedWindow : percentageWindow,
_ => fixedWindow < percentageWindow ? fixedWindow : percentageWindow // 默认使用Auto
};
}
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new token refresh window feature lacks test coverage for key scenarios:

  • Tests for ShouldRefreshToken method
  • Tests for GetTokenRefreshOptions method's configuration priority logic
  • Tests for CalculateRefreshWindow method with different strategies
  • Integration tests verifying the configuration from both HttpApiOptions and IOptionsMonitor<OAuthTokenOptions>

Consider adding tests that verify the configuration fallback chain and the refresh window calculation strategies work correctly.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,41 @@
using Microsoft.Extensions.DependencyInjection;
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing using Microsoft.Extensions.Options; import. The code calls builder.Services.Configure<OAuthTokenOptions> on lines 22 and 37, which is an extension method from the Microsoft.Extensions.Options namespace. Without this import, the code will not compile.

Suggested change
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

Copilot uses AI. Check for mistakes.
Comment on lines +127 to +131
// 检查是否为默认配置,如果不是则使用
if (options != null)
{
return options;
}
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The null check on line 128 is redundant. The IOptionsMonitor<T>.Get(string name) method never returns null - it creates a new default instance if no configuration exists. This check should either be removed or the logic should verify if the options are actually configured (e.g., by checking specific property values differ from defaults).

Suggested change
// 检查是否为默认配置,如果不是则使用
if (options != null)
{
return options;
}
// 直接返回 options,无需检查 null(IOptionsMonitor.Get 永不返回 null)
return options;

Copilot uses AI. Check for mistakes.
Comment on lines +133 to +136
catch
{
// 如果获取失败,继续尝试下一种方式
}
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty catch blocks on lines 133 and 151 silently suppress all exceptions, making debugging difficult. Consider at least logging the exception or catching specific exception types that are expected. If any exception is acceptable here, add a comment explaining why it's safe to ignore.

Copilot uses AI. Check for mistakes.
Comment on lines +151 to +154
catch
{
// 如果获取失败,使用默认配置
}
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty catch block silently suppresses all exceptions, making debugging difficult. Consider at least logging the exception or catching specific exception types that are expected. If any exception is acceptable here, add a comment explaining why it's safe to ignore.

Copilot uses AI. Check for mistakes.
/// </summary>
/// <param name="token">token结果</param>
/// <returns></returns>
private bool ShouldRefreshToken(TokenResult token)
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local scope variable 'token' shadows TokenProvider.token.

Copilot uses AI. Check for mistakes.
Comment on lines +167 to +170
private TimeSpan CalculateRefreshWindow(TokenResult token, OAuthTokenOptions options)
{
var fixedWindow = TimeSpan.FromSeconds(options.RefreshWindowSeconds);
var percentageWindow = TimeSpan.FromSeconds(token.Expires_in * options.RefreshWindowPercentage);
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local scope variable 'token' shadows TokenProvider.token.

Suggested change
private TimeSpan CalculateRefreshWindow(TokenResult token, OAuthTokenOptions options)
{
var fixedWindow = TimeSpan.FromSeconds(options.RefreshWindowSeconds);
var percentageWindow = TimeSpan.FromSeconds(token.Expires_in * options.RefreshWindowPercentage);
private TimeSpan CalculateRefreshWindow(TokenResult tokenResult, OAuthTokenOptions options)
{
var fixedWindow = TimeSpan.FromSeconds(options.RefreshWindowSeconds);
var percentageWindow = TimeSpan.FromSeconds(tokenResult.Expires_in * options.RefreshWindowPercentage);

Copilot uses AI. Check for mistakes.
@EzrealJ
Copy link
Collaborator Author

EzrealJ commented Dec 5, 2025

#276 @vicenteyu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant