User Principal in Entity Framework Core DbContext

asp.net-core asp.net-mvc entity-framework entity-framework-core

Question

I am upgrading a project from .NET 4.6 to .NET Core. It is an ASP.NET MVC website with a WebAPI that uses EntityFramework. When the a (MVC or WebAPI) Controller fires up the DbContext, there is code that needs to identity the user as a ClaimsIdentity to inspect their claims. In previous .NET, this was most reliably available on Thread.CurrentPrincipal like this:

ClaimsIdentity identity = System.Threading.Thread.CurrentPrincipal.Identity as ClaimsIdentity;

IIRC, this was the safest way to do it since you could be coming from different contexts - WebAPI or ASP.NET MVC.

In the .NET core solution, I have tried to Dependency Inject an IHttpContextAccessor into the constructor, but the User on HttpContext is not authorized and has no claims

ClaimsIdentity identity = httpContext.HttpContext.User.Identity;
// identity.IsAuthenticated == false. identity.Claims is empty.

Security is wired up in Startup.cs:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).
                AddCookie(options =>
                {
                    options.LoginPath = "/Login";
                    options.Cookie.HttpOnly = true;
                }).
                AddJwtBearer(options =>
                {
                    options.RequireHttpsMetadata = false;
                    options.SaveToken = true;

                    var key = Configuration["Tokens:Key"];

                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidIssuer = Configuration["Tokens:Issuer"],
                        ValidAudience = Configuration["Tokens:Issuer"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key))
                    };
                });

The user logins on a /Login MVC view page, which logs in via Cookies and also generates a Bearer token in another request that is saved on the client. After all this the user is redirected to the homepage.

Cookie Login:

    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties() { IsPersistent = bIsPersistent });

Token Generation (called from ajax, saved to localstorage before redirection)

var secretKey = Configuration["Tokens:Key"];
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
var creds = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256 );
var expires = DateTime.Now.AddHours(8);
var token = new JwtSecurityToken(
    _config["Tokens:Issuer"],
    _config["Tokens:Issuer"],
    oAuthIdentity.Claims,
    expires: expires,
    signingCredentials: creds
    );

ret = Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });

After landing on the homepage, an ajax call is made to the WebApi with the bearer token (I pulled the bearer token out of the http request and verified the signature on jwt.io), and the webapi causes the DbContext to be instantiated, and this is where the identity is not valid.

It's as if the Identity is not properly marshalled over to the DbContext -

How to I get the correct User or Identity in the DbContext?

Additionally, at the point I need it is in the DbContext construction, which I don't have alot of control over with the Dependency Injection. But I need to get this info basically from a default constructor or lazy load it somehow.

1
0
11/2/2018 7:09:20 PM

Popular Answer

With your setup, you have two authentications setup. So, in your ConfigureServices function in Startup class, you need to use something like the following:

services.AddAuthentication().AddCookie().AddJwtBearer();

Don't forget to specify a default authentication. For instance, if you want the authentication to be cookies by default, you can use this:

services.AddAuthentication("Cookies").AddCookie().AddJwtBearer();

Or to keep the code safer,

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie().AddJwtBearer();

In your startup class, in the Configure function, don't forget to add

app.UseAuthentication();

When authenticating within a controller, you will need to use the scheme name along with the [Authorize] if you are not using the default scheme.

[Authorize(AuthenticationSchemes = "")]
0
11/2/2018 7:08:22 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow