I currently have an ASP.NET MVC 4 website where members have an account and can log in using both Facebook and my own login form. I am then using FormsAuthentication.
I would next like to build an API, using WebAPI and expose some of my functionality to a mobile client I am planning on building.
I do not have any plans on having others consume my API, so this would just be for the client I build.
How would I go about implementing security on the WebAPI? Should I be using a token system where I can have a login form on the client, receive the credentials, log them in, and return a token which would be send back to the server on each call?
Should I implement oAuth on the server?
Try to be completely RESTful: use HTTP's built-in authentication system where authentication information is provided by the client in each request. You can also use HTTP Basic Authentication without any security concerns provided that you use SSL, otherwise HTTP Digest is also secure enough for this purpose.
You will need to implement your own HTTP Basic Authentication provider for ASP.NET, fortunately it's easy (and fun!).
It also beats other systems which require a signed URI using a querystring parameter, which is ugly and messes up lovely REStfulness, or carrying a token around (usually passed as a cookie).
Holy wars about how to do authentication in rest aside, you can just use forms authentication. If you are also using a web interface from the same site/domain and you have your authentication stuff well factored this is really convenient and easy.
you need a base class for your api controllers
public class MyApiControllerBase : ApiController
{
public MySecurityContextType SecurityContext { get; set; }
}
an ActionFilterAttribute
public class AuthenticationContextAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
MyApiControllerBase controller = actionContext.ControllerContext.Controller as MyApiControllerBase ;
if (controller != null)
{
var context = ((HttpContextBase)controller.Request.Properties["MS_HttpContext"]);
HttpCookie cookie = context.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
controller.SecurityContext= ParseFormsAuthenticationTicket(ticket);
}
}
}
and code to create the ticket in the first place.
LogIn(HttpRequestBase httpRequest, string userName, string password)
{
var context = DoLoginLogic(userName,password);
FormsAuthentication.SetAuthCookie(context, usePersistentCookies);
}
Authorization will obviously need to be done in the controller methods.
Related
To enable my service layer to access the current User Id anytime it needs, I use Thread.CurrentPrincipal.
The service layer is used by two front-end layers, one MVC App and one MVC Web Api used for a Mobile App.
In the web app, I use Forms Authentication and the Principal is set into Application_PostAuthenticateRequest. It works fine.
In the web Api, I use Owin. But I cannot find a way to set that Principal after each request is authenticated with the access token.
I can do it when the user logs in with its credentials by overriding GrantResourceOwnerCredentials into my OAuthAuthorizationServerProvider or when he logs with its refresh token by overriding GrantRefreshToken in the same class.
But where could I assign it for requests automatically authenticated with the access token ?
NB. I know that in my Api Controllers I can access the current User, and it is correctly set, but I don't want to pass it with each call to my service layer.
Thanks.
I found how to set it.
The bearer validation is not done by the OAuthAuthorizationServerProvider. I had to implement a custom OAuthBearerAuthenticationProvider, and override the ValidateIdentity method:
public class MyBearerAuthenticationProvider : OAuthBearerAuthenticationProvider
{
public override Task ValidateIdentity(OAuthValidateIdentityContext context)
{
Thread.CurrentPrincipal = new Principal(context.Ticket.Identity);
return base.ValidateIdentity(context);
}
}
And plug that provider into my app by using:
OAuthBearerAuthenticationOptions bearerAuthenticationOptions = new OAuthBearerAuthenticationOptions()
{
Provider = new MyBearerAuthenticationProvider()
};
app.UseOAuthBearerAuthentication(bearerAuthenticationOptions);
Unfortunately Thread.CurrentPrincipal is null in my Business Layer. I assume the token validation is done in another thread than the request execution. So I'll have to change my method.
I have been looking for answer to this question for a few days now, but I have not found any success. I would post the links, but it would probably take up the entire page.
So here is what I have...
I have an MVC application, which uses the WC-Federation protocol. I have been able to configure the application, so that it authenticates the users, and returns the claims from ADFS. This works perfect. I can also extract all the claims with no issues. But I am doing this within one of the actions in the controller.
And here is what I want to do...
I want to use ADFS to authenticate the user, but I want to use my own internal roles to authorize the user to have access to specific controllers (e.g. [Authorize(Roles = "CoolRole")]). I want to be able to do this, because I already have a Web API that uses OAuth 2.0, with a backend SQL Server database to manage users and roles (internal and external user.) I now want a secure portal that will allow internal users to access the data with a single-sign-on experience. Looking at the Controller model, I noticed there are some properties associated with the authentication process (OnAuthentication, OnAuthenticationChallenge) and one for the authorization process (OnAuthorization.)
I don't necessarily need the code, but I feel like I've hit a brick all, and I need to be pointed in the right direction.
UPDATE
I tried this:
protected override void OnAuthorization(
System.Web.Mvc.AuthorizationContext filterContext)
{
//Private class to create a new IPrincipal based on my AppUserMgr
var user = _setCurrentUser(
(ClaimsIdentity)filterContext.HttpContext.User.Identity);
filterContext.HttpContext.User = user;
base.OnAuthorization(filterContext);
}
This returned a 401 (Unauthorized) response.
and...
protected override void OnAuthentication(
System.Web.Mvc.Filters.AuthenticationContext filterContext)
{
//Private class to create a new IPrincipal based on my AppUserMgr
var user = _setCurrentUser(
(ClaimsIdentity)filterContext.HttpContext.User.Identity);
filterContext.Principal = user;
base.OnAuthorization(filterContext);
}
This just calls the STS numerous times, before it fails. I even tried swapping after the assignment to after the base is called in both. No luck.
Prior to the previous ones, I also tried to add an AuthorizeFilter to the control, but that didn't help:
http://pratapreddypilaka.blogspot.in/2012/03/custom-filters-in-mvc-authorization.html
I found this link: http://brockallen.com/2013/01/17/adding-custom-roles-to-windows-roles-in-asp-net-using-claims/
From there, I guessed my way through
Here is the basics of what I did:
I ended up overriding the OnAuthentication method of the Controller, but still made sure to call the base. I did this from within an extended class. Here is the concept:
public class AdfsController : Controller
{
//Some code for adding the AppUserManager (used Unity)
protected override void OnAuthentication(
System.Web.Mvc.Filters.AuthenticationContext filterContext)
{
base.OnAuthentication(filterContext);
//Private method to set the Principal
_setCurrentUser(filterContext.Principal);
}
private void _setCurrentUser(IPrincipal principal)
{
//Put code to find to use your ApplicationUserManager or
//dbContext. roles is a string array
foreach(var role in roles)
{
((ClaimsIdentity)((ClaimsPrincipal)principal).Identity)
.AddClaim(new Claim(ClaimTypes.Role, role));
}
}
}
In the Controller, you can now add the follow:
public class HomeController : AdfsController
{
//I used a magic string for demo, but store these in my globals class
[Authorize(Roles = "CoolRole")]
public ActionResult Index()
{
return View();
}
}
I tested this by checking a role assigned to the current user, and that worked! Then I changed the role to something like "reject", which the user was not assigned; and I received a 401 Unauthorized.
ADFS is the authentication/token service in Azure. to enable the Roles Based Authentication, you can use Azure RBAC (Role Based Access Controll) service to basically Augment the claims that you get back from the ADFS and add the roles that you get back from RBAC to the token, and use the same token in your API so lock down or secure the backend with that augmented token...
here is the reference for RBAC:
http://azure.microsoft.com/en-in/documentation/articles/role-based-access-control-configure/
Here is my scenario: I have a MVC web application and Web API. Web application making calls to web api for saving/retrieving data from server.
Lets say this is a question/answer web site. Right now I have an API that gives me userid if I provide username, password. But there are other areas in the website and its easy to retrieve other user's userid. I'm keeping the userid in the session storage and sending that in the POST object wherever required. Now any user can tweak that userid in the session storage and they can post the question/answer on behalf of other user.
How I can prevent this? One approach I was thinking but not sure if this is feasible solution - can we retrieve the userid from the supplied bearer token on the server side?
Sure you can do this, once you establish token based authentication in Web API using the resource owner credential flow, and when you attribute you protected controllers with [Authorize]. The valid bearer token you will send to this protected endpoint will create ClaimsPrincipal principal (identity) object where the user is stored in it, you can get the username as the below:
[RoutePrefix("api/Orders")]
public class OrdersController : ApiController
{
[Authorize]
[Route("")]
public IHttpActionResult Get()
{
ClaimsPrincipal principal = Request.GetRequestContext().Principal as ClaimsPrincipal;
var Name = ClaimsPrincipal.Current.Identity.Name;
var Name1 = User.Identity.Name;
return Ok();
}
}
For more detailed information about this you can read my detailed posts about this topic here.
I'm trying to implement OAuth 2.0 resource access for the resource server. I have acquired a token and want to pass that token to the resource server so that resource server could validate with the authorization server for every request, passing the token in the http header
(e.g. Authorization: Bearer mF_9.B5f-4.1JqM).
I'm using MVC 4, and I've been told MvcHandler should be used to achieve this However I'm not sure where to start. Can anyone point me to a general direction on what should be done? I already have bunch of actions and controllers and want to put this layer on top of those instead of going back to every action and changing and/or decorating each action.
Use Authentication filters
Authentication filters are a new kind of filter in ASP.NET MVC that
run prior to authorization filters in the ASP.NET MVC pipeline and
allow you to specify authentication logic per-action, per-controller,
or globally for all controllers. Authentication filters process
credentials in the request and provide a corresponding principal.
Authentication filters can also add authentication challenges in
response to unauthorized requests.
You just need to implement the IAuthenticationFilter for your needs register it and it's done.
public class YourAuthenticationAttribute : ActionFilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
if (user.Identity.IsAuthenticated == false)
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
If you want it to be global add it as a global filter in FilterConfig.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new YourAuthenticationAttribute());
}
More info:
ASP.NET MVC 5 Authentication Filters
ASP.NET MVC 5 Authentication Filters
AUTHENTICATION FILTERS IN ASP.NET MVC 5
FINALLY THE NEW ASP.NET MVC 5 AUTHENTICATION FILTERS!
Given that the FormsAuthentication module fires before a custom http module that handles the OnAuthenticateRequest, I'm curious if one can cancel or invalidate the forms authentication based on my own criteria.
Basically I have a process where the user logs in. After that they get a token. I get the token back after the forms authentication fires upon subsequent requests. What I want to do is then validate that the token hasn't expired against our back end server. If it's expired I need to do something so that they are forced to log back in. My thought was to do something in my OnAuthenticateRequest handler that would get picked up later in the pipeline and force a redirect back to login page or something. Is that possible?
In an ASP.NET MVC application in order to handle custom Authentication and Authorization people usually write custom Authorize attributes. They don't deal with any OnAuthenticateRequest events. That's old school. And by the way if you are going to be doing some custom token authentication why even care about Forms Authentication? Why not replace it?
So:
public class MyAuthorizeAttribute: AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
string token = GetTokenFromHttpContext(httpContext);
if (IsTokenValid(token))
{
// The user has provided a valid token => you need to set the User property
// Obviously here based on the token value you already know which is the
// associated user and potential roles, so you could do additional checks
var identity = new GenericIdentity("john.smith");
var user = new GenericPrincipal(identity, new string[0]);
httpContext.User = user;
return true;
}
// Notice that here we are never calling the base AuthorizeCore method
// but you could call it if needed
return false;
}
private string GetTokenFromHttpContext(HttpContextBase httpContext)
{
// TODO: you know what to do here: get the token from the current HTTP Context
// depending on how the client passed it in: HTTP request header, query string parameter, cookie, ...
throw new NotImplementedException();
}
private bool IsTokenValid(string token)
{
// TODO: You know what to do here: go validate the token
throw new NotImplementedException();
}
}
Now all that's left is to decorate your controllers/actions with this custom attribute instead of using the default one:
[MyAuthorize]
public ActionResult SomeAction()
{
// if you get that far you could use the this.User property
// to access the currently authenticated user
...
}
Is that possible?
This is definitely possible. You could even set your autehtication scheme to None so that forms module isn't there in the pipeline and have only your own module.
However, even if forms is there, your custom module can override the identity set for the current request. Note also that until the forms cookie is issued, forms module doesn't set the identity. This was quite common to use both forms module and the SessionAuthenticationModule - forms does the job of redirecting to the login page and the session auth module handles its own authentication cookie.
This means that you can safely mix the two: the forms module and your own custom module for a similar scenario.
Darin suggests another approach and this of course is valid too. An advantage of an authentication module (versus the authentication filter) is that the authentication module could support other ASP.NET subsystems (web forms / wcf / webapi).