ASP.NET Web Api Authentication Methods - c#

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.

Related

Implementing custom Authorization logic for Web API on ASP.NET Core 6

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.

ASP.Net Core WebAPI Authorization Policy for User or Admin

I have a controller that returns data about users. I want to set the authorization such that an admin can access this controller and retrieve data for any user, and a non-admin user can access the controller and retrieve data for themselves.
I've ruled out using [Authorize (Roles = "Admin")] because this means users can't get their own data. So I've inserted the following logic into the controller action:
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.Name).Value;
var roles = _httpContextAccessor.HttpContext.User.FindAll(ClaimTypes.Role);
var query = roles.Select(r => r.Value).Contains("Admin");
Customer customer =await _context.Customers.FindAsync(id);
if (!(customer.EmailAddress == userId || query))
return Unauthorized();
This is roughly equivalent to this Stack Overflow answer, but for ASP.Net Core rather than MVC.
My question is, is there a way to do this with an Authorization Policy? Adding the RequireRole check is straightforward and covered in the MS Documentation as well as countless blogs, but I couldn't find or figure out a way to use a policy to check that the data the user is trying to access is their own.
I'm sure this isn't an uncommon requirement, is there a way to do this, or is what I'm currently doing OK? The only other approach I could think of was to have two separate endpoints, but both options seem inelegant.
The policy is for authorization , but either Admin or A normal user can access the controller , they are all authorized .
That is your custom logic to determine which data should be returned , that is nothing related to authorization . If you insist on using policy , you can put the logic to handler but that is nothing change when logic is in controller :
public class CustomerHandler : AuthorizationHandler<CustomerRequirement>
{
IHttpContextAccessor _httpContextAccessor = null;
public CustomerHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
CustomerRequirement requirement)
{
HttpContext httpContext = _httpContextAccessor.HttpContext;
//your logic
httpContext.Items["message"] = "ownData";
context.Succeed(requirement);
return Task.CompletedTask;
}
}
And read in controller so that you can know whether read his own data or all users' data :
var message = HttpContext.Items["message"];
In my option ,set two endpoints/function in your web api , one for admin , one for user is the clean way . In addition , that is your client app's responsibility to determine that current user wants to return his own data or all user's data . That seems not quite correct to send request to web api and let api to determine by logic . Webapi should include the clean functions/endpoint to map each request from client .

How to inject the current user for a request in to a controller / service?

In my WebApi based application I have a Ninject binding for injecting the current user in to things ...
Bind<User>().ToMethod((context) =>
{
User result = new User { UserName = "Guest" };
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
var service = Kernel.Get<IUserService>();
var user = service.GetAll().FirstOrDefault(u => u.UserName == HttpContext.Current.User.Identity.Name);
return user;
}
return result;
});
Pretty straightforward right?
I then have a webApi controller that has a service class injected to its ctor, which in turn has the current user injected in to it, the stack looks something like this ...
class SomethingController : ApiController {
public SomethingController(ISomethingService service) { ... }
}
class SomethingService : ISomethingService {
public SomethingService(User user) { ... }
}
Frustratingly for some odd reason when i inject the current user in to the service constructor the user is not authenticated yet.
It appears that the stack is being constructed before the authentication provider (aspnet identity) has done its work and confirmed who the user is.
Later when the actual service call is made from the controller the user is authenticated and the user object I have been given is for a guest not the actual user that made the call.
My understanding was that a controller was not constructed until authentication and determining the request / user details was done.
How can I ensure that the current, authenticated user is passed to my business services correctly every time (only after authentication has taken place)?
EDIT:
I think I found the problem: dotnetcurry.com/aspnet/888/aspnet-webapi-message-lifecycle it looks like the controller and all its dependencies are created before any auth logic is run. I guess the question then becomes ... can I force the AuthoriseFilter to run and confirm the users identity before the controller is constructed.
This changes my question somewhat to become:
How do I ensure authentication occurs before the controller and all its dependencies are constructed?
EDIT 2: An Answer - Not ideal but an answer ...
Can someone with more rep unlock this question please, this is not a duplicate of the other question, the OP is asking about the basica usage of Ninject I am asking about WebApi lifecycle and how to get session related context prior to the session being known for the current request.
Ok I don't like this answer but its a solution.
If anyone else has a better one I would love to hear it ...
If I update my service constructor to this ...
class SomethingService : ISomethingService {
public SomethingService(IKernel kernel) { ... }
}
... and I drop the kernel in to a local field, when I come to run my service code ...
public void Foo() {
var user = kernel.Get<User>();
}
... What this means ...
I get the user at the point of the request lifecycle where authentication and authorisation has taken place, and the stack is correctly constructed.
Now if I ask for the user, when the rule runs the HttpContext is correctly showing the right user details.
It works but ...
This is very much DI antipattern type behaviour, I don't like it and would prefer to find a solution that meant I could Authenticate the user there and then if it hadn't already happened yet but it would basically mean replicating code that the WebApi stack already has (that could mean its easier though) and will take place anyway basically resulting in the Authentication process happening twice.
In the absence of such a solution ... this is a "semi" reasonable workaround.
It's been a while but I later found out the answer to this.
The problem was that whilst I had some auth info in the form of say a an auth token provided as a header value, I was trying to construct information about the user and I may not have even needed it during the request.
I wanted to ensure that all my business services sat behind the controller handling the request would be given the current user when in fact they only needed the auth information so that the auth info could be used to determine and make security related decisions (such as getting the user if appropriate).
In the end I took to creating an IoC binding that could examine my owin context and ask that for the auth information, that auth information I would then inject in to my controller / service as needed.
From there I at least had the contextual information I needed to make the key decisions.

Validating user rights when invoking controller method in ASP.NET MVC

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

How to use ServiceStack authentication correctly in ASP.Net MVC controller

I'm having problem with getting ServiceStack [Authentication] attribute to work in ASP.Net MVC4 controller, pages / action methods with the attribute keep redirecting Users to the login page even after the login details are submitted correctly.
I've followed the SocialBootstrapApi example, with the difference being that all the authentication web service calls are made from the controllers:
this.CreateRestClient().Post<RegistrationResponse>("/register", model);
Other things that I've done so far:
Use my own user session implementation subclassing AuthUserSession (not too different from the example, but using my own implementation of User table)
Inherit ServiceStackController on my BaseController, overriding the default login URL
Enable Auth feature in AppHost with my user session implementation
Registration does work, user auth logic works (even though the session does not persist), and I can see the ss-id and ss-pid cookies in the request.
So my complete list of questions:
How do I make the [Authenticate] attribute work (or, what did I do wrong)?
How do I save and reuse the user session in an MVC controller? At the moment this.UserSession is always null.
How do I logout a user? this.CreateRestClient().Get<AuthResponse>("/auth/logout"); does not seem to work.
Update 1:
The session cookies (ss-id and ss-pid) gets created when I attempt to load the secured page (ones with [Authenticate] attribute), before any credentials get submitted. Is this the expected behaviour?
Update 2:
I can see that the session is saved in MemoryCacheClient, however trying to retrieve it in the base controller via this.Cache.Get<CustomUserSession>(SessionKey) returns null (where SessionKey is like: urn:iauthsession:1)
After much fiddling around, apparently the way to hook ServiceStack authentication is to call the AuthService via:
try {
authResponse = AuthService.Authenticate(new Auth{ UserName = model.UserName, Continue = returnUrl, Password = model.Password });
} catch (Exception ex) {
// Cut for brevity...
}
and NOT authResponse = this.CreateRestClient().Post<AuthResponse>("/auth/credentials", model);!
Where AuthService is defined in the base controller as:
public AuthService AuthService
{
get
{
var authService = ServiceStack.WebHost.Endpoints.AppHostBase.Instance.Container.Resolve<AuthService>();
authService.RequestContext = new HttpRequestContext(
System.Web.HttpContext.Current.Request.ToRequest(),
System.Web.HttpContext.Current.Response.ToResponse(),
null);
return authService;
}
}
Everything else (incl. session) works correctly now.
You can find how it could be done in the ServiceStack Use Cases repository. The following example is based on MVC4 but works perfectly for MVC3 either: CustomAuthenticationMvc.

Categories

Resources