ASP.net mvc, User and ClaimsIdentity lost after RedirectToRouteResult on HandleUnauthorizedRequest - asp.net-mvc-4

as the title said, after i handled HandleUnauthorizedRequest on my custom AuthorizeAttribute and redirect to a page, it seems like the User and ClaimsIdentity is lost on the recieving page even though i set it before in HttpContext.User.
public override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
{
// just for testing.
string username = GetCookies("username", httpContext);
string role = GetCookies("role", httpContext);
var identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.NameIdentifier, username)
}, "keycloak_auth");
identity.AddClaim(new Claim(ClaimTypes.Role, role));
httpContext.User = new ClaimsPrincipal(identity);
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "action", "Contact" },
{ "controller", "Home" }
});
}
}
On the receiving end on the Razor's page, i just simply display the name and it seems blank
#{
ViewBag.Title = "Contact";
}
<h2>#ViewBag.Title.</h2>
<h3>#ViewBag.Message</h3>
<h3>#User.Identity.Name</h3>
However, User.Identity.Name has become empty, what else am I missing here and where and what should i check for this issue?
Edit: I edited the sample code for simpler reproduction, i just created a simple attribute with forced values and redirect to the contract page from the ASP.Net default project files. The result is same, the identity (User.Identity) is lost including IsAuthenticated, Names, etc.

Related

AspNetCore alternative for System.Web.Security.FormsAuthenticationTicket

We're using System.Web.Security.FormsAuthenticationTicket to create anonymous cookies for users that aren't logged in. Is there an equivalent in AspNetCore?
I'm well aware that ASP.NET Core cannot support forms authentication. The new way of doing things is cookies. So how to create a cookie that does the equivalent in the new situation?
Asp.net core cannot support form authentication. I recommend you use cookie-base authentication. This link can help you build it.
If you want to skip a method that requires authorized access. You can add attribute [AllowAnonymous].
[AllowAnonymous]
public IActionResult Privacy()
{
return View();
}
Or you can refer to this link.
Configure cookie in Startup.cs.
services.AddAuthentication("auth")
.AddCookie("auth",config=>
{
config.Cookie.Name = "cookie.name";
config.LoginPath = "/home/login";
});
Generate token in this action. You can fill the claim by receiving form data.
[HttpPost]
public IActionResult login()
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name,"myName"),
new Claim(ClaimTypes.Role,"myRole")
};
var claimIdentity = new ClaimsIdentity(claims,"id card");
var claimPrinciple = new ClaimsPrincipal(claimIdentity);
var authenticationProperty = new AuthenticationProperties
{
IsPersistent = true
};
HttpContext.SignInAsync(claimPrinciple,authenticationProperty);
return View();
}

What claim in ClaimsPrincipal should I fill to make Roles Authorization work in .Net Core 2.0?

I have this method to sign in a user using .Net Core Cookie Authentication.
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login(LoginVM loginModel)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, loginModel.UserName)
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
// TODO: Validate against DB Staff/User.
// If valid, sign in.
await HttpContext.SignInAsync(new ClaimsPrincipal(claimsIdentity));
return Redirect(loginModel?.ReturnUrl ?? "/");
// Else return View();
}
I am filling up ClaimTypes.Name with the user name being posted up to the login View Model.
Is there like a ClaimTypes.Roles value to fill up?
I need to be able to use "User.IsInRole(...)".
This would be a collection of roles of course for a user. Not just one role.
Does anyone know how to do this?
Add like this:
claims.Add(new Claim(ClaimTypes.Role, p.Role.RoleCd));

Authorization .net core 2.0

I have setup cookie authentication in my asp.net core application. I have a login page where the credentials matchup against the active directory. All this works fine.
Now next I want to implement authorization in my application. I have a table of users together with permission against them. For example permission like Reading & Write. When the user is successfully authenticated I want to check for these permissions and show them certain functionality while restricting others. For example, show certain dropdowns for write permission while hiding for reading permission.
What is the best approach to handle this in the .NET Core.
I have read about adding policy like:
services.AddAuthorization(options => {
options.AddPolicy("Read", policy =>
policy.RequireClaim("Read", "MyCLaim"));
});
Then in my controller:
[Authorize(Policy = "Read")]
public class HomeController : Controller
{
}
Where do I get the permissions for logged in user from my database and how to verify if the user has those permissions or not.
Would appreciate inputs.
Where do I get the permissions for logged in user from my database and
how to verify if the user has those permissons or not.
Right after a user is authenticated, you collect user's claims, and store them in Authentication Cookie.
For example, SignInAsync method.
public async Task SignInAsync(User user, IList<string> roleNames)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Sid, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName)
};
foreach (string roleName in roleNames)
{
claims.Add(new Claim(ClaimTypes.Role, roleName));
}
var identity = new ClaimsIdentity(claims, "local", "name", "role");
var principal = new ClaimsPrincipal(identity);
await _httpContextAccessor.HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme, principal);
}
FYI: It happens to be that I store them as Role claims. You do not have to follow that route, if you don't want.
You can then verify the policy inside Startup.cs.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
// Set up policies from claims
// https://leastprivilege.com/2016/08/21/why-does-my-authorize-attribute-not-work/
services.AddAuthorization(options =>
{
options.AddPolicy(Constants.RoleNames.Administrator, policyBuilder =>
{
policyBuilder.RequireAuthenticatedUser()
.RequireAssertion(context => context.User.HasClaim(
ClaimTypes.Role, Constants.RoleNames.Administrator))
.Build();
});
});
...
}
}
Usage is same as what you have described.
[Authorize(Policy = "Read")]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}

Custom IsInRole() when using Cookie Middleware without ASP.NET Identity

To maintain compatibility with existing applications I was planing on using Cookie Middleware without ASP.NET Identity, as described in the documentation:
https://docs.asp.net/en/latest/security/authentication/cookie.html
This seems to work as expected as far as logging a user in, but I'm having issues with roles -- specifically when using the [Authorize(Roles = "ADMIN")].
In the code below, I can call p.IsInRole("ADMIN") and my implementation of MyClaimsPrincipal.IsInRole() is called and returns true.
What doesn't work is the [Authorize(Roles = "ADMIN")] attribute because it ends up calling ClaimsPrincipal.IsInRole (which returns False) instead of MyClaimsPrincipal.IsInRole() (which returns True).
[Authorize(Roles = "ADMIN")]
public class MyAdminController : Controller
{
public IActionResult Index()
{
var p = new MyClaimsPrincipal(ClaimsPrincipal.Current);
bool isAdmin = p.IsInRole("ADMIN");
return View();
}
}
When not using Identity and only using Cookie Middleware, can I use the [Authorize(Roles = "ADMIN")] attribute?
How? :-)
If I had to guess, I'm not implementing p.IsInRole() correctly -- currently this method loads the roles, then returns a True/False. Perhaps I have to 'load' my roles elsewhere in such a way that the ClaimsPrincipal.IsInRole is sufficient. If I was using Identity(), I assume this would be an implementation of IUserRoleStore.
My other 'if i had to guess' answer is that somewhere in startup.cs I need to replace the current ClaimsPrincipal with an instance of MyClaimsPrincipal.
Thank you!
You should add role claims when cookie is created.
In startup.cs:
app.UseCookieAuthentication(options =>
{
options.AuthenticationScheme = "MyCookieMiddlewareInstance";
options.LoginPath = new PathString("/Account/Login/");
options.AccessDeniedPath = new PathString("/Account/Forbidden/");
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
});
And login post method may be something like this(i assume that you have a custom login page):
[HttpPost]
public IActionResult Login(string userName, string password, string returnUrl)
{
var user = _userService.GetUser(userName, password);// i assume that _userService is injected
if (user == null)
{
//return Error;
}
var claims = new List<Claim>()
{
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim(ClaimTypes.Name, user.GetFullName() ),
};
var identity = new ClaimsIdentity(claims, "Forms");
identity.AddClaim(new Claim(ClaimTypes.Role, "ADMIN"));
var principal = new ClaimsPrincipal(identity);
HttpContext.Authentication.SignInAsync("MyCookieMiddlewareInstance", principal);
return Redirect(returnUrl);
}

How to get custom claims value in Asp.net Web Api 2 - Owin - ApiController.

I create some Claims with this code, I am getting the values when I log in the app. I want to use the claim value of "Id" in some of my api controllers,
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{ "UserName", customer.FirstName }, { "Id", customer.Id.ToString() }
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
So in the controller I have this code:
[Authorize]
[Route("api/banners/{customerId")]
[HttpGet]
public HttpResponseMessage GetBanners(int customerId)
{
return Request.CreateResponse<IEnumerable<Banner>>(HttpStatusCode.OK, AppDataAccess.GetBanners(customerId)));
}
And I would like to change the code to this one:
[Authorize]
[Route("api/banners")]
[HttpGet]
public HttpResponseMessage GetBanners()
{
int CustomerId = SOME FUNCTION THAT GETS THE CLAIM VALUE I WANT (ID)
return Request.CreateResponse<IEnumerable<Banner>>(HttpStatusCode.OK, AppDataAccess.GetBanners(customerId)));
}
I have been researching but I just found the AuthorizationFilterAttribute but it just works using it on the route, Is there any way that you know I could build that function so I just call it in the controller??
I am using Microsoft.AspNet.Identity.Owin 2.0.0
Thanks for the help
Save your custom claims like this:
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim("Id", user.Id.ToString()));
And get like this:
((ClaimsIdentity)User.Identity).Claims.FirstOrDefault(x => x.Type == "Id").Value