Web Api 2: Authorising against custom claims - asp.net-web-api

In certain controllers, I want to authorize a user against a company ID.
For example, consider the following resource:
api/v1/companies/1234/orders
This should only be accessible by users who belong to company 1234.
Note that I am using OAuth token bearer authentication.
Creating the company claim in OAuthAuthorizationServerProvider:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//...
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
// Creating the companyId claim
identity.AddClaim(new Claim("CompanyId", user.CompanyId.ToString()));
context.Validated(identity);
}
The current controller implementation for the orders resource:
[RoutePrefix("api/v1/companies/{companyId:Guid}/orders")]
public class OrdersController : ApiController
{
[Route]
public IHttpActionResult GetOrders(Guid companyId)
{
var orders = OrdersRepository.Get(companyId);
return Ok(orders.Select(x => OrderModel.From(x)));
}
}
Where do I authorize the companyId URL value against the identity claim?
Can [Authorize] be somehow used here?

Here is a custom authorize filter you could use
using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
public class AuthorizeAction : AuthorizeAttribute {
public string CompanyId;
protected override bool IsAuthorized(HttpActionContext actionContext) {
if (String.IsNullOrEmpty(CompanyId))
{
var routeData = actionContext.Request.GetRouteData();
var myId = routeData.Values["CompanyId"] as string;
CompanyId = myId;
}
var user = actionContext.RequestContext.Principal as ClaimsPrincipal;
if (user == null || !user.Identity.IsAuthenticated)
return false;
if (user.Claims.Any(claim => claim.Type.Equals("CompanyId") && claim.Value.Equals(CompanyId)))
return true;
return false;
}
}
You could also decorate your action or controller with below if you wanted only a single company to access a action or controller.
[AuthorizeAction(CompanyId = "1234")]

See the approach in https://github.com/AzureADSamples/NativeClient-DotNet/blob/master/TodoListService/Controllers/TodoListController.cs - instead of "scope" you can use your custom claim type

Related

passing parameter from custom filter to web api controller

I am working on a web application in which we are using web-api and oAuth2.
I had stored my UserId in front-end but now for security reason I am storing my UserId in backend against the token generated from oAuth2.
So I have around 800 api's in my application all of them are POST api's and the data is passing in those api's like below
Type 1
[HttpPost]
[Authorize]
[ActionName("GetList")]
[Filters.AuthorizeLoginApi()]
public List<BusinessEntities.Admin.Users> GetList(Dictionary<string, string> Parameters)
{
try
{
if (Parameters != null)
{
BusinessLayer.IAdmin.IUsers a = (BusinessLayer.IAdmin.IUsers)DALFinder.GetInstance(typeof(BusinessLayer.IAdmin.IUsers));
return a.GetList(Convert.ToString(Parameters["LoginText"]), Convert.ToString(Parameters["Name"])
, Convert.ToString(Parameters["Email"]), Convert.ToInt32(Parameters["UserTypeId"]), Convert.ToString(Parameters["IsActive"])
, Convert.ToInt32(Parameters["UserId"])); /*(LoginText, Name, Email, UserTypeId, IsActive, UserId);*/
}
else
{
return new List<BusinessEntities.Admin.Users>();
}
}
catch (Exception ex)
{
Utils.Logger.Instance.LogException(ex);
return new List<BusinessEntities.Admin.Users>();
}
}
In the above code I have a Dictionary parameter in which I am storing my userId
Type 2
[HttpPost]
[Authorize]
[ActionName("Delete")]
[Filters.AuthorizeLoginApi()]
public SPResponse Delete(BusinessEntities.Admin.Users item)
{
SPResponse response = new SPResponse();
try
{
//item.ModifiedByUserId is my UserId
BusinessLayer.IAdmin.IUsers a = (BusinessLayer.IAdmin.IUsers)DALFinder.GetInstance(typeof(BusinessLayer.IAdmin.IUsers));
response = a.Delete(item);
}
catch (Exception ex)
{
response.ReturnMessage = ex.Message;
}
return response;
}
I am doing custom validation in each and every api calls like below
public class AuthorizeLoginApi : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
//Code to Get userId from database
//int UserId = data coming from db
//pass the above UserId Parameter into every apis as UserId/ModifiedByUserId
}
}
Now I want to Pass UserId/ModifiedByUserId from OnActionExecuting filter method into my respective API's
How can I achieve this

Web API Validation for Model Bound in GET request

I have created a custom Model Binder to read the data from the URI in a specific format
public ResponseObject Get([FromUri(BinderType = typeof(CustomModelBinder)]ProductFilter product
{...}
public class ProductFilter
{
[Required(ErrorMessage = #"Name is required")]
public string Name { get; set; }
}
public class CustomModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
//Code to convert the uri parameters to object
return true;
}
}
In the above example, i need the name to be passed from the client before executing the Action.
But, I am unable to run the in-built validations on the Product class using this?
Any ideas?
I wrote in a custom action filter and I registered this action filter in the GlobalConfiguration for all the services. The action filter hooks on to onActionExecuting, looks for the validation in the bound arguments.
bool isValid;
foreach (var item in actionContext.ActionArguments)
{
var parameterValue = item.Value;
var innerContext = new ValidationContext(parameterValue);
if(parameterValue != null)
{
var innerContext = new ValidationContext(parameterValue);
isValid = Validator.TryValidateObject(parameterValue, innerContext, results, true);
}
}
//If not valid, throw a HttpResponseException
if(!isValid)
throw new HttpResponseException(HttpStatusCode.BadRequest);
else
base.onActionExecuting(actionContext);
With more tuning, the exact validation message can be retrieved from the validation context and sent as the response message.
I was also able to extend this to having validation attributes on the parameters themselves, thereby giving more flexibility to my Api

Custom Role Provider has issue with AuthorizeAttribute for MVC

I am developing a MVC 5 application with custom role provider, but it seems that the AuthorizeAttribute never call my customer role provider, my code is as below:
My Customer provider:
namespace MyDomain
{
public class CustomRoleProvider : RoleProvider
{
public override string[] GetRolesForUser(string username)
{
using (MyContext objContext = new MyContext())
{
var objUser = objContext.Users.FirstOrDefault(x => x.Username == username);
if (objUser == null)
{
return null;
}
else
{
string[] ret = { objUser.Access_Levels.Name };
return ret;
}
}
}
public override bool IsUserInRole(string username, string roleName)
{
var userRoles = GetRolesForUser(username);
return userRoles.Contains(roleName);
}
}
My controller:
[Authorize(Roles = "Administrator")]
public class AdminController : Controller
And Web.Config:
<system.web>
<roleManager defaultProvider="CustomRoleProvider" enabled="true" >
<providers>
<clear />
<add name="CustomRoleProvider" type="Online_Storage_Portal.CustomRoleProvider" cacheTimeoutInMinutes="30"/>
</providers>
</roleManager>
</system.web>
Also my custom role provider is in the same project as my other controllers, I am able to call my custom role provider method with following code within my controller
String[] roles = Roles.GetRolesForUser(username)
but the controller with [Authorize(Roles = "Administrator")] always redirect the page to login screen even the user login and role are both valued.
Please help!!
I believe I've found the source of your problem. I'm going to assume you're using Windows Authentication, and trying to use your custom role provider in place of the Windows Groups that are automatically loaded. Looking into the MVC AuthorizeAttribute source, you'll find that it is actually calling Principal.IsInRole. Per MSDN:
InRole first checks the IsRoleListCached property to determine whether a cached list of role names for the current user is available. If the IsRoleListCached property is true, the cached list is checked for the specified role. If the IsInRole method finds the specified role in the cached list, it returns true.
If IsInRole does not find the specified role, it calls the GetRolesForUser method of the default Provider instance to determine whether the user name is associated with a role from the data source for the configured ApplicationName value.
So I'm guessing that because the Principal is a Windows Principal, it is coming in with it's roles populated and cached. When the IsInRole is called, it says 'Hey, I've already got roles, why would I go back to the provider to get them again?"
What you could do instead would be somthing like this:
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
WindowsIdentity identity = HttpContext.Current.Request.LogonUserIdentity;
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(new GenericIdentity(identity.Name), Roles.GetRolesForUser());
}
This will pull the windows identity off the HttpContext, use the name to explicitly fetch roles from your custom provider, and then slap a new GenericPrincipal on the request instead. I went further and implemented some logic to store the roles in an encrypted cookie so we don't have to go the role provider on each request.
void Application_PostAuthenticateRequest()
{
HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket authTicket;
if (authCookie == null || authCookie.Value == "")
{
string[] getRoles = Roles.GetRolesForUser();
authTicket = new FormsAuthenticationTicket(1,
User.Identity.Name,
DateTime.Now,
DateTime.Now.AddMinutes(20),
true,
String.Join(";", getRoles));
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
HttpContext.Current.Response.Cookies.Add(authCookie);
}
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch
{
return;
}
string[] roles = authTicket.UserData.Split(';');
if (Context.User != null)
Context.User = new System.Security.Principal.GenericPrincipal(Context.User.Identity, roles);
}

Token based authorization implementation in MVC5

let me first set the stage with the cast of characters:
An MVC 5 application
A WebAPI
I need to implement a token based security to access #2 from #1.
What I have already:
Created a startup class in #2
public void Configuration(IAppBuilder app)
{
// token generation
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(8),
Provider = new MyServerProvider()
});
// token consumption
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
var httpConfiguration = new HttpConfiguration();
WebApiConfig.Register(new HttpConfiguration());
app.UseWebApi(httpConfiguration);
}
Standard code available everywhere on the internet.
This is the code in MyServerProvider also in #2
public class MyServerProvider: OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
await Task.FromResult(0);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
if (context.UserName == "one" && context.Password == "two")
{
var id = new ClaimsIdentity(context.Options.AuthenticationType);
id.AddClaim(new Claim("name", context.UserName));
id.AddClaim(new Claim("role", "user"));
context.Validated(id);
}
else
{
context.Rejected();
}
await Task.FromResult(0);
}
}
And another class that provides the token also in #3
public class TokenProvider
{
public TokenResponse _tokenValue { get; set; }
public string _accessToken { get; set; }
public string GetToken(string tokenEndpoint, string userName, string password)
{
var client = new OAuth2Client(new Uri(tokenEndpoint));
var tokenResponse = client.RequestResourceOwnerPasswordAsync(userName, userName).Result;
_tokenValue = tokenResponse;
_accessToken = _tokenValue.AccessToken;
return _accessToken;
}
}
So far so good.
Q1. Now when a request from a controller hits the api or the api is
called form JavaScript, what happens?
Q2. Which method from the
above get's called?
Q3. What does GrantResourceOwnerCredentials do?
Q4. What does the context object in the above question has and how
does one add the userName and Password to it and how are claims store in a cookie?
Q5. If I have to store the token in a cookie and use it for subsequent requests, do I write
that code in OnActionExecuting method of the controller in #1?
This all may sound very specific but it isn't. I am trying to understand token based authentication from a real world scenario and I am new to this.
I have gone through the samples at ThinkTecture Github repo and they all do a good job in explaining them but I am stuck at implementing it.
Any help is greatly appreciated.
Regards.

Getting HttpRequest extension method written for MVC to work in Web Api

I'm working with OAuth 2.0 for MVC, a .NET library for Oauth2. I'm building a Web Api project, however, and am hoping to get this library to work with Web Api.
The problem I'm running into is that the library uses two extension methods on the HttpRequestBase that it calls from the controller.
Here are the extension methods:
public static string GetToken(this HttpRequest request)
{
var wrapper = new HttpRequestWrapper(request);
return GetToken(wrapper);
}
public static string GetToken(this HttpRequestBase request)
{
if (request == null)
return String.Empty;
// Find Header
var headerText = request.Headers[OAuthConstants.AuthorzationHeader];
if (!String.IsNullOrEmpty(headerText))
{
var header = new AuthorizationHeader(headerText);
if (string.Equals(header.Scheme, "OAuth", StringComparison.OrdinalIgnoreCase))
return header.ParameterText.Trim();
}
// Find Clean Param
var token = request.Params[OAuthConstants.AuthorzationParam];
return !String.IsNullOrEmpty(token)
? token.Trim()
: String.Empty;
}
In the MVC project, they simply call Request.GetToken() from the controller. Of course, Web Api's request is an HttpRequestMessage. I'm afraid addressing the difference between HttpRequest and HttpRequest message is beyond my capabilities right now.
Can I convert this extension method to work with HttpRequestMessage or somehow make it work in Web Api??
Thanks!
All the properties you used to have are still available (assuming the OAuthConstants.AuthorzationParam is set on the query string?)
using System;
using System.Linq;
using System.Net.Http;
namespace YourApp
{
public static class Extensions
{
public static string GetToken(this HttpRequestMessage request)
{
if (request == null)
return String.Empty;
// Find Header
var headerText = request.Headers.GetValues(OAuthConstants.AuthorzationHeader).SingleOrDefault();
if (!String.IsNullOrEmpty(headerText))
{
//Brevity...
}
// Find Clean Param
var token = request.GetQueryNameValuePairs().SingleOrDefault(x => x.Key == OAuthConstants.AuthorzationParam).Value;
return !String.IsNullOrEmpty(token)
? token.Trim()
: String.Empty;
}
}
}
Controller
using System.Collections.Generic;
using System.Web.Http;
using YourApp;
namespace YourApp.Controllers
{
public class FoosController : ApiController
{
public IEnumerable<string> Get()
{
var token = Request.GetToken();
return null;
}
}
}

Resources