Skip to main content

Troubleshooting & FAQ

Common Issues​

After upgrading to v10.0.3, resolving IUserContext throws Unable to resolve service for type 'Microsoft.AspNetCore.Http.IHttpContextAccessor'​

Cause: AddUserContext() no longer registers IHttpContextAccessor as a side effect (it did up to v10.0.2), but the UserContext implementation requires it to reach the current request's ClaimsPrincipal.

Fix: Register it explicitly in Program.cs:

builder.Services.AddHttpContextAccessor();
builder.Services.AddUserContext();

No service for type 'RA.Utilities.Authorization.IUserContext' has been registered​

Cause: This error comes from v10.0.2, where AddUserContext() registered only the internal UserContext class without mapping IUserContext to it.

Fix: Upgrade to v10.0.3, where AddUserContext() registers the interface directly.

InvalidOperationException: User id is unavailable​

Cause: IUserContext.UserId or ClaimsPrincipal.GetUserId() is called but the user is not authenticated, or the NameIdentifier claim is missing or not a valid Guid.

Fix: Check IsAuthenticated before accessing UserId:

if (user.IsAuthenticated)
{
Guid id = user.UserId; // Safe
}

Or use the nullable Id property instead:

string? id = user.Id; // Returns null instead of throwing

IUserContext properties are all null / IsAuthenticated is false​

Cause: The user is not authenticated. IUserContext reads from HttpContext.User, which is only populated when the request has been authenticated by middleware.

Fix: Ensure authentication middleware is registered and ordered correctly:

var app = builder.Build();

app.UseAuthentication(); // Must come before UseAuthorization
app.UseAuthorization();

app.MapEndpoints();

Also verify the endpoint requires authentication:

app.MapGet("/me", (IUserContext user) => ...).RequireAuthorization();

HasScope returns false even though the token has the scope​

Cause: Scope comparison is case-sensitive and uses the "scope" claim with space-separated values.

Fix: Verify the scope claim in your token:

// Debug: inspect the raw scope claim
var scopeClaim = user.GetClaimValue("scope");
// Should contain values like "api.read api.write openid profile"

// Check with exact case
bool hasScope = user.HasScope("api.read"); // ✅ correct
bool wrongCase = user.HasScope("API.READ"); // ❌ will be false

If your identity provider uses non-standard claim names (e.g., "scp" instead of "scope"), use HasClaim instead:

if (user.HasClaim("scp", "api.read"))
{
// ...
}

AddUserContext() is not recognized​

Cause: Missing using directive — or you're still calling the old name after upgrading.

Fix: Add the namespace and use the current method name:

using RA.Utilities.Authorization.Extensions;

builder.Services.AddUserContext();

Upgrading from v10.0.1 or earlier? AddAppUser() was renamed to AddUserContext() in v10.0.2. See the migration guide.

HasClaim gives a compilation error about too few arguments​

Cause: You're using the v10.0.0 signature HasClaim(string claimValue) after upgrading to v10.0.1 or later.

Fix: Add the claimType parameter:

// ❌ v10.0.0
user.HasClaim("CanDeleteUsers");

// ✅ v10.0.1+
user.HasClaim("permission", "CanDeleteUsers");

FAQ​

What's the difference between Id and UserId?​

PropertyTypeBehavior
Idstring?Returns the raw NameIdentifier claim value, or null if missing
UserIdGuidParses the NameIdentifier claim as a Guid. Throws InvalidOperationException if missing or invalid

Use UserId when you know the user is authenticated and your IDs are GUIDs. Use Id when you need the raw string or are handling the null case yourself.

Does IUserContext work with Minimal APIs?​

Yes. Inject it directly into endpoint handlers:

app.MapGet("/me", (IUserContext user) => new { user.UserId, user.Email });

Can I use IUserContext outside of a request context (e.g., background jobs)?​

No. IUserContext reads from IHttpContextAccessor.HttpContext.User, which is only available during an HTTP request. For background jobs, construct a ClaimsPrincipal manually or pass the relevant user information as parameters.

How do I add custom properties to IUserContext?​

Since IUserContext is an interface, the simplest approach is an extension method over it:

public static class UserContextExtensions
{
public static string? GetDepartment(this IUserContext user) =>
user.GetClaimValue("department");
}

For richer scenarios (e.g., typed value objects), define your own interface extending IUserContext and register your implementation alongside AddUserContext().

Is IUserContext thread-safe?​

Yes. It is registered as scoped — each request gets its own instance — and it holds a reference to a ClaimsPrincipal, which is immutable. Scoped instances are never shared across concurrent requests.

Does HasScope support the scp claim?​

No — HasScope specifically checks the "scope" claim. If your identity provider uses "scp" instead, use GetClaimValues("scp") and check manually, or use HasClaim:

bool hasScope = user.HasClaim("scp", "api.read");

Known Limitations​

  • GUID-only user IDs: UserId and GetUserId() parse only Guid. For integer or string IDs, use Id and convert manually.
  • Request-bound: IUserContext requires an active HttpContext. It won't work in background jobs, hosted services, or message handlers without a request context.
  • Case-sensitive scope matching: HasScope uses ordinal comparison. Most OIDC providers use lowercase scopes, but verify your provider's output.
  • No implicit authorization: IUserContext reads claims; it doesn't enforce authorization. Use [Authorize] attributes or RequireAuthorization() to protect endpoints.
  • No claim transformation: If you use IClaimsTransformation, the transformed claims are available through IUserContext since it reads HttpContext.User after transformation.