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

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

Related

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

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.

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();
}

ASP.NET Core Windows Authentication and Application Roles

I'm trying to create a fairly simple intranet application that will use Active Directory for authentication, and will use the AspNetRoles table to check if the user is in a certain application role. This app is just an in-house lottery where some users can create events/contests that other users can then submit an entry to the contest. I'm thinking of starting out with 2 basic roles:
Administrator - Can perform CRUD operations on "Event" or
"Contest" entities
Contestant - Can perform GET operations on
"Contest" entities, and can create new "Entry" entities.
Here's where I'm stuck: I've got Windows Authentication working in the sense that from a controller, I can do a User.Identity.Name and see my domain login name. Furthermore, I can verify that an account belongs to a domain group by doing User.IsInRole("Domain Users"). If I want to avoid creating new AD groups for each role in my application (let's say design changes down the road require additional roles), how can I use Authorization on controllers to check against Application Roles?
Here's an example controller I want to use:
[Route("api/[controller]")]
[Authorize(Roles = "Contestant")]
public class EventTypesController : Controller
{
private IRaffleRepository _repository;
private ILogger<EventTypesController> _logger;
public EventTypesController(IRaffleRepository repository, ILogger<EventTypesController> logger)
{
_repository = repository;
_logger = logger;
}
[HttpGet("")]
public IActionResult Get()
{
try
{
var results = _repository.GetAllEventTypes();
return Ok(Mapper.Map<IEnumerable<EventTypeViewModel>>(results));
}
catch (Exception ex)
{
_logger.LogError($"Failed to get all event types: {ex}");
return BadRequest("Error occurred");
}
}
}
In my Startup.cs, in ConfigureServices, I'm wiring up Identity as follows:
services.AddIdentity<RaffleUser, ApplicationRole>()
.AddEntityFrameworkStores<RaffleContext>();
My RaffleUser class is really just the default implementation of IdentityUser:
public class RaffleUser : IdentityUser
{
}
My ApplicationRole class is also just the default implementation of IdentityRole. I also tried seeding some data in a seed class:
if (!await _roleManager.RoleExistsAsync("Administrator"))
{
var adminRole = new ApplicationRole()
{
Name = "Administrator"
};
await _roleManager.CreateAsync(adminRole);
await _context.SaveChangesAsync();
}
if (await _userManager.FindByNameAsync("jmoor") == null)
{
using (var context = new PrincipalContext(ContextType.Domain))
{
var principal = UserPrincipal.FindByIdentity(context, "DOMAIN\\jmoor");
if (principal != null)
{
var user = new RaffleUser()
{
Email = principal.EmailAddress,
UserName = principal.SamAccountName
};
await _userManager.CreateAsync(user);
await _context.SaveChangesAsync();
var adminRole = await _roleManager.FindByNameAsync("Administrator");
if (adminRole != null)
{
await _userManager.AddToRoleAsync(user, adminRole.Name);
await _context.SaveChangesAsync();
}
}
}
}
The data makes it to the tables, but it just seems like at the controller level, I need to convert the authenticated user to an IdentityUser. Do I need some middleware class to do this for me? Would that be the best way to make authorization reusable on all controllers?
First, I ended up creating a custom ClaimsTransformer that returns a ClaimsPrincipal populated with UserClaims and RoleClaims (after refactoring my app, I decided to go with policy-based authorization, and the access claim can be added at either the role or user level):
public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
{
var identity = (ClaimsIdentity)context.Principal.Identity;
var userName = identity.Name;
if (userName != null)
{
var user = await _userManager.FindByLoginAsync("ActiveDirectory", userName);
if (user != null)
{
identity.AddClaims(await _userManager.GetClaimsAsync(user));
var roles = await _userManager.GetRolesAsync(user);
identity.AddClaims(await GetRoleClaims(roles));
}
}
return context.Principal;
}
private async Task<List<Claim>> GetRoleClaims(IList<string> roles)
{
List<Claim> allRoleClaims = new List<Claim>();
foreach (var role in roles)
{
var rmRole = await _roleManager.FindByNameAsync(role);
var claimsToAdd = await _roleManager.GetClaimsAsync(rmRole);
allRoleClaims.AddRange(claimsToAdd);
}
return allRoleClaims;
}
I wired that up in the Startup.cs:
services.AddScoped<IClaimsTransformer, Services.ClaimsTransformer>();
I also went with Policy-based authorization:
services.AddAuthorization(options =>
{
options.AddPolicy("Administrator", policy => policy.RequireClaim("AccessLevel", "Administrator"));
options.AddPolicy("Project Manager", policy => policy.RequireClaim("AccessLevel", "Project Manager"));
});
So, users or roles can have a claim set with a name of "AccessLevel" and a value specified. To finish everything off, I also created a custom UserManager that just populates the User object with additional details from ActiveDirectory during a CreateAsync.
You need to add a DefaultChallangeScheme to use Windows authentication. This is how i do, but if someone has a better solution i am all ears :)
I use the following setup in my current application.
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<SecurityDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(options =>
{
options.DefaultChallengeScheme = IISDefaults.AuthenticationScheme;
});
Then i put in my application claims in a transformer.
services.AddTransient<IClaimsTransformation, ClaimsTransformer>();
I hope this will get you in the right direction.

Proper way to get current User ID in Entity Framework Core

There are a bunch of different answers floating around here for the different RC's of ASP.NET Core on how to get the ID of the currently logged in user. I wanted to ask the definite question here. Please note that project.json now has "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0"
With RC1, you could do something like this:
using Microsoft.AspNet.Identity;
using System.Security.Claims;
User.GetUserId();
But with the newly released version 1 of EF Core, Microsoft.AspNet.Identity is not the right version.
There was suggestions to use UserManager, which seems like a lot just to get the currently logged in user:
private Task<ApplicationUser> GetCurrentUserAsync() => _userManager.GetUserAsync(HttpContext.User);
var user = await GetCurrentUserAsync();
var userId = user?.Id;
Another method that I found was:
private readonly UserManager<ApplicationUser> _userManager;
_userManager.GetUserId(User)
So with ASP.NET Core 1 RTM and EF Core 1 with the following libraries in project.json, what is the proper way to get the id of the currently logged in user?
"Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0",
"Microsoft.AspNetCore.Mvc": "1.0.0",
If you are accessing this from withing the Controller, then using UserManager to get the user ID is pretty inefficient as you are making a round trip to the database. If you are using ClaimsIdentity, you can do something like this to get the user id:
var claimsIdentity = (ClaimsIdentity)this.User.Identity;
var claim = claimsIdentity.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
var userId = claim.Value;
This method just reads the user ID which is already present in the cookie, which in turn is automatically deserialized and stored in a ClaimsIdentity instance.
I use this helper class:
public static class UserHelpers
{
public static string GetUserId(this IPrincipal principal)
{
var claimsIdentity = (ClaimsIdentity)principal.Identity;
var claim = claimsIdentity.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
return claim.Value;
}
}
So getting a user ID becomes:
var userId = this.User.GetUserId();
If, for some reason, the required claim is not present in the Claims colleciton, you can easily add it when creating the user's ClaimsIdentity:
public class ApplicaionUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
userIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, this.UserId));
return userIdentity;
}
}
ASP.NET Core Identity is injected via DI in the startup.cs - as such you just have to inject UserManager via a constructor
UserManager<ApplicationUser> userManager
You can then use the following in methods
_userManager.GetUserId(User);
That's the way its used in the Sample Web Application when you create a new ASP.NET Core 1 project with Individual User Account.
The one-liner below is a more concise version of the other answers above.
var user = User.FindFirst(ClaimTypes.NameIdentifier).Value;
To explain a little further, I wanted to use the most basic form of authentication without any tables in the database so I chose this one -
Using Cookie Authentication without ASP.NET Core Identity from the Core documentation.
To get this working, the first step is to add the services in Startup.cs
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = new PathString("/Account/Login/");
options.LogoutPath = new PathString("/Account/Logoff/");
options.AccessDeniedPath = new PathString("/Account/AccessDenied/");
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
});
services.ConfigureApplicationCookie(identityOptionsCookies =>
{
// See https://andrewlock.net/automatically-validating-anti-forgery-tokens-in-asp-net-core-with-the-autovalidateantiforgerytokenattribute/
identityOptionsCookies.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
});
Then in the AccountController on the post back having entered a valid user id and password, the simplest Claims based authentication is to just add the login id as a Claim, e.g.
var claims = new List
{
new Claim(ClaimTypes.NameIdentifier, loginViewModel.Guid, ClaimValueTypes.String, issuer),
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(claimsIdentity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal,
new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMinutes(_cookieTimeoutInMinutes),
IsPersistent = true,
AllowRefresh = false
});
Once the Sign In completes you can retrieve the user id as described in the one liner above. See the answer from Milos Mrdovic above for the more detailed steps.
var user = User.FindFirst(ClaimTypes.NameIdentifier).Value;
See Claims-Based Authorization for further information.
You can get UserId by this way also.
public class Program
{
private readonly SignInManager<ApplicationUser> _signInManager;
public Program(SignInManager<ApplicationUser> signInManager)
{
_signInManager = signInManager;
var UserId = _signInManager.Context.User.Claims.FirstOrDefault().Value;
}
}
Where ApplicationUser class is given below....
public class ApplicationUser:IdentityUser
{
[Column(TypeName = "Nvarchar(500)")]
public string FirstName { get; set; }
[Column(TypeName = "Nvarchar(500)")]
public string MiddleName { get; set; }
[Column(TypeName = "Nvarchar(500)")]
public string LastName { get; set; }
[Column(TypeName = "DateTime")]
public DateTime? LastAccess { get; set; }
}
And Your ApplicationUser class should inherited by IdentityUser.

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);
}