3

I have a .net core application that I have upgraded from 1.1 to 2.0. The problem I am having is working out how to set up both authentication and authorization.

I am getting this exception when I am trying to hit an api endpoint...

2017-08-15 15:28:12.2191|13|Microsoft.AspNetCore.Server.Kestrel|ERROR| Connection id "0HL73T7CAJGBE", Request id "0HL73T7CAJGBE:00000001": An unhandled exception was thrown by the application. No authenticationScheme was specified, and there was no DefaultChallengeScheme found.

My controller has this attribute on it...

[Authorize(Policy = "Viewer3AuthPolicy")]

My startup.cs has this method to try and set everything up...

   public void ConfigureServices(IServiceCollection services)
    {
    SetCorsPolicy(services);

    services.AddMvc();

    services.AddAuthentication(o =>
    {
        o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    });

    SetAuthorisationPolicy(services);

    services.AddAuthorization(options =>
    {
        options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build();
        options.AddPolicy("Viewer3AuthPolicy",
            policy => policy.RequireClaim(Constants.AuthPolicyClaimsName, Constants.AuthPolicyClaimsValue));
    });
}

In my Configuremethod I am calling...

app.UseAuthentication();

I am thinking that I must have some ordering wrong or am making the wrong calls in the setup.

Does anyone have any ideas?

2
  • Authentication in asp.net core 2.0 has changed, i hope you find the answer Commented Aug 16, 2017 at 12:31
  • I think this is you need : wildermuth.com/2017/08/19/… core 2.0 has big difference with core 2.0 preview. here is implantation with angular as front end: github.com/Longfld/ASPNETcoreAngularJWT Commented Aug 22, 2017 at 1:05

2 Answers 2

2

Solution with _userManager.AddClaimsAsync. Here is the simplified version of changes I made under ConfigureServices:

services.AddAuthorization(options => {       
    options.AddPolicy("CRM", policy => { policy.RequireClaim("department", "Sales", "Customer Service", "Marketing", "Advertising", "MIS"); });
});

AccountController constructor:

    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly IEmailSender _emailSender;
    private readonly ILogger _logger;

    private readonly MyDB_Context _context;

    public AccountController(
        MyDB_Context context,
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager,
        IEmailSender emailSender,
        ILogger<AccountController> logger)
    {
        _context = context;
        _userManager = userManager;
        _signInManager = signInManager;
        _emailSender = emailSender;
        _logger = logger;
    }

Under LogIn: (var vUser is my own class with properties Name, department, SingIn, etc...). The sample below uses combination of custom user table mytable (to read from claim types and their values) and AspNetUserClaims table (to add claims):

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
    ViewData["ReturnUrl"] = returnUrl;
    if (ModelState.IsValid) {
        var vUser = _context.mytable.SingleOrDefault(m => m.Email.ToUpper() == model.Email.ToUpper());

        const string Issuer = "https://www.mycompany.com/";
        var user = _userManager.Users.Where(u => u.Email == model.Email).FirstOrDefault();

        ApplicationUser applicationUser = await _userManager.FindByNameAsync(user.UserName);
        IList<Claim> allClaims = await _userManager.GetClaimsAsync(applicationUser); // get all the user claims

        // Add claim if missing
        if (allClaims.Where(c => c.Type == "department" && c.Value == vUser.department).ToList().Count == 0) {
            await _userManager.AddClaimAsync(user, new Claim("department", vUser.department, ClaimValueTypes.String, Issuer));
        }
        // Remove all other claim values for "department" type
        var dept = allClaims.Where(c => c.Type == "department" && c.Value != vUser.department);
        foreach(var claim in dept) {
            await _userManager.RemoveClaimAsync(user, new Claim("department", claim.Value, ClaimValueTypes.String, Issuer));
        }

        vUser.SignIn = DateTime.Now; _context.Update(vUser); await _context.SaveChangesAsync();

        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(vUser.Name, model.Password, model.RememberMe, lockoutOnFailure: false);
        //var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded) {
            _logger.LogInformation("User logged in.");
            return RedirectToLocal(returnUrl);
        }
        if (result.RequiresTwoFactor) {
            return RedirectToAction(nameof(LoginWith2fa), new { returnUrl, model.RememberMe });
        }
        if (result.IsLockedOut) {
            _logger.LogWarning("User account locked out.");
            return RedirectToAction(nameof(Lockout));
        } else {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return View(model);
        }
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

This is what I have in my controller:

[Authorize(Policy = "CRM")]
public class CRMController : Controller
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for this. I will try it shortly and let you know what happens.
Is _userManager your own class or a .net type?
it's .net type, generated by choosing Authentication "Individual User Accounts". I've updated the answer with my Controller constructor.
I'm using combination of AspNetUsers table (generated by application) and my custom table with all the claims in MyDB_Context.
1

Adding

AddJwtBearer(options => { options.RequireHttpsMetadata = false; ... });"

did the trick for me.

[dotnet core 2.0]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.