Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User.HasClaim does not successfully read claim properties in .NET Core 2.0 Web Api

I am having an issue when using the Authorize attribute with a Policy that I'm defining in Startup.cs. I edited my controller to manually check claims. I can see the claims including a scope claim with the correct scopes but when I manually check for that claim/scope it comes back as false. I'm using Azure AD B2C as my identity server and successfully get a validated token.

Here is code from my Startup.cs:

    services.AddAuthorization(options =>
    {
        var policyRead = new AuthorizationPolicyBuilder()
            .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
            .RequireAuthenticatedUser()
            .RequireClaim("http://schemas.microsoft.com/identity/claims/scope", "vendor.read")
            .Build();
        options.AddPolicy("VendorRead", policyRead);

        var policyWrite = new AuthorizationPolicyBuilder()
            .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
            .RequireAuthenticatedUser()
            .RequireClaim("http://schemas.microsoft.com/identity/claims/scope", "vendor.write")
            .Build();
        options.AddPolicy("VendorWrite", policyWrite);
    });

    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    }) 
        .AddJwtBearer(jwtOptions =>
        {
            jwtOptions.Authority = $"{Configuration["AzureAdB2C:Instance"]}/{Configuration["AzureAdB2C:TenantId"]}/{Configuration["AzureAdB2C:SignUpSignInPolicyId"]}/v2.0/";
            jwtOptions.Audience = Configuration["AzureAdB2C:ClientId"];
            jwtOptions.RequireHttpsMetadata = true;
            jwtOptions.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = $"{Configuration["AzureAdB2C:Instance"]}/{Configuration["AzureAdB2C:TenantId"]}/v2.0/",
                ValidAudience = Configuration["AzureAdB2C:ClientId"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["AzureAdB2C:ClientSecret"]))
            };
            jwtOptions.Events = new JwtBearerEvents
            {
                OnAuthenticationFailed = AuthenticationFailed,
                OnTokenValidated = TokenValidated
            };
        });

Here is my controller code where I'm manually checking for claims:

// GET: api/Vendor/5
[HttpGet("{id}")]
public async Task<IActionResult> Get(VendorRequest request)
{
    var hasClaim1 = User.HasClaim(c => c.Type == "vendor.read");
    var hasClaim2 = User.HasClaim(c => c.Type == "scope");
    var hasClaim3 = User.HasClaim(c => c.Type == "scp");
    var hasClaim4 = User.HasClaim(c => c.Type == "http://schemas.microsoft.com/identity/claims/scope");
    var hasClaim5 = User.HasClaim("http://schemas.microsoft.com/identity/claims/scope", "vendor.read");
    var hasClaim7= User.HasClaim("http://schemas.microsoft.com/identity/claims/scope", "vendor.write");
    var allowed = await _authorization.AuthorizeAsync(User, "VendorRead");
    if (!allowed.Succeeded)
    {
        return StatusCode(StatusCodes.Status403Forbidden);
    }

The only hasClaim that comes back as true is hasClaim4.

Here is what my claims look like: enter image description here

Any ideas on what I'm doing wrong? I am only trying to get the vendor.read scope to work for now.

like image 558
stumpykilo Avatar asked Apr 24 '18 17:04

stumpykilo


People also ask

How do I apply a claim in .NET core?

Adding claims checks Claim based authorization checks are declarative - the developer embeds them within their code, against a controller or an action within a controller, specifying claims which the current user must possess, and optionally the value the claim must hold to access the requested resource.

How would you implement claims based authentication in .NET core?

The claims-based authorization works by checking if the user has a claim to access an URL. In ASP.NET Core we create policies to implement the Claims-Based Authorization. The policy defines what claims that user must process to satisfy the policy. We apply the policy on the Controller, action method, razor page, etc.

What is claim in .NET core?

Claims can be created from any user or identity data which can be issued using a trusted identity provider or ASP.NET Core identity. A claim is a name value pair that represents what the subject is, not what the subject can do.


1 Answers

The scope claim is a space delimited list so the RequireClaim() helper will not work in this case but the more generic RequireAssertion() will.

Example Scope Claim

"scp": "demo.read demo.write user_impersonation Test-Value"

Sample RequireAssertion()

services.AddAuthorization(options =>
{
    options.AddPolicy("ScopeCheck", policyBuilder => 
        policyBuilder.RequireAssertion(async handler =>
        {
            var scopeClaim = handler.User.FindFirst("http://schemas.microsoft.com/identity/claims/scope");
            var scopes = scopeClaim?.Value.Split(' ');
            var hasScope = scopes?.Where(scope => scope == "demo.write").Any() ?? false;
            return hasScope;
        }));
});

Sample Controller

[Authorize("ScopeCheck")]
public class SecureController : Controller
{
    [HttpGet]
    public IActionResult Test()
    {
        return Ok(new { Message = "You are allowed" });
    }
}

Full Sample Project - Sample token


Access Token Scope (RFC 6749 section-3.3)

The value of the scope parameter is expressed as a list of space- delimited, case-sensitive strings. The strings are defined by the authorization server. If the value contains multiple space-delimited strings, their order does not matter, and each string adds an additional access range to the requested scope

like image 168
spottedmahn Avatar answered Nov 14 '22 23:11

spottedmahn