Help - MVC Certificate Security - security

We have an MVC 2/Entity Framework app that is a replacement/rewrite of an existing system. It's been using ASP membership for security during development but now we need to replace this so it is compatible with the customers existing security infrastructure, partly to allow both old and new systems to run side by side for a while and also because they already have a process and system to setup customers and we can't replace this yet.
The existing security centers around a table in the database that stores a certificate number mapped to a customerid. The customerid is then used to filter relevant data sent back in the UI.
My question is what is the most efficient way to go from certificate number to customerid. Each MVC controller action can grab the certificate number from the HTTPContext and do a look up in the security table to get the customerid but it seems inefficient to this on every controller action. The system could have 1000 concurrent users. We are thinking that it should work similiar to ASP.NET membership, where a username/password login generates a security token that is then placed in a cookie. Instead we would have the certificate replace the username/password login but it would still generate a security token.
The problem is we don't know enough about this system to determine how to go about it, or even if it's the best way forward. If anyone can offer any advice or pointers to how we would implement this it would be much appreciated.

Either
add it to the users Session once you look it up so its available upon login.
add it to the forms auth ticket (make sure you are patched for the POET vulnerability
or this could be forged)
or
cache the table in memory and do you lookups as required. this might be 'slightly' less efficient than session if you are already using the session because you will then be locking two collections (normal session usage) and the collection you store this in will have to be synchronized.
If you choose to store this information in the ticket you can create a CustomIdentity object to store this customer id in.
/// <summary>
/// Deserializes the forms auth cookie with our userid, companyid, etc.
/// </summary>
/// <param name="sender">
</param>
/// <param name="e"></param>
void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
string encTicket = authCookie.Value;
if (!String.IsNullOrEmpty(encTicket))
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(encTicket);
CustomIdentity id = new CustomIdentity(ticket);
//Assign the roles. If they aren't available, get from the session.
//The problem is when we use this custom principal it seems our roles arent populated.
GenericPrincipal principal = new GenericPrincipal(id, new string[] { "User" });
HttpContext.Current.User = principal;
}
}
}
After each request is authenticated via the forms auth ticket you can deserialize this information into a Customer IIdentity object which can then be read throughout the application via:
int companyId = ((CustomIdentity)HttpContext.Current.User.Identity).CompanyId

Related

Get Azure AD Groups Before Building Authorization Policies

We're developing an application that uses a back-end built on .Net Core 2.2 Web API. Most of our controllers merely require the [Authorize] attribute with no policy specified. However, some endpoints are going to require the user to be in a particular Azure AD Security Group. For those cases, I implemented policies like this in the Startup.cs file:
var name = "PolicyNameIndicatingGroup";
var id = Guid.NewGuid; // Actually, this is set to the object ID of the group in AD.
services.AddAuthorization(
options =>
{
options.AddPolicy(
name,
policyBuilder => policyBuilder.RequireClaim(
"groups",
id.ToString()));
});
Then, on controllers requiring this type of authorization, I have:
[Authorize("PolicyNameIndicatingGroup")]
public async Task<ResponseBase<string>> GroupProtectedControllerMethod() {}
The problem is that our users are all in a large number of groups. This causes the Graph API to return no group claims at all, and instead a simple hasGroups boolean claim set to true. Therefore, no one has any groups, and thus cannot pass authorization. This no-groups issue can be read about here.
This string-based policy registration, lackluster as it may be, seems to be what the .Net Core people are recommending, yet it falls flat if the groups aren't populated on the User Claims. I'm not really seeing how to circumnavigate the issue. Is there some special way to set up the AppRegistration for my API so that it does get all of the groups populated on the User Claims?
Update:
In the solution, I do have a service that calls Graph to get the user's groups. However, I can't figure out how to call it before it's too late. In other words, when the user hits the AuthorizeAttribute on the controller to check for the policy, the user's groups have not yet been populated, so the protected method always blocks them with a 403.
My attempt consisted of making a custom base controller for all of my Web API Controllers. Within the base controller's constructor, I'm calling a method that checks the User.Identity (of type ClaimsIdentity) to see if it's been created and authenticated, and, if so, I'm using the ClaimsIdentity.AddClaim(Claim claim) method to populate the user's groups, as retrieved from my Graph call. However, when entering the base controller's constructor, the User.Identity hasn't been set up yet, so the groups don't get populated, as previously described. Somehow, I need the user's groups to be populated before I ever get to constructing the controller.
I found an answer to this solution thanks to some tips from someone on the ASP.NET Core team. This solution involves implementing an IClaimsTransformation (in the Microsoft.AspNetCore.Authentication namespace). To quote my source:
[IClaimsTransformation] is a service you wire into the request pipeline which will run after every authentication and you can use it to augment the identity as you like. That would be where you’d do your Graph API call [...]."
So I wrote the following implementation (see an important caveat below the code):
public class AdGroupClaimsTransformer : IClaimsTransformation
{
private const string AdGroupsAddedClaimType = "adGroupsAlreadyAdded";
private const string ObjectIdClaimType = "http://schemas.microsoft.com/identity/claims/objectidentifier";
private readonly IGraphService _graphService; // My service for querying Graph
private readonly ISecurityService _securityService; // My service for querying custom security information for the application
public AdGroupClaimsTransformer(IGraphService graphService, ISecurityService securityService)
{
_graphService = graphService;
_securityService = securityService;
}
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var claimsIdentity = principal.Identity as ClaimsIdentity;
var userIdentifier = FindClaimByType(claimsIdentity, ObjectIdClaimType);
var alreadyAdded = AdGroupsAlreadyAdded(claimsIdentity);
if (claimsIdentity == null || userIdentifier == null || alreadyAdded)
{
return Task.FromResult(principal);
}
var userSecurityGroups = _graphService.GetSecurityGroupsByUserId(userIdentifier).Result;
var allSecurityGroupModels = _securityService.GetSecurityGroups().Result.ToList();
foreach (var group in userSecurityGroups)
{
var groupIdentifier = allSecurityGroupModels.Single(m => m.GroupName == group).GroupGuid.ToString();
claimsIdentity.AddClaim(new Claim("groups", groupIdentifier));
}
claimsIdentity.AddClaim(new Claim(AdGroupsAddedClaimType, "true"));
return Task.FromResult(principal);
}
private static string FindClaimByType(ClaimsIdentity claimsIdentity, string claimType)
{
return claimsIdentity?.Claims?.FirstOrDefault(c => c.Type.Equals(claimType, StringComparison.Ordinal))
?.Value;
}
private static bool AdGroupsAlreadyAdded(ClaimsIdentity claimsIdentity)
{
var alreadyAdded = FindClaimByType(claimsIdentity, AdGroupsAddedClaimType);
var parsedSucceeded = bool.TryParse(alreadyAdded, out var valueWasTrue);
return parsedSucceeded && valueWasTrue;
}
}
Within my Startup.cs, in the ConfigureServices method, I register the implementation like this:
services.AddTransient<IClaimsTransformation, AdGroupClaimsTransformer>();
The Caveat
You may have noticed that my implementation is written defensively to make sure the transformation will not be run a second time on a ClaimsPrincipal that has already undergone the procedure. The potential issue here is that calls to the IClaimsTransformation might occur multiple times, and that might be bad in some scenarios. You can read more about this here.
You can use the Microsoft Graph API to query the user's groups instead:
POST https://graph.microsoft.com/v1.0/directoryObjects/{object-id}/getMemberGroups
Content-type: application/json
{
"securityEnabledOnly": true
}
Reference: https://learn.microsoft.com/en-us/graph/api/directoryobject-getmembergroups?view=graph-rest-1.0&tabs=http
The scenario will be:
Your client app will acquire access token (A) for accessing your back-end Web API.
Your Web API application will acquire access token (B) for accessing the Microsoft Graph API with the access token (A) using OAuth 2.0 On-Behalf-Of flow. Access token (B) will be used to get the user's groups.
Web API validates the user's group using a policy (recommended) or custom attribute.
The protocol diagram and sample request are listed in this article using the Azure AD V2.0 Endpoint. This article is for the V1.0 endpoint. Here are code samples for .Net Core.

Shiro: where credential should be stored?

Little prehistory:
I develop RESTful services. That services receives requests from the web frontend and resends it to another server with the actual business logic. I use Shiro to protect my services. Problem is that some business logic functions require a user password. Of course, I can store password in my principal, but I think it is not correct to store credentials there.
Question
So, what is the conceptual right place where I should store credentials to have access inside my REST services?
Update
Ok, I can also store passwords in Shiro sessions, but i don't think that it is the correct place.
Normally, the info is kept in an implementation of AuthenticationToken. This interface has two method: getPrincipal (for example login or email) and getCredentials(). The last is usually used to store a password.
If you look at class UsernamePasswordToken, which is an implementation of this interface, you see that the two are indeed used for username and password.
Now what we did is extend the class AuthorizingRealm for our own authentication mechanism and in the authentication method we store the token in the principal.
#Override
public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
... authentication logic
SimplePrincipalCollection principalCollection = new SimplePrincipalCollection(login, realmName);
principalCollection.add(token, realmName);
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principalCollection, login.getPasswordHash());
return simpleAuthenticationInfo;
}
Now you can get the token later:
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
AuthenticationToken token = principals.oneByType(AuthenticationToken.class);

MVC 5 & ASP.NET Identity - Implementation Confusion

I'm creating a new web application that will be written using MVC 5 and Entity Framework Database First Approach. I would also like to use ASP.Net Identity to look after membership, authentication, authorisation etc.
I've read a good bit about the ASP.Net Identity on the web and how it works, however, I am still learning about this topic.
When I created my MVC 5 application in Visual Studio 2013 and looked at the Account Controller my first instinct was that I didn't like what I saw, i.e., a DbContext was being referenced named 'ApplicationDbContext'. The reason I didn't like this was because I prefer to keep my DbContext in the appropriate project within my solution, i.e., in my Model layer which adheres to the separation of concerns logic.
Also, the out of the box MVC 5 project uses Entity Framework Code First to create a default database and tables to store the Users, Roles etc.
Because I must use an existing database with an existing User table, this approach does not suit my needs.
I still want to use the latest ASP.Net Identity for my application as it looks to have many benefits, therefore, I found this article which stripped back alot of the Entity Framework code but still got OWIN powered authentication into an ASP.NET MVC.
http://www.khalidabuhakmeh.com/asp-net-mvc-5-authentication-breakdown-part-deux
Using the tutorial above, here is the HttpPost Login method for my Account Controller
[HttpPost]
[AllowAnonymous]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
//Calling my own custom Account Service which validates users login details
var user = _AccountService.VerifyPassword(model.UserName, model.Password, false);
if (user)
{
var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, model.UserName), }, DefaultAuthenticationTypes.ApplicationCookie, ClaimTypes.Name, ClaimTypes.Role);
//ToDo: Manually adding Role, but will pull from db later
identity.AddClaim(new Claim(ClaimTypes.Role, "guest"));
AuthenticationManager.SignIn(new AuthenticationProperties
{
IsPersistent = model.RememberMe
}, identity);
return RedirectToAction("Index", "MyDashboard");
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
return View(model);
}
In my previous MVC applications I usually rolled my own custom membership and when a User logged into the site and was authenticated, I would have stored the any additional user details such as userID, DOB etc in the UserData string of the FormsAuthenticationTicket.
As the code above does not use FormsAuthentication, instead it uses OWIN CookieAuthentication, I am not sure how to store this additional user data.
Therefore, I have a few questions about the problems I am experiencing.
How do I store the userID or any other additional piece of user data (DOB etc) the way I used to in FormsAuthentication? Is this done by adding a Claim to the identity?
Does the method of using ASP.Net Identity/ OWIN above seem correct considering I am using Entity Framework Database First with an existing database?
Should I be using the out of the box code that is used in the Account Controller, i.e., UserManager, ApplicationUser, ApplicationDbContext etc and hooking this up to work with my existing database?
I apologise if my question is confusing, I suppose I'm just a little unsure of what approach I should be using whilst attempting to use ASP.Net Identity in my latest project.
Any feedback would be greatly appreciated.
Thanks.
1) The new Katana Cookie middleware supports claims. This is what makes this better than forms auth cookie; claims model any key/value pair and those can be stored in the authentication cookie. See this post for more details:
http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/
2 & 3) As far as your storage for identity data, if you need to work with an existing table then you might not be able to use Microsoft's EF provided classes. Instead you'd be left on your own to implement IUserStore and all the other store interfaces your app needs. I'm not certain it's worth changing what you're already using to store the user data.
Keep in mind that the OWIN/Katana part is separate from the identity storage.
Here is the solution
To speed things up you can add sample app to your project and start by modifying the sample app, Samples app includes confirmation email, password recovery, roles admin and user role management etc. NuGet package is at:
Install-Package Microsoft.AspNet.Identity.Samples -Pre
See full details on sample app here: ASP.NET Identity 2.0: Customizing Users and Roles
Controll access to controller or Action by using below attributes
[Authorize] //Anyone with authorization
[Authorize(Roles="Administrator")] //Admin role only
Check if user is in role by
HttpContext.User.IsInRole("Administrator")
UserManager.IsInRole(userID, "Administrator")
Get profile data by
// Create manager
var manager = new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(new ApplicationDbContext()))
// Find user
var user = manager.FindById(User.Identity.GetUserId());
var profileProperty_1 = user.profileProperty_1

Azure ACS, WIF 3.5, Asp.Net 3.5 Custom Membership Provider and IsAuthenticated

Following the steps in this guide Using Azure ACS I have a working Azure ACS service configured & authenticating via Facebook, redirecting back to a website running on my development server.
On authentication success Azure ACS redirects back to my local development website and the IsAuthenticated flag is true, however I want to set the IsAuthenticated flag to true only if the email from the claim also exists in my local database, via a check/call to a custom MembershipProvider. If the email from the claim does not exist I want to redirect the client to a register page. Once registered and authenticated I would like to set the IsAuthenticated flag to true.
Currently once authenticated with Facebook and AzureACS, a user can request a secure page such as ViewAccountBalance.aspx, even though the account does not exist since out of the box IsAuthenticated flag to true. Interested to hear what others have done and what the best practice is.
You'll need to make a clear difference between authentication and authorization. Since the user logged in through Facebook it means he's authenticated (you know who he is and where he comes from).
Now, if you want to restrict parts of the application based on a specific condition you're actually talking about authorization. You might consider combining roles with a simple HttpModule. Example: your HttpModule could verify which page the user is browsing. If the user accesses a page that requires an active profile, you could use the following code:
public class RequiresProfileHttpModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.AuthorizeRequest += new EventHandler(OnAuthorize);
}
private void OnAuthorize(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
if (app.Request.Url.ToString().Contains("bla") && !app.Context.User.IsInRole("UsersWithProfile"))
app.Response.Redirect("http://myapp/register.aspx");
}
}
The only thing you'll need to take care of is to update the principal to make sure it has the role UsersWithProfile if the user filled in his email address.
This is just one of many possible solutions. If you're using ASP.NET MVC you could achieve the same result with global ActionFilters. Or, you could also try to work with the IClaimsPrincipal (add a claim if the user has a profile).
Sandrino is correct. You can use role based authorization (or more generally, claim based authorization). By default, ACS simply returns the claims issued by the identity providers to your relying party. For Facebook, it will return an email claim. However, you can configure ACS to create additional rules. For example, you can map particular users to a role whose value is administrator. Then ACS will also return this role claim to your relying party. Then you can use Sandrino’s suggestion to use role based authorization. You can also refer to http://msdn.microsoft.com/en-us/library/windowsazure/gg185915.aspx for more information.

How do I secure REST resources so that only a single user of a role can access it?

I have succesfully created a REST web service with Jersey and secured it via java security annotations.
It looks something like this
GET /users/ // gives me all users
GET /users/{id} // gives the user identified by {id}
POST /users/ // creates user
PUT /users/{id} // updates user identified by {id}
DELETE /users/{id} // delete user
I also have setup a realm with two roles: user and admin
I secured all methods so that only admins can access them.
Now i want to give free the PUT /users/{id} and GET /users/{id} methods, so that users can access their own and only their own resources.
Example:
// user anna is logged in and uses the following methods
GET /users/anna // returns 200 OK
GET /users/pete // returns 401 UNAUTHORIZED
Since i could not find a way to configure this through annotations, I am thinking of passing the HTTP request to the corresponding method to check if the user is allowed to access the resource.
It would look something like this for the GET /users/{id} method:
#GET
#Path("/users/{id}")
#RolesAllowed({"admin","user"})
#Produces(MediaType.APPLICATION_JSON)
public Response getUser(
#PathParam("id") String id,
#Context HttpServletRequest req
) {
HttpSession session = request.getSession(false);
if (session != null && session.getValue("userID").equals(id))
return getObject(User.class, id);
return Response.status(Status.UNAUTHORIZED).build();
}
I don't like this aproach because i think i would have to add the userID manualy to the session.
Do you know a more elegant way to solve this?
If not how do you add the userid to the session while using form authentication?
EDIT
Thank you Will and Pavel :) Here is my final solution:
#Context
private SecurityContext security;
// ...
#GET
#Path("/users/{id}")
#RolesAllowed({"admin","user"})
#Produces(MediaType.APPLICATION_JSON)
public Response getUser(#PathParam("id") String id){
if (security.isUserInRole("user"))
if (security.getUserPrincipal().getName().equals(id))
return getObject(User.class, id);
else
return Response.status(Status.UNAUTHORIZED).build();
else
return getObject(User.class, id);
}
In the HttpServletRequest, you can call getRemoteUser() or getUserPrincipal() to get the identity of the logged in user. You would then continue like you are doing in specifically allowing or denying them access to the particular resource.
Blessed Geek is referring more specifically to the aspect of REST regarding stateless transactions and the use of HTTP authentication. While this is an important point in the larger scope of a REST architecture, it's less relevant to your specific question since you don't specify the type of authentication mechanism you're using against your Java EE app, especially since authentication is a container issue in Java EE, not an application issue.
If you're using basic authentication, then you are using HTTP headers to manage authentication and authorization. If you're using form based authentication, then the container is managing this for you via the servlet session, making the service stateful (since sessions are a stateful artifact).
But this has no bearing on your specific question.
One of the most important aspects of deploying REST is understanding the role of http headers and cookies.
For REST to be practical, you need to deploy an authentication framework.
Read
GWT and Google Docs API.
GWT-Platform login + session management
Read up on Google Federated Login, OAuth and OpenID.
Some of my explanations may be outdated, if they were posted before OAuth 2.0.

Resources