Browse Source

Log token read failures without the exception

- Match the ASP.NET Core provider, which keeps the key id and key ring location out of the log
- Assert the provider the manager uses, not only the one the key's descriptor hands out
pull/26113/head
maliming 3 days ago
parent
commit
2cf45f9eb1
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 2
      docs/en/release-info/migration-guides/abp-10-7.md
  2. 6
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AspNetCore/AbpSingleActiveTokenProvider.cs
  3. 5
      modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/CrossHostTokenProvider_Tests.cs

2
docs/en/release-info/migration-guides/abp-10-7.md

@ -151,7 +151,7 @@ Add a navigation property to the principal entity if you rely on the previous be
- The provider options classes derive from `AbpDataProtectionTokenProviderOptions` instead of `DataProtectionTokenProviderOptions`. The `Name` and `TokenLifespan` properties are unchanged, but code that assigns one of them to `DataProtectionTokenProviderOptions`, passes it to a method taking that type, returns it, or uses it as a generic constraint no longer compiles.
- `IdentityUserManagerSingleActiveTokenExtensions` moved with the providers, and its `Remove*TokenAsync` helpers changed in two ways. They now follow the provider's options `Name` instead of the key it is registered under, so an application that renamed a provider gets the hash it actually wrote removed. And they throw an `AbpException` instead of reporting success when the key is not served by an `AbpSingleActiveTokenProvider`, which is the case once the ABP providers are turned off: there is no stored hash to remove then, and a token that was never single-active cannot be revoked this way.
- The constructors changed accordingly. `AbpSingleActiveTokenProvider` takes `IOptions<AbpDataProtectionTokenProviderOptions>`, which each provider satisfies with its own concrete options class, and `ILogger<AbpSingleActiveTokenProvider>` instead of `IOptions<DataProtectionTokenProviderOptions>` and `ILogger<DataProtectorTokenProvider<IdentityUser>>`. The five DataProtector-based providers (`AbpDefaultTokenProvider`, `AbpPasswordResetTokenProvider`, `AbpEmailConfirmationTokenProvider`, `AbpChangeEmailTokenProvider`, `LinkUserTokenProvider`) take the new logger type as well. The email and phone 2FA providers moved unchanged. Constructing a provider by hand also behaves differently at the edges: a null options now throws instead of falling back to the ASP.NET Core defaults, which carry the wrong provider name, and a null logger falls back to `NullLogger` instead of throwing.
- `AbpIdentityDomainModule` calls `AddDataProtection()`, because `UserManager` instantiates every provider in `Tokens.ProviderMap` when it is resolved and the DataProtector-based providers need `IDataProtectionProvider`. Hosts that never issue a token, such as a DbMigrator console application, do not register it themselves, and now load the key ring on startup and create one if the store is empty. That is a side effect for such a host, not a reason to configure it: only a host that generates or validates a token needs the same key ring and `SetApplicationName` as the rest of the solution.
- `AbpIdentityDomainModule` calls `AddDataProtection()`, because `UserManager` instantiates every provider in `Tokens.ProviderMap` when it is resolved and the DataProtector-based providers need `IDataProtectionProvider`. Hosts that never issue a token, such as a DbMigrator console application, do not register it themselves. Data Protection registers a hosted service that loads the key ring when the host starts and creates one if the store is empty, so such a host now does that too. That is a side effect for such a host, not a reason to configure it: only a host that generates or validates a token needs the same key ring and `SetApplicationName` as the rest of the solution.
**What to do**

6
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AspNetCore/AbpSingleActiveTokenProvider.cs

@ -185,9 +185,11 @@ public abstract class AbpSingleActiveTokenProvider : IUserTwoFactorTokenProvider
return false;
}
}
catch (Exception ex)
catch (Exception)
{
Logger.LogDebug(ex, "Could not read the '{ProviderName}' token. It was protected under another provider name, key ring or application name, or the payload is not a token this provider produced.", Options.Name);
// Without the exception, the way ASP.NET Core's provider logs this: it carries the key id and
// the key ring location.
Logger.LogDebug("Could not read the '{ProviderName}' token. It was protected under another provider name, key ring or application name, or the payload is not a token this provider produced.", Options.Name);
return false;
}
}

5
modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/CrossHostTokenProvider_Tests.cs

@ -270,6 +270,11 @@ public class CrossHostTokenProvider_Tests
.ShouldBe(typeof(OtherUserTokenProvider));
var userManager = sp.GetRequiredService<IdentityUserManager>();
// What the map says and what the manager uses come apart here.
userManager.FindTokenProvider(AbpPasswordResetTokenProvider.ProviderName)
.ShouldBeOfType<AbpPasswordResetTokenProvider>();
var user = await userManager.GetByIdAsync(userId);
await userManager.GeneratePasswordResetTokenAsync(user);

Loading…
Cancel
Save