Add IdentityUser to HttpContext.User - c#

I am using .NET Core 3.0 . I am trying to authenticate a user via an API Key authorization. I have created a middleware that is called every time a controller endpoint is called. I can authenticate the user and get the claims but I cannot set the HttpContext.User object to use this identity.
public async System.Threading.Tasks.Task Invoke(HttpContext context, Services.AuthenticationService authenticationService, UserManager<IdentityUser> userManager, SignInManager<IdentityUser> _signInManager)
{
if (!context.Request.Path.ToString().Contains("Token") && context.Request.Path.StartsWithSegments(new PathString("/api")))
{
var userKey = context.Request.Headers["user-key"];
// Register the user and its claims to the session
var user = authenticationService.GetUserFromToken(userKey);
var claims = await userManager.GetClaimsAsync(user);
if (claims == null)
{
context.Response.StatusCode = 401; //UnAuthorized
await context.Response.WriteAsync("Claims not found for user");
return;
}
var claimsIdentity = new ClaimsIdentity(IdentityConstants.ApplicationScheme);
claimsIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
claimsIdentity.AddClaims(claims);
context.User.AddIdentity(claimsIdentity);
await _signInManager.Context.SignInAsync(IdentityConstants.ApplicationScheme, new ClaimsPrincipal(claimsIdentity));
}
await _next.Invoke(context);
}
When I check HttpContext.User in my controller the main identity object has no information. In the Identities object there are two identies. The second identity has the the claim and user name that I set in the middleware.
How do I set the main identity to be the one and only identity? The _signInManager code doesn't actually do anything and I get the same results in the HttpContext.User object with or without it.

Related

.NET Core 2.0 Identity AND jwt?

I've been looking around and trying to do more research on .NET Core Identity (https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.1&tabs=visual-studio%2Caspnetcore2x) and Jwt (json web tokens). I've been rolling with the default Identity as authentication/authorization in my .NET Core 2.0 app and it has been working well so far.
I'm running into a roadblock and I think it's the way of my understanding of .NET Core identity and jwt. My application has MVC and an web api. I would ideally like to secure the web api, but I hear the best way to do that now is through jwt. Good - cool.
I can go ahead and configure jwt and then use it as my authentication/authorization (https://blogs.msdn.microsoft.com/webdev/2017/04/06/jwt-validation-and-authorization-in-asp-net-core/), but - do I need to go ahead and spin up a new server to serve as the authorization server for jwt? If so, I'm not going to do that (too expensive).
What about my .NET Core identity code if I do go with jwt? Does that have to go away then? If it can co-exist, how might I authorize my MVC pages with Identity and my api endpoints with jwt?
I realize this is an open-ended question, but the core of it is:
Can .NET Core Identity and JWT co-exist? Or do I have to choose one or the other? I have MVC and an web api and would like to secure both.
Yes, you can.
The logic process is in this method:
Step 1: GetUserClaims
var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
Into GetClaimsIdentity you will
private async Task<ClaimsIdentity> GetClaimsIdentity(string userName, string password)
{
if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))
return await Task.FromResult<ClaimsIdentity>(null);
var userToVerify = await _userManager.FindByNameAsync(userName);
if (userToVerify == null) {
userToVerify = await _userManager.FindByEmailAsync(userName);
if (userToVerify == null) {
return await Task.FromResult<ClaimsIdentity>(null);
}
}
// check the credentials
if (await _userManager.CheckPasswordAsync(userToVerify, password))
{
_claims = await _userManager.GetClaimsAsync(userToVerify);
return await Task.FromResult(_jwtFactory.GenerateClaimsIdentity(userToVerify.UserName, userToVerify.Id, _claims));
}
// Credentials are invalid, or account doesn't exist
return await Task.FromResult<ClaimsIdentity>(null);
}
Step 2: Group all user claims you need add to the token - Use System.Security.Claims
public ClaimsIdentity GenerateClaimsIdentity(string userName, string id, IList<Claim> claims)
{
claims.Add(new Claim(Helpers.Constants.Strings.JwtClaimIdentifiers.Id, id));
// If your security is role based you can get then with the RoleManager and add then here as claims
// Ask here for all claims your app need to validate later
return new ClaimsIdentity(new GenericIdentity(userName, "Token"), claims);
}
Step 3: Then back on your method you have to generate and return the JWT Token
jwt = await jwtFactory.GenerateEncodedToken(userName, identity);
return new OkObjectResult(jwt);
To generate token do something like this:
public async Task<string> GenerateEncodedToken(string userName, ClaimsIdentity identity)
{
List<Claim> claims = new List<Claim>();
//Config claims
claims.Add(new Claim(JwtRegisteredClaimNames.Sub, userName));
claims.Add(new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()));
claims.Add(new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64));
//End Config claims
claims.AddRange(identity.FindAll(Helpers.Constants.Strings.JwtClaimIdentifiers.Roles));
claims.AddRange(identity.FindAll("EspecificClaimName"));
// Create the JWT security token and encode it.
var jwt = new JwtSecurityToken(
issuer: _jwtOptions.Issuer,
audience: _jwtOptions.Audience,
claims: claims,
notBefore: _jwtOptions.NotBefore,
expires: _jwtOptions.Expiration,
signingCredentials: _jwtOptions.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
return encodedJwt;
}
There are many ways to do this.
The most common is:
Validate Identity User --> Get User identifiers --> Generate and Return Token Based on Identifiers --> Use Authorization for endpoints
Hope this help
You can validate the username and password and generate the Jwt.
First, make sure your API has the following default identity set up in the startup.cs:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Secondly, you can validate the login with something like this:
You can set up an API controller something like this:
[ApiController, Route("check")]
public class TokenController : ControllerBase
{
private readonly SignInManager<IdentityUser> signin;
public TokenController(SignInManager<IdentityUser> signin)
{
this.signin = signin;
}
[HttpGet]
public async Task<string> Get(string user, string pass)
{
var result = await signin.PasswordSignInAsync(user, pass, true, false);
if (result.Succeeded)
{
string token = "";
return token;
}
return null;
}
}
Within your get function, you can now generate your Jwt.

How to get UserID from ASP.NET Identity when sending Rest Request

I have an API which uses ASP.NET Identity, it is fairly easy for me to get the UserId once the token has been generated as following
HttpContext.Current.User.Identity.GetUserId().ToString())
Now I have an external application which is trying to authenticate using this API but I need the UserId of the user who generated the token
When I send a request to http://myapiURL/token I get the following
access_token
token_type
expires_in
userName
issued
expires
And when I send a request to get API/Account/UserInfo using the generated token I get the following
Email
HasRegistered
LoginProvider
Question How do I get UserId?
I have two options,
A. I modify UserInfoViewModel GetUserInfo() to have UserId in UserInfoViewModel?
B. I create a new method in ApiController such as GetUserId (API/Account/GetUserId) which runs HttpContext.Current.User.Identity.GetUserId().ToString()) and sends back the
UserId
Is there any other way?
Cheers
I believe you want UserId in the response of /Token.
By default Identity does not add UserId in response.
so you need to add it manually in ApplicationOAuthProvider.cs in method GrantResourceOwnerCredentials
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
ticket.Properties.Dictionary.Add("UserId", user.Id);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}

OAuthAuthorizationServerProvider: validate client bearer token

I was able to generate a token by validating the incoming username and password.
In startup.cs I have this
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(100),
Provider = new MYAuthorizationServerProvider(),
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new OAuthBearerAuthenticationProvider()
});
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
}
}
In MyAuthorizationsServiceProvider I have
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
var userServices = new UserService();
var user = await userServices.ValidateUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "Provided username and password is incorrect");
return;
}
else
{
identity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
identity.AddClaim(new Claim("username", user.UserName));
identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
context.Validated(identity);
}
}
This is all good until now. I have a controller which is accessible only by Admin role and it works fine for the token generated.
Now let's assume that I have stripped off the user role in the backend for that specific user or deactivated the user. Now the token should not work for that specific controller or invalidate the authentication as the user is deactivated. How does the Oauth know the back end change and how does it validate?
If someone could provide an answer with some example that would be really helpful.
I also have public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) but for some reason this does not fire up.
How does the Oauth know the back end change and how does it validate?
It will only verify the username and password against the backend when the user signs in. After that the principal and claims are set from the token that the client passes along with the request.
One option is to create a custom authorized filter which validates the user against the backend in every request but that is not recommended as that would be very costly in request time.
A better option would be to set the valid time on the token to a lower number than 100 days AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), and add an RefreshTokenProvider to the OAuthAuthorizationServer. Then in that provider revalidate the user against the backend. You could read here about how to implement a refresh provider

How to add claims during user registration

I'm using ASP.NET MVC 5 project with identity 2.1.0 and VS2013 U4. I want to add claims to user during registration in order to be stored in db. These claims represent user custom properties.
As I created a web page for administrator to create/edit/delete users, I'm still using create method from AccountController to create a user, but I don't want to login that user. How can I add those claims to the user ?
You probably already have a UserManager class. You can use that one to create users and to add claims.
As an example in a controller:
// gather some context stuff
var context = this.Request.GetContext();
// gather the user manager
var usermanager = context.Get<ApplicationUserManager>();
// add a country claim (given you have the userId)
usermanager.AddClaim("userid", new Claim(ClaimTypes.Country, "Germany"));
In order for this to work you need to implement your own UserManager and link it with the OWIN context (in the example it's ApplicationUserManager which basically is class ApplicationUserManager : UserManager<ApplicationUser> { } with only a small amount of configuration added). A bit of reading is available here: https://msdn.microsoft.com/en-us/library/dn613290%28v=vs.108%29.aspx
you can use Like
private void SignInAsync(User User)
{
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, User.Employee.Name));
claims.Add(new Claim(ClaimTypes.Email, User.Employee.EmailId));
claims.Add(new Claim(ClaimTypes.Role, User.RoleId.ToString()));
var id = new ClaimsIdentity(claims,
DefaultAuthenticationTypes.ApplicationCookie);
var claimsPrincipal = new ClaimsPrincipal(id);
// Set current principal
Thread.CurrentPrincipal = claimsPrincipal;
var ctx = Request.GetOwinContext();
var authenticationManager = ctx.Authentication;
authenticationManager.SignIn(id);
}
after login pass the User table value in this function
SignInAsync(result);
you can get clam value like
var identity = (ClaimsPrincipal)Thread.CurrentPrincipal;
// Get the claims values
string UserRoleValue = identity.Claims.Where(c => c.Type == ClaimTypes.Role)
.Select(c => c.Value).SingleOrDefault();
You can, in fact, create claims at the same time you create the user account.
Just add the claims to the user object before you call CreateAsync on the user manager.
var identityUser = new IdentityUser
{
UserName = username,
Email = email,
// etc...
Claims = { new IdentityUserClaim { ClaimType = "SomeClaimType", ClaimValue = "SomeClaimValue"} }
};
var identityResult = await _userManager.CreateAsync(identityUser, password);
This will create the user and associate the claims with the user as one logical operation with persistence.

WebApi 2 Authentication

I am a little confused with WebApi and MVC.
I created a blank WebApi project and selected Individual User Accounts as the authentication method.
This generated the AccountController : ApiController class. In here there are methods for Registering, Getting user info, etc, but there is no method for logging in.
How is the MVC user supposed to log in?
Cheers,
/r3plica
In default Web Api template is using OWIN middleware to authenticate user.
In Startup.Auth.cs you can find configuration info about urls for authentication.
static Startup()
{
PublicClientId = "self";
UserManagerFactory = () => new UserManager<IdentityUser>(new UserStore<IdentityUser>());
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token") - this url for get token for user,
Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
}
After you send request to TokenEndPointPath with user name and password in parameters, OWIN middleware call method GrantResourceOwnerCredentials which implemented in ApplicationOAuthProvider in default template with user account.In this method you can check user name and password and grant access to user.
You can find default implementation of this method below.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
using (UserManager<IdentityUser> userManager = _userManagerFactory())
{
IdentityUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await userManager.CreateIdentityAsync(user,
context.Options.AuthenticationType);
ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
}

Categories