I'm unit testing my WebAPI using HttpClient and self hosted api. I have a custom MessageHandler setting principal based on API key sent by the client. My controller is protected with [Authorize] attribute, but my test call gets in, because User.Identity is filled with my windows username. How can I make sure user isn't set when making an HttpClient call from my tests?
Your custom MessageHandler should set the User to null if the API key is not provided or invalid:
IPrincipal principal = null;
if (IsValidApiKey(someKey))
{
// The provided API key is valid => we populate the principal
principal = ...
}
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
Related
I'm making my own system to authenticate jwt tokens in certain scenarios.
When I have properly validated the token, I have
var userIdentity = await user.CreateIdentityAsync(DefaultAuthenticationTypes.ExternalBearer);
owinContext.Authentication.User = new System.Security.Claims.ClaimsPrincipal(userIdentity);
owinContext.Authentication.SignIn(userIdentity);
System.Web.HttpContext.Current.User = owinContext.Authentication.User;
await next()
but that doesn't seem to fix authentication which still fails at - I believe - the Asp.Net Mvc level. Because I know it uses HttpContext I try adding this before calling next()
HttpContext.Current.User = new GenericPrincipal(userIdentity, new string[0]);
This gets me further along but I still seem to be getting an an authorization error it would seem (by searching source for the message that I get and where its used) to be coming from the Web Api [Authorize] attribute.
I'm hitting a wall as far as tracing through the .net source code. The only way I should be getting this message is if IsAuthorized returns false. But there are no roles nor users specified (it's just plain [Authorize]) and before heading off to the next() I can stop the debugger and check that yes there is a user identity, and yes it IsAuthorized.
I've overridden the AuthorizeAttribute so as to place breakpoints and can see that by the time it is called however, my actionContext is associated with a completely different identity with IsAuthorized == false. Which in turn makes me wonder if I'm signing in the user identity wrong
So... am I doing this correctly? What should I be doing?
I have never undertstood why but in my case, i have need to valid the ticket after signing in:
var userIdentity = await user.CreateIdentityAsync(DefaultAuthenticationTypes.ExternalBearer);
ctx.Authentication.SignIn(userIdentity);
AuthenticationTicket ticket = new AuthenticationTicket(userIdentity, null);
ctx.Validated(ticket);
Edit
I'm not really in the same context. In my case, I have a custom authentication provider inheriting of Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationProvider :
public class CustomBearerAuthenticationProvider:OAuthBearerAuthenticationProvider
{
public CustomBearerAuthenticationProvider() : base()
{
this.OnValidateIdentity = (context) => Task.Run(() =>
{
var identity = this.CreateApplicationIdentity(user);
context.OwinContext.Authentication.SignIn(identity);
AuthenticationTicket ticket = new AuthenticationTicket(identity, null);
context.Validated(ticket);
});
}
}
context is of type : Microsoft.Owin.Security.OAuth.OAuthValidateIdentityContext
I've been running in circles trying to find an answer to this, and I can't seem to make any progress.
All I want to do, is check for a correct username and password combo, then GIVE the user authorization to access MVC actions decorated with the [Authorize] tag.
public ActionResult DoLogin(PageInitModel model)
{
EF_db db = new EF_db();
string saltPassword = getSaltedPasswordEncryption(model.UserName, model.Password);
var user = (from s in db.Users
where s.Username == model.UserName
&& s.Password == saltPassword
select s).FirstOrDefault();
if(user == null)
{
model.LoginFail = true;
return View("Login", model);
}
else
{
//
// give the user some magical token to access [Authorize] actions here
//
return RedirectToAction("Index", "Menu");
}
}
Above is the login action (called from a basic form), and below would be one of the actions I would like to restrict access to:
public class MenuController : Controller
{
[Authorize]
public ActionResult Index()
{
var pageInitModel = new PageInitModel();
return View("Menu",pageInitModel);
}
}
I would like to keep track of the users myself (in my own tables), because there are many additional attributes I would like to track. I'm not sure if I need to write a custom AuthorizeAttribute, or what, but I can't seem to make any headway.
Any help pointing me in the right direction is greatly appreciated.
Thanks!
You should look into ASP.NET Identity. Note this was introduced in ASP.NET 5 (?) and replaces some older frameworks Microsoft had like Basic Membership etc.
Honestly you really don't want to roll your own. ASP.NET Identity does exactly what you describe right out of the box. Keep in mind there are two distinct concepts Authentication and Authorization.
Authentication is verifying the user is who he says he is.
Authorization is restricting access to only users who are "allowed".
There are many ways to structure Authorization but I assume Role based Authorization will meet your need. You will define multiple roles, say User, Admin, Moderator, Admin, etc.
You then restrict access to actions based on the role. You can even make roles overlap and allow a single user to have multiple roles. The end result is that once a user logs in their role determines what they can do via the authorize tags.
[Authorize(Roles="Admin")]
If the user is not logged in, they will be redirected to the login form to AUTHENTICATE ("Um I don't know who you are". Once authenticated if they are not authorized they will still be restricted from this action ("Oh I know who you are but you are not allowed to do this".
To round it out you can also have anonymous actions which mean no authentication is required and Authorize actions which are not limited to a specific role (any authenticated user is allowed but not unauthenticated users).
The fact that even with a basic [Authorize] you are having issues leads be to believe there is some configuration problems in even the Authentication. I recommend going through a tutorial building an example app like this one:
http://blogs.msdn.com/b/webdev/archive/2013/10/20/building-a-simple-todo-application-with-asp-net-identity-and-associating-users-with-todoes.aspx
As I understood you have your users information and you may want to use the authentication by custom code. But to actually login the user you must create a session of the user (Cookie session) for further authentication of requests.
You have already verified the username and password. now you've to create a session cookie using form authentication. Here's answer to your first problem:
// give the user some magical token to access [Authorize] actions here
// User name and password matches
if (userValid)
{
FormsAuthentication.SetAuthCookie(username, false);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
The Authorize attribute will then check if the request coming for the action is authenticated. This attribute internally checks if a session cookie is valid and authenticated.
Also this attribute not only checks for authentication but also can verify authorization. So you can use custom roles as well with Authorize attribute to restrict users to accessing specific views/actions.
It was pointed out to me that I needed to look into cookies or existing membership providers, so I looked around in FormsAuthenticationTickets and rolled my own solution that I would love feedback on.
I ended up creating a new ticket when the login was successful with the FormsAuthenticationTicket class (see below).
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
user.Username,
DateTime.Now,
DateTime.Now.AddHours(2),
false,
userData,
FormsAuthentication.FormsCookiePath);
// Encrypt the ticket.
string encTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie.
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
Then I wrote a custom ActionFilter, so I could make my own method decorators (or whatever they're called).
namespace MyApp.Filters
{
public class CustomLoginFilter : ActionFilterAttribute, IActionFilter
{
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
// Retrieves the cookie that contains your custom FormsAuthenticationTicket.
HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
// There's actually a cookie
// Decrypts the FormsAuthenticationTicket that is held in the cookie's .Value property.
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket != null && user != null && data == authTicket.UserData)
{
// Everything looks good
this.OnActionExecuting(filterContext);
}
else
{
//log bounceback - someone screwed with the cookie
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "Login" },
{ "action", "Index" }
});
}
}
else
{
//log bounceback - not logged in
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "Login" },
{ "action", "Index" }
});
}
}
}
}
I'll likely play with it more, but it seems to work fine for now.
Thanks for the help.
I'm developing an asp.net MVC website with the following requirements:
Develop pages for Admin and Users, these pages must be accessed
based on logged in user role: Admin or User
The website supports login only, You will call a PHP API which resides on an external website, it returns a JSON as a result that includes id, username, and role (admin, user)
You may save the result of returned json on a session to be used in your pages but this data must disappear after logout or session expiration.
I know how to develop the calling HTTP stuff and processing json, but I'm not familiar with authorization and authentication stuff, nor with using membership providers, I searched a lot and at first I thought of using SimpleMembership but I found that won't work since it depends on SQL queries and in my case I'm not going to use any type of databases.
I heard about asp.net identity but I'm not sure how to use it or if it's for my case or not, I searched again and I couldn't find any resource to help me achieve authentication and authorization for my case
I'm asking for your help to help me out and point me in the right direction
Thank you for your help
There is an example of using OAuth separated http auth API:
http://www.asp.net/web-api/overview/security/external-authentication-services
Yes, this example depends on some specified http API..
But in case when you have some another JSON/XML RPC API you can try to create your own feature like a:
public class ExternalAuthAPIClient {
public User Auth(string username, string password) { .... }
}
And use it in your AuthController in the method Login
BUT! This approach requires a lot of side changes.. where to store your user.. then create custom AuthenticateAttribure ... etc.
The better solution is to create oAuth supported API on your PHP side and use it with ASP.NET Identity.
I finally found a solution,I didn't need to use any membership providers since my website supports only login and via an API,I wrote the following code,this one is in AccountController :
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel login, string returnUrl)
{
if (!ModelState.IsValid)
{
ViewBag.Error = "Form is not valid; please review and try again.";
return View(login);
}
//Call external API,check if credentials are valid,set user role into userData
string userData="Admin";
var ticket = new FormsAuthenticationTicket(
version: 1,
name: login.Username,
issueDate: DateTime.Now,
expiration: DateTime.Now.AddSeconds(HttpContext.Session.Timeout),
isPersistent: false,
userData: userData);
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
HttpContext.Response.Cookies.Add(cookie);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", userData);
}
Then decorate admin/user controller with Authorize attribute like this:
[Authorize(Roles = "admin")]
public class AdminController : Controller
Then add the following code in Global.asax :
public override void Init()
{
base.PostAuthenticateRequest += Application_PostAuthenticateRequest;
}
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
var decodedTicket = FormsAuthentication.Decrypt(cookie.Value);
var roles = decodedTicket.UserData;
var principal = new GenericPrincipal(HttpContext.Current.User.Identity, roles);
HttpContext.Current.User = principal;
}
}
I may be missing something obvious here. I'm new to both MVC and Web API, so I'm working on keeping my head above water.
I have an MVC application that interfaces with a Web API service. Authentication will be handled by a login service developed internally. When working, the MVC client should check if the current user is authenticated. If they're not, then it will redirect to this login service, which is supposed to authenticate the user and update the current user. I then need to be able to access this identity from the Web API service.
I'm operating under the assumption that the current principal (set via Thread.CurrentPrincipal or HTTPContext.Current.User) in the MVC application should be available in my Web API service, but whenever I try to access it from the service, the principal is empty. I've tried accessing the principal from the service using all of the following options, but it's always empty:
RequestContext.Principal
User.Identity
HttpContext.Current.User
Thread.CurrentPrincipal
Here's the basic idea of my code:
MVC Controller:
public ActionResult Index() {
//Just create a test principal here to see if it's available in the service
IPrincipal temp = new GenericPrincipal(new GenericIdentity("myUserName"), new string[]{});
Thread.CurrentPrincipal = temp;
using (var client = new HttpClient()) {
client.BaseAddress = new Uri("myServiceAddress");
HttpResponseMessage response = client.GetAsync("resourceString")).Result;
...Code to deal with result
}
}
Web API Controller:
[HttpGet]
public HttpResponseMessage MyAction() {
if (User.Identity == null || !User.Identity.IsAuthenticated) {
//So sad
} else {
//Do some work
}
}
The current principal is always empty, regardless of how I try to access it.
I think that you're going to need to set both the thread and context principal. Here's what I'm doing:
private static void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null) {
HttpContext.Current.User = principal;
}
}
Part way down This Article it says:
If your application performs any custom authentication logic, you must set the principal on two places:
Thread.CurrentPrincipal. This property is the standard way to set the thread's principal in .NET.
HttpContext.Current.User. This property is specific to ASP.NET.
While implementing a custom IAuthenticationFilter, I end up having the below code:
public class CustomAuthenticationAuthenticationFilter : ActionFilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
ClaimsPrincipal principal;
// logic to retrieve the claims principal
filterContext.HttpContext.User = principal;
filterContext.Principal = principal;
Thread.CurrentPrincipal = principal;
}
}
}
For a reason I can't grasp, I have 2 issues with this implementation:
The filterContext.HttpContext.User.Identity.IsAuthenticated is always false. Therefore, I always re-run my custom logic to authenticate users.
As soon as I go to a page that has a controller decorated with another Authentication action filter attribute (e.g. Authorize), the security principal is "lost" and the re-show the the "register" and "Login" links.
I strongly believe that I am not persisting the identity correctly, but I can't figure out the right way. Does anyone have a clue?
Probably you should save reference of your prinicipal to a cookie. Or you should run your logic to retrieve the claims principal for every request.
I don't familiar with claims-based identity, but anyway
filterContext.HttpContext.User = principal;
filterContext.Principal = principal;
Thread.CurrentPrincipal = principal;
is not enough to save autorization in web-application, because filter context and http context is exists for one request. I'm not sure about Thread, but for me it is not a good place to store principal in web application.