I'm working on a project where users can log in and create as many number of "work projects" as they like, which are tied to their account Id. We're using OWIN and ASP.NET Identity 2.1.
All the MVC controller actions that respond to HTTP POST requests require the WorkProjectId to be passed in as a HTTP header. The logged in user should only ever be able to interact with WorkProjects that are associated with their login. This presents an important security consideration: is it best practice to interrogate what WorkProjectId are associated with the currently logged in user at the time the controller action is invoked, perhaps by using a custom attribute?
E.g.
[EnsureUserIsAllowedToDoAnythingToThisWPID]
public async Task UpdateWorkProjectTitle(ViewModel vm) {
...
}
Because the user can create as many WorkProjects as they see fit, I don't think I can do this with Claims based security. As far as I understand, if WorkProjectIds were somehow stored as Claims, if they were modified it would necessitate logging the user in and out whenever that happened ... which is obviously not acceptable.
So, to achieve what I need, is it "wrong" to store the Ids the logged in user has access to in session state? I've been burned very badly in the past on other projects with session state abuse (read: far too much data being serialised into session state) bringing the web servers to their knees due. I'd prefer to avoid it if there are equally simple approaches.
Thanks
Why not just add/remove claims for current user? On controller side via UserManager.AddClaim by pasting in logged-in-user id and desired Claim object (i.e. id of workProject?). As far as I know, storing user data (i.e. allowed WorkProjectIds) in cookies is preferable. And your custom authorize attribute will check if requested WorkProject is allowed for current user:
[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method,
Inherited = true, AllowMultiple = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private string _url; // path to action, also you can get it from request
private Operations _operation; // user requested action (CRUD? or administer, execute, etc.)
// example of usage as attribute [CustomAuthAttrib("some string", Operations.Create)]
public CustomAuthorizeAttribute(string url, Operations operation)
{
_url = url;
_operation = operation;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
// any httpContext.Request... operations
return base.AuthorizeCore(httpContext);
}
}
Here is my some raw listing, currently I'm facing somewhat similar problem. And, to access claims here probably you will need some extension methods that came within OWIN/Katana and/or ASP.NET Identity framework
Related
I have to implement authorization for my web api using another/external API. So I have to get the JWT from the request and call another API with that token to know whether the user is authorized.
presently my authentication is working, and I am using
IServiceCollection.AddAuthentication().AddJwtBearer() // removed code to set options
in sample above, I have removed code to provide options and setting the TokenValidationParameters. So my auth logic is working as expected.
Now i am looking to implement custom Authorization. In my custom authorization logic i have to make call to another/external API and pass some parameters. The parameters will be different for different action methods in my controller. The external API will return bool (indicating whether authorized or not), I don't have need to maintain any application role/claims in my code.
is using dynamic policy name and string parsing as mentioned in doc the only/recommended option.
So i have to get jwttoken from request and call another API with that token to know if user is authorized or not.
You should try to prevent having to make an an outbound API request for each request your API gets.
It seems like you have an external authentication service which lets your users log in and returns a token of sorts. You need to know how that third party signs their tokens, then obtain some form of (public) key from them.
With this key you can validate whether the token has been signed by the party you trust. For this, you configure the appropriate TokenValidationParameters which you pass to your services.AddAuthentication().AddJwtBearer() so you can let Identity validate their token using their keys.
See:
Authorize with a specific scheme in ASP.NET Core
Microsoft.AspNetCore.Authentication.JwtBearer Namespace
Ultimately you'd also configure some sort of background job that cycles the keys of the external provider when they do, if they do, which they hopefully do.
As for your updated question: you appear to want to use an external service for authorization, i.e. who can do what.
You'll have to implement this yourself. Usually this is done using scopes, which are retrieved once, during authentication. These can contain values like "finance" to give access to financial controllers, or "finance:orders:list finance:products".
[RequiredScope("finance:orders", "finance:orders:list")]
public IActionResult Index()
{
return View();
}
If the API you're talking to does not have a way to retrieve the relevant scopes, claims or permissions during authentication (or once per resource), then you can't, for example, cache the user's roles to your controllers or entities.
You need to realise this will incur extra overhead per API call, as well as your application being down when the authentication/authorization service is down.
If you still want to do this, the most trivial way to do async authorization on a controller would be a policy:
public class AuthorizeWithAuthServiceRequirement : IAuthorizationRequirement
{
public const string PolicyName = "external-authorization-service";
}
public class AuthorizeWithAuthServiceHandler : AuthorizationHandler<AuthorizeWithAuthServiceRequirement>
{
private IYourApiService _yourApiService;
public AuthorizeWithAuthServiceHandler(IYourApiService yourApiService/* your DI here */)
{
_yourApiService = yourApiService;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthorizeWithAuthServiceRequirement requirement)
{
var httpContext = context.Resource as HttpContext
?? throw new ArgumentException("Can't obtain HttpContext");
// Get the user or their claims or the ID from the route or something
var user = httpContext.User;
var claim = user.FindAll("foo-claim");
var allClaims = user.Claims;
var id = httpContext.Request.RouteValues["id"];
// TODO: response and error handling
var isUserAuthorized = _yourApiService.IsUserAuthorized(user, id, entity, claim, ...);
if (!isUserAuthorized)
{
context.Fail(/* TODO: reason */);
}
}
}
You register this with DI like this:
// Register the handler for dependency injection
services.AddSingleton<IAuthorizationHandler, AuthorizeWithAuthServiceHandler>();
// Register the policy
services.AddAuthorization(options =>
{
options.AddPolicy(AuthorizeWithAuthServiceRequirement.PolicyName, x => { x.AddRequirements(new AuthorizeWithAuthServiceRequirement()); });
});
And then apply it to a controller or action method:
[Authorize(Policy = AuthorizeWithAuthServiceRequirement.PolicyName)]
public class FooController : Controller
{
}
If you want more fine-grained control like custom attributes with parameters (like [CustomAuthorization(ApiPermission.Foo)]) per controller or action, or if you want to first load an entity and pass that to the handler, see Ilja in Asp.Net Core: Access custom AuthorizeAttribute property in AuthorizeHandler and their GitHub repository demonstrating three different approaches.
I am creating an application in that there is role-based module management and it is changed anytime by anytime.
Scenario:
If a user has access to create and view Employee than a user can do only create and view employees but in future admin change user's role from creating and view to view and delete than a user can only do that activity.
I try with [Authorize(Roles ="Staff")] but if admin change runtime than it isn't managed.
Can anyone look into this and get back to me?
This is a complicated question and there isn't a right answer but there are several ways to do it.
First I will assume you are using stateless auth using a claim based jwt the simplest way is writing your own Policy that will read user roles before every request, this is the simplest way to do it and fastest to implement.
internal class DatabaseRoles : IAuthorizationRequirement
{
public string Role { get; }
public DatabaseRoles(string role)
{
Role = role;
}
}
internal class DatabaseRolesHandler : AuthorizationHandler<DatabaseRoles>
{
private readonly UserManager<IdentityUser> userManager;
public DatabaseRolesHandler(UserManager<IdentityUser> userManager, RoleManager<IdentityRole> roleManager)
{
this.userManager = userManager;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, DatabaseRoles requirement)
{
//NOTE this is the out of the box implementation of roles and simple query to get the roles from the EF backed database. I would recoment makeing a custom privelages store for this and not using roles for this but access rights
var user = await userManager.FindByIdAsync(userManager.GetUserId(context.User));
if (await userManager.IsInRoleAsync(user, requirement.Role))
{
context.Succeed(requirement);
}
}
}
But this solution is not that performant because it requires a call to the database on every request. This is fine on small loads but might create problems in traffic.
The other way is to reevoke the all user tokens when the roles change but this is super complicated. I am sure if you create some fast access store for roles like redis there will be no issues to do the check on every call. Also I do not recommend creating your own user storage because it's a nightmare to maintain and keep up to date in regards to security standards.
If you are using Session/Cookie to store the logged in user details, you can empty the details whenever Admin make role changes. On every action you can check the role in Session/Cookie and move forward.
Now as soon as user clicks anywhere on the screen which hits the controller. The condition will be checked and the User will be logged out as the Session/Cookie object is empty.
I am trying to see if there is something "out of the box" in ASP.net5 for authorization for my application needs. I am using a group/permission based approach for authorization. Using Identity3 I am using Role as Group and then I have created permissions from this. Each permission has a resource that it links to and 1 or more values, like:
Resource = Page, Permissions = Add, Update, View, Delete
Another complication is that the groups have dynamic names, and dynamic permissions!!
I have started to read about authorization in ASP.net5 and it seems that I have found something called Policies, which sound good. It seems to force you to use Claims, which is possible if I use a ClaimsTransformer to get all my permissions and add them as claims from the Db. But am I right in thinking that I would have to create a policy for each Permission, on each resource? That seems like a lot of setup.
Is there anything that I do not know about is already built in ASP.net5 that I could use? Like an attribute like this
[Authorize("Page", "Delete")]
Which I could add to the PageController Delete method.
If I have to use some sort of service and DI that into the controller to implement this, then that would be fine as well.
There is a ClaimsPrincipalPermissionAttribute that can fit to your requirements.
Or you can implement your own AuthorizeAttribute.
I use AspNet.Security.OpenIdConnect.Server for authorization. But you can also have a look at OpenIddict
In any case you can add the Authorize attribute to any method you want like this
[Authorize(Roles = "Administrator,SimpleUser,AnOtherRole")]
public void MyMethod() {}
Resource based authorization might fulfill your needs, but I am a little confused with the page being the resource, rather than what the page acts upon.
Taking your Page/Delete combination, I would imagine that rather than the resource being Page, your Page Delete action takes a parameter, indicating the page that is to be deleted? (If this is not the case then this approach isn't going to work of course)
In this case you'd do something like
[Authorize]
public class PageController : Controller
{
IAuthorizationService _authorizationService;
public PageController(IAuthorizationService authorizationService)
{
_authorizationService = authorizationService;
}
public Delete(int pageId)
{
var page = pageRepo.GetPage(pageId);
if (await authorizationService.AuthorizeAsync(User, page, Operations.Delete))
{
return View(page);
}
else
{
return new ChallengeResult();
}
}
}
In order to enable this you're write a handler based on page and an Operations requirement (or any old requirement, but a parameterized operations requirement means you can write a single handler and branch accordingly).
We tried very hard to move away from putting data in the attribute, and move it into requirements, because data in attributes is, to be frank, a maintenance nightmare.
One other thing to note; as handlers are resolved through DI you could inject your user to permissions resolver into the handler, which would avoid using claims transformation.
ASP.NET provides authentication mechanism out of the box which is easy to use, example:
public class HomeController : Controller
{
[Authorize]
public ActionResult Index()
{
ViewBag.Message = "This can be viewed only by authenticated users only";
return View();
}
[Authorize(Roles="admin")]
public ActionResult AdminIndex()
{
ViewBag.Message = "This can be viewed only by users in Admin role only";
return View();
}
}
Check this tutorial
Or if you want more sophisticated mechanism you can implement your own memberhsip provider based on the ASP.NET Membership Provider
I am trying to implement authentication for my web api.
I have read about different techniques of api authentication and the token technique is the most reasonable for me.
I read different articles about how to implement token based authentication in asp.net but they all rely on different libraries such as OAuth or Owin which also provide their own method of database interactions.
The thing is that I have already implemented database interaction with abstract repositories and entities and I would like to find out how can I implement api authentication easily and simply without interfering with my current design.
(By the way, my project is built on top of an empty web api project, so it doesn't come with all the bootstrap and authentication classes).
Thank you
One solution I've seen is to use .NET's HttpApplicationState class and store tokens in appstate; this way you're not directly messing with Session (which would be a REST antipattern), but you can still track all currently logged in users and use HttpContext/HttpActionContext to x-ref active tokens in the app. The benefit to using HttpActionContext is that it is thread-safe, whereas HttpContext is not, so you can lock the appstate, mess with the HttpContext of an individual request, and then unlock the appstate to allow other threads in.
Since locking/unlocking appstate does tie up the app, I'm not sure how well this solution scales, but here it is anyway . . .
General outline:
When a user first logs in, a token is generated for him/her and stored in appstate. Then you can tag any API calls that require authentication (or that need other information stored on that user) with a custom attribute that checks for that token in the appstate, sending the token name as a header in the API call (e.g. "{token-name: TOKEN}").
Here's a brief example:
[in Controller method first activated at login:]
CustomUserObject user = new CustomUserObject();
//store user props
string token = Guid.NewGuid().ToString();
//create AppState instance, mine's called _appState
//...
_appState.Lock();
_appState[token] = user;
_appState.UnLock();
//...
[Then in global.asax:]
public class CustomAuthorize : System.Web.Http.AuthorizeAttribute
{
HttpRequestMessage request = actionContext.ControllerContext.Request;
string token = string.Empty;
if (request.Headers.GetValues("token-name") != null)
{
token = request.Headers.GetValues("token-name").FirstOrDefault().ToString();
IAppStateService appService; //<--- I've created a custom service tier class for appstate stuff
//Get appState instance, however makes sense for you.
//I'm using repo pattern with UnitOfWork, so mine looks like this...
//"IContainer ioc = DependencyResolution.IoC.Initialize();"
//"IAppStateService appService = ioc.GetInstance<IAppStateService>();"
appService.SetHttpApplicationState(HttpContext.Current.Application);
bool isAuthorized = appService.CheckTokenAndDoStuff(token);
//inside that method ^^^ you'll do stuff like
//"_appState.Lock();"
//"if (_appState[token] == null) return false" (or whatever)
//"_appState.Unlock();"
}
if (isAuthorized)
{
HttpResponseMessage resp = request.CreateResponse(HttpStatusCode.OK);
resp.Headers.Add("AuthenticationToken", token);
resp.Headers.Add("WWW-Authenticate", "Basic");
resp.Headers.Add("AuthenticationStatus", "Authorized");
}
return isAuthorized;
}
[then in webapi]
[HttpPost]
[CustomAuthorize]
public HttpResponseMessage NameOfMethod(...)...
...and that should x-check your appstate for your user token for you. Just make sure to include your token in your request header, and make sure to include the Basic Auth info in your response header.
In the documentation for ServiceStack, it says that the best practice is:
Normally ServiceStack calls the method bool HasPermission(string
permission) in IAuthSession. This method checks if the list
List Permissions in IAuthSession contains the required
permission.
IAuthSession is stored in a cache client as explained above You can
fill this list in the method OnAuthenticated you've overriden in the
first part of this tutorial.
I am integrating with an existing system, and have my custom BasicAuthProvider working (inherited from the base BasicAuthProvider). Authentication is working perfectly, now I am building out the Authorization portion. I plan on using the Permissions list as listed above, but I need access to the Route information to determine if a user has access to a particular resource. I see in the IAuthServiceBase there is an IRequestContext which has the absolute URL, but before going through and parsing that out, I figured there has to be a way to gain access to the ServiceStack Route structure to give me either the class name of the Service being requested, or the DTO the requested service is related to.
Here is the OnAuthenticated method from my BasicAuthProvider class:
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
UserSession sess = (UserSession)session;
Model.User currentUser = UserRepository.GetUserByUsername(session.UserAuthName);
//Fill the IAuthSession with data which you want to retrieve in the app eg:
session.FirstName = currentUser.Person.FirstName;
session.LastName = currentUser.Person.LastName;
session.UserName = currentUser.User1;
sess.CurrentUser = currentUser;
//Important: You need to save the session!
authService.SaveSession(session, TimeSpan.FromDays(1));
}
Under MVC I have used some of the Raw Request Data to get the Controller and Action name before, to determine resource authorization, but this is the first project I am using ServiceStack with.
You may find the [RequiredPermission] attribute or even the implementation of it will help you, e.g. the 3rd parameter passed in a RequestFilter is the Request DTO.
And since a Request DTO maps 1:1 with the service, you can be sure that the request is destined for the IService<TRequest> (or its subclasses e.g. ServiceBase<T>, RestServiceBase<T>).
You can access the type of the service programatically as done in the FilterAttributeCache:
var serviceType = EndpointHost.Metadata.GetServiceTypeByRequest(requestDtoType);
I'm not sure of the exact context/use-case you're trying to support but using the [RequiredPermission] or [RequiredRole] attributes may have what you need which by default validates against the list of roles and permissions available in the built-in UserAuth table.
Externally you can use the /assignroles and /unassignroles web services (as part of the AuthorizationFeature plugin) to assign roles and permissions to users (it requires a user with the Admin role by default).
For more info see the documentation pages on Authentication/Authorization and Validation on the ServiceStack GitHub project wiki.