The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Replace scattered plan-name checks such as if (user.Plan == "Pro") with one entitlement decision. Model each paid capability as a named ID, resolve it from your authoritative subscription or account data, and enforce it on the server through an ASP.NET Core authorization policy and handler. Keep feature flags for a separate job: deciding whether a feature should be exposed, targeted, or rolled out. A flag does not prove that a customer has paid for something.
Why plan checks spread and why they break
Most applications begin with one comparison against a plan name. Within a year, the same comparison appears in controllers, Razor pages, background jobs, and view models. Each copy encodes a decision about what a plan includes. When marketing renames a tier, adds a mid-level plan, or grants a trial extension, every copy must change, and the ones you miss keep granting or denying access without any error.
As an Amazon Associate I earn from qualifying purchases.
The fix is not a cleaner if statement. It is separating three questions that plan checks currently answer at once:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →- Entitlement: Is this account allowed to use this capability?
- Exposure: Should the interface show this capability to this user right now?
- Rollout: Is the code path for this capability switched on for this environment, cohort, or percentage of users?
Entitlement is an access decision and belongs in authorization. Exposure and rollout are configuration-driven choices and belong in feature management. Mixing them is the root of most hardcoded plan checks.
#1 Best Overall
Feature flags answer a different question
Microsoft’s .NET Feature Flag Management documentation describes the purpose plainly: “Feature flags provide a way for .NET and ASP.NET Core applications to turn features on or off dynamically.” That describes state controlled by configuration. It says nothing about whether a particular customer has purchased access.
A flag named ProExports that is turned on for every Pro account is therefore a copy of the plan check, moved into configuration. It still needs someone to write the mapping from plan to flag, and it cannot answer for an individual user whose trial or add-on differs from the plan name. Treat a flag named after a subscription tier as a red flag in code review.
The distinction in this article is an architectural inference drawn from Microsoft’s separate documentation of feature management and of ASP.NET Core authorization. Those documents do not state it as a rule, but they describe mechanisms built for different decisions.
Step 1: Inventory the plan checks and name the capabilities
Search the codebase for plan identifiers and for the enums, strings, and boolean properties derived from them. A search for Plan, Tier, Subscription, and IsPaid usually finds most of them. For each hit, record three things:
- The user-visible capability it gates, such as exporting a report or inviting a team member.
- Whether the check protects a server-side operation or only changes what the interface shows.
- Whether the check is temporary, such as a rollout guarded by a plan comparison that was never removed.
Only the first group is entitlement. Cosmetic differences, like a badge or a banner, can stay in views, and rollout gates should move to feature flags. Expect the inventory to reveal duplicate checks for the same capability written in different places with slightly different logic. Those discrepancies are the bugs this refactor fixes.
Step 2: Define stable capability identifiers
Give each entitlement a stable identifier in domain language, such as reports.export or team.members.invite. The identifier should describe what the customer can do, not which plan grants it. Plans change; capabilities rarely do. Microsoft’s documentation does not prescribe this naming scheme. It is a convention that keeps application code independent of pricing.
A minimal catalogue can be a static class so identifiers are checked at compile time:
Recommended Free Tools
Rank #2
public static class Capabilities
{
public const string ReportsExport = "reports.export";
public const string TeamMembersInvite = "team.members.invite";
}
Step 3: Choose the authoritative entitlement source
The correct source depends on your billing and account model. Microsoft’s documentation does not prescribe a plan-to-feature mapping, so the schema and the source of truth are decisions you must make from your own data. The three common options are below.
| Source | How it works | Main strength | Main risk |
|---|---|---|---|
| Local entitlement projection | A table keyed by account and capability, updated from billing events or admin actions | Fast reads inside your own database; full control over the schema | Projection can lag or drift from the billing system if events are missed |
| Identity claim | Entitlements are added to the user’s token or cookie at sign-in | No database call per request | Claims persist until the token or cookie is reissued, so revoked access can outlive the change |
| External entitlement service | Your code calls a billing or licensing API for each decision or per session | Closest to the billing system of record | Adds latency and a runtime dependency; failure behaviour must be designed explicitly |
Many production systems combine a local projection for speed with periodic reconciliation against the billing system. Whichever option you choose, write down which system is authoritative when two sources disagree.
Step 4: Implement the policy, requirement, and handler
In ASP.NET Core, a policy is a named collection of requirements. A requirement describes the rule, and a handler decides whether the current user satisfies it. For entitlements, the requirement carries the capability ID, and the handler asks your entitlement source.
First, define the requirement and an entitlement lookup abstraction:
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesusing Microsoft.AspNetCore.Authorization;
public sealed class CapabilityRequirement(string capability) : IAuthorizationRequirement
{
public string Capability { get; } = capability;
}
public interface IEntitlementStore
{
Task<bool> HasCapabilityAsync(string accountId, string capability, CancellationToken ct = default);
}
Next, the handler. It reads the account from claims, asks the store, and calls Succeed only when the entitlement exists:
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
public sealed class CapabilityHandler(IEntitlementStore store)
: AuthorizationHandler<CapabilityRequirement>
{
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
CapabilityRequirement requirement)
{
var accountId = context.User.FindFirstValue("account_id");
if (string.IsNullOrEmpty(accountId))
{
return; // no Succeed call: requirement not met
}
if (await store.HasCapabilityAsync(accountId, requirement.Capability))
{
context.Succeed(requirement);
}
}
}
Register the policies and the handler in Program.cs:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("reports.export", policy =>
policy.Requirements.Add(new CapabilityRequirement(Capabilities.ReportsExport)));
options.AddPolicy("team.members.invite", policy =>
policy.Requirements.Add(new CapabilityRequirement(Capabilities.TeamMembersInvite)));
});
builder.Services.AddScoped<IAuthorizationHandler, CapabilityHandler>();
Register the handler as scoped rather than singleton if IEntitlementStore depends on a scoped service such as an Entity Framework DbContext. A singleton handler that captures a scoped dependency fails at runtime when it is resolved. The claim name account_id is an example; use whatever claim your identity provider issues.
Protect endpoints with the policy name:
app.MapPost("/reports/{id}/export", [Authorize(Policy = "reports.export")] (int id) =>
{
// export logic
return Results.Accepted();
});
Policy-based authorization is the enforcement point for these endpoints. Everywhere a plan name was compared before, replace the comparison with the policy, or with an IAuthorizationService call where the policy cannot be declared on an endpoint.
Free tools Windows power users keep installed
One-click scans. No signup required.
Step 5: Use resource-based authorization when the target matters
Some entitlements depend on the object being acted on. An account may be entitled to export reports, but only reports belonging to a workspace it is subscribed to. A user-only policy cannot see the record, so the check must run after the resource loads. ASP.NET Core’s resource-based authorization handles this with a handler typed to the resource and an imperative call to IAuthorizationService.
public sealed class ReportExportHandler(IEntitlementStore store)
: AuthorizationHandler<CapabilityRequirement, Report>
{
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
CapabilityRequirement requirement,
Report report)
{
var accountId = context.User.FindFirstValue("account_id");
if (accountId is null) return;
if (report.WorkspaceAccountId == accountId
&& await store.HasCapabilityAsync(accountId, requirement.Capability))
{
context.Succeed(requirement);
}
}
}
// In a minimal API or controller action:
var report = await db.Reports.FindAsync(id);
if (report is null) return Results.NotFound();
var result = await authorization.AuthorizeAsync(user, report, Capabilities.ReportsExport);
if (!result.Succeeded) return Results.Forbid();
Load the resource first, then authorize against it. Returning NotFound before the authorization check, as shown, avoids confirming that a record exists to a caller who cannot see it, but choose the status code behaviour your API contract requires.
Step 6: Enforce on the server, then decide what the interface shows
Authorization must run where the protected operation runs. Client-side checks can hide a button, but a request can always be sent directly. A safe order of operations for each capability is:
- The server applies the entitlement policy to the endpoint or service method that performs the work.
- The server returns a forbidden or not-found response when the policy fails, and never performs the action.
- The view receives a capability set, such as
IAuthorizationServiceresults or a precomputed list of granted IDs, and renders the matching controls. - Tests confirm that a direct request is rejected even when the button is hidden.
Step 3 of the view logic is where cosmetic and entitlement logic can drift apart again. Compute the interface’s view of capabilities from the same handler or the same store, not from a second copy of the plan rules.
Rank #4
Step 7: Add Microsoft.FeatureManagement only for rollout behaviour
Once entitlements live behind policies, feature flags can handle the questions they are designed for. The Microsoft.FeatureManagement library provides asynchronous checks through IFeatureManager.IsEnabledAsync, supports filters and variants, and integrates with .NET configuration and dependency injection. The API reference pages available at the time of writing report package version 4.3.0; confirm the current version on NuGet before you pin one in your project.
Install the package, register it, and bind flags from configuration:
// Program.cs
builder.Services.AddFeatureManagement();
{
"FeatureManagement": {
"NewExportLayout": false,
"BulkInviteRollout": true
}
}
public sealed class ExportController(IFeatureManager features) : ControllerBase
{
[HttpPost("reports/{id}/export")]
public async Task<IActionResult> Export(int id)
{
if (await features.IsEnabledAsync("NewExportLayout"))
{
// new rendering path, still behind the reports.export policy
}
return Accepted();
}
}
The flag decides which path runs. The policy decides whether the caller may run it at all. Use AddFeatureManagement alongside, not instead of, the authorization configuration above. Azure App Configuration is one option for centrally managing these flags across environments; its documentation describes it as a configuration source for .NET feature flags, and it is a separate service with its own pricing and access requirements that you would need to evaluate on its own terms.
When a flag is the wrong tool
- The question is “Has this customer paid?” Answer it with the entitlement source.
- The rule must hold for every request regardless of rollout settings, such as a paid export limit. Keep it in authorization, where a misconfigured flag cannot disable it.
- The flag would be named after a plan. Name it after the rollout instead, such as
NewExportLayout.
Migrating without breaking access
Move one capability at a time. For each existing plan check:
- Add the capability ID and its policy, backed by the new entitlement source.
- Route the old plan comparison through the same evaluator during the transition, so both paths produce the same result for every account in your data.
- Write tests for each plan, for accounts with add-ons or trial extensions, and for accounts whose plan changed recently.
- Compare decisions in telemetry: log the old and new result, and alert on any mismatch before switching traffic fully.
- Delete the plan comparison once parity holds across a representative period.
Expect mismatches in the first pass. They usually reveal accounts whose entitlements were never recorded in the new source. Fix the data before changing the code path.
Staleness and caching
An entitlement check is only as current as the data it reads. Decide, per capability, how quickly a subscription change must take effect. A cancelled export add-on may be acceptable to honour for a few minutes; a revoked admin role may not. The documentation reviewed does not establish a universal propagation interval for commercial entitlements, so this window is a product and security decision for your team.
Best Value
Identity claims are the most common source of delay. Once a claim is issued, it remains valid until the token or cookie expires or is reissued. If you use claims, keep token lifetimes short or refresh claims when a subscription changes. If you cache entitlement lookups in memory or a distributed cache, define the expiry and invalidate the cache from billing events, not only on a timer.
Evidence and limits
The Microsoft documentation supporting this approach comes from three sources: the .NET Feature Flag Management documentation on Microsoft Learn, the IFeatureManager and IFeatureFilter API references for Microsoft.FeatureManagement, and the Microsoft Learn pages on policy-based and resource-based authorization in ASP.NET Core. They describe how each mechanism works. They do not prescribe a plan-to-feature schema, a naming convention, or a caching policy, and they contain no published figures on how entitlement migrations perform. The examples above are implementation patterns you should adapt and test in your own application.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Check package versions and API signatures against the .NET version and Microsoft.FeatureManagement release your project uses before copying code.
Next, confirm that the authoritative entitlement data is complete for every account before removing the old checks. The refactor changes where the decision lives; it cannot make missing data correct.
Frequently Asked Questions
Can I put plan entitlements inside a feature flag provider?
You can, but it is usually the wrong layer. A flag provider controls whether functionality is turned on, often through configuration. It does not know which account paid for what. Keep the paid-capability decision in an entitlement source behind an authorization policy, and use flags only for rollout.
Do I need an ASP.NET Core policy if I already check a claim in the controller?
A controller-level claim check works for a single endpoint, but it spreads the rule across the code again. A named policy gives every endpoint, service, and view the same definition, and it lets resource-based checks run against loaded records through IAuthorizationService.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




