You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AspNet.Security.OpenIdConnect.Server更新后令牌失效及刷新令牌机制咨询

Hi Eduardo, sorry to hear you're hitting this token invalidation issue after updating your .NET Core project or restarting IIS—let's break down why this is happening and how to fix it for AspNet.Security.OpenIdConnect.Server 2.0.0-rc2-final.

Why This Happens

The core problem here is the default behavior of ASOS 2.0.0-rc2-final:

  • It uses an in-memory store for refresh tokens, which gets wiped clean every time your app restarts (whether from updating DLLs, recycling the IIS app pool, or a full restart).
  • It also generates a temporary signing key on startup—when the app restarts, this key changes, so any existing access tokens can't be validated anymore.

Together, these two issues mean all existing tokens (access and refresh) become invalid after a restart, forcing users to log in again.

Fixes to Keep Users Logged In

You'll need to address both the token storage and signing key issues. Here's how to implement each step:

1. Use a Persistent Refresh Token Store

Replace the in-memory store with a persistent one (like a database) so refresh tokens survive app restarts.

First, create a refresh token entity and DbContext:

public class RefreshToken
{
    public int Id { get; set; }
    public string Token { get; set; }
    public string UserId { get; set; }
    public string ClientId { get; set; }
    public DateTime Expires { get; set; }
}

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
    public DbSet<RefreshToken> RefreshTokens { get; set; }
}

Next, configure ASOS to use this persistent store by hooking into its provider events:

public void ConfigureServices(IServiceCollection services)
{
    // Add your database context
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    // Configure ASOS
    services.AddOpenIdConnectServer(options =>
    {
        options.TokenEndpointPath = "/connect/token";
        options.AllowRefreshTokenFlow = true;

        // 👇 Hook into refresh token creation to save it to the database
        options.Provider.OnCreateRefreshToken = context =>
        {
            // Generate a unique refresh token (you can use Guid or a more secure method)
            context.RefreshToken = Guid.NewGuid().ToString("n");

            // Save the token to your database
            var token = new RefreshToken
            {
                Token = context.RefreshToken,
                UserId = context.Ticket.Principal.FindFirst(ClaimTypes.NameIdentifier).Value,
                ClientId = context.ClientId,
                Expires = context.Ticket.Properties.ExpiresUtc?.UtcDateTime ?? DateTime.UtcNow.AddDays(7)
            };

            using (var db = context.HttpContext.RequestServices.GetRequiredService<ApplicationDbContext>())
            {
                db.RefreshTokens.Add(token);
                db.SaveChanges();
            }

            return Task.CompletedTask;
        };

        // 👇 Validate refresh tokens against the database
        options.Provider.OnValidateRefreshToken = context =>
        {
            using (var db = context.HttpContext.RequestServices.GetRequiredService<ApplicationDbContext>())
            {
                var token = db.RefreshTokens.FirstOrDefault(t => t.Token == context.RefreshToken);

                // Reject if token doesn't exist or is expired
                if (token == null || token.Expires < DateTime.UtcNow)
                {
                    context.Reject(
                        error: OpenIdConnectConstants.Errors.InvalidGrant,
                        description: "The specified refresh token is invalid or has expired.");
                    return Task.CompletedTask;
                }

                // Validate the user and client match
                var user = db.Users.Find(token.UserId);
                if (user == null || token.ClientId != context.ClientId)
                {
                    context.Reject(
                        error: OpenIdConnectConstants.Errors.InvalidGrant,
                        description: "The specified refresh token is invalid.");
                    return Task.CompletedTask;
                }

                // Create a new claims principal for the user
                var identity = new ClaimsIdentity(context.Scheme.Name);
                identity.AddClaim(ClaimTypes.NameIdentifier, user.Id.ToString());
                // Add any other user claims you need here

                context.Validate(new ClaimsPrincipal(identity));
            }

            return Task.CompletedTask;
        };

        // 👇 Optional: Clean up revoked refresh tokens
        options.Provider.OnRevokeRefreshToken = context =>
        {
            using (var db = context.HttpContext.RequestServices.GetRequiredService<ApplicationDbContext>())
            {
                var token = db.RefreshTokens.FirstOrDefault(t => t.Token == context.RefreshToken);
                if (token != null)
                {
                    db.RefreshTokens.Remove(token);
                    db.SaveChanges();
                }
            }

            return Task.CompletedTask;
        };
    });
}

2. Use a Fixed Signing Key

To ensure access tokens remain valid after restarts, configure a fixed signing key instead of the temporary one ASOS generates by default. You can use either a symmetric key or an X.509 certificate:

Option A: Symmetric Key (Simpler for Development/Testing)

options.SigningCredentials.AddKey(new SymmetricSecurityKey(
    Encoding.UTF8.GetBytes("your-32-character-or-longer-secure-secret-key-here")));

Make sure the key is long enough (at least 32 bytes for HS256) and stored securely (use environment variables or a secrets manager in production).

Option B: X.509 Certificate (Recommended for Production)

options.SigningCredentials.AddCertificate(new X509Certificate2(
    "path/to/your-certificate.pfx", "your-certificate-password"));

Use a valid SSL certificate (self-signed for development, trusted CA for production).

Bonus: Clean Up Expired Tokens

Add a background task or scheduled job to delete expired refresh tokens from your database regularly—this prevents your store from getting cluttered with unused tokens.

Final Notes

With these changes, when you update your DLLs or restart IIS:

  • Access tokens will still be valid (since the signing key is fixed) until their expiration time.
  • Refresh tokens will be retrieved from the database, so users can get new access tokens without logging in again.

内容的提问来源于stack exchange,提问作者Eduardo Tolino

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:50:35