I am using a WebAPI service in my webapplication. In this service is used for all account functions (/api/account/login, /api/account/logout, ...). Within the same webroot I have a website which uses this webAPI service to communicate with the backend system. So from my C# code i'm calling the IsLoggedIn function in my WebAPI which returns true when I'm logged in. This is working great.
[HttpGet]
public bool IsLoggedIn()
{
return (WebSecurity.IsAuthenticated);
}
In my arearegistration i added the following code:
context.Routes.MapHttpRoute("Account", "api/account/{action}/{id}", new { Controller = "Account", id = RouteParameter.Optional });
And the follwing GlobalConfiguration:
GlobalConfiguration.Configuration.Filters.Add(new ExceptionHandlingAttribute());
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Add(new JsonMediaTypeFormatter());
The custom ExceptionHandlingAttribute checks if the thrown exception is of a specific type and returns a custom ReasonPhrase, so nothing special.
When I'm logged in and call the function from javascript (jQuery) or in Fiddler the IsLoggedIn function returns false. What should I add to my jQuery call to make sure the the right user is still found in the WebAPI? This is happening for POST and GET calls.
Please help :)
It will always be false as REST is stateless, each request knows nothing about previous requests.
You could enable Session State by doing something like this
http://www.strathweb.com/2012/11/adding-session-support-to-asp-net-web-api/
A better approach would be to have your Login call return a token which is then passed on subsequent calls to identify the user. Here's an example:
How to use OAuth 2 - OAuth 2 C# example
Related
I am writing a .net Core 2.0 Web API controller that performs file upload using a multipart type http request and is based in the streaming technique described here.
At this point I have to say that I if you want you can skip the next two paragraphs that describe the reason that led me to the need for a solution to the problem that is described after the two paragraphs.
I initially thought of authenticating the user by sending authentication data in the first section of the multipart request and validating the user as soon as the user data are read, by contacting the database and performing the proper request. However, I thought that since this is a streaming request, any delay in authenticating the user using the database, would delay reading the stream with the file. This would cause the TCP receive buffer to fill with data (possibly also increase its size) and would defeat the purpose of streaming the file (instead of buffering), since memory consumption for this connection would increase.
In order to get rid of this issue I thought of using a 2 step authentication using JWTs. The Web API user will first perform a request and ask for a JWT. Then it would use this JWT in the upload request. As I understand it, JWT authentication should be much faster than a database request since it is performed by validating the JWT using the key stored in the server, so the previous issue should not exist.
I implemented the JWT authentication for the upload request following this very good description from Auth0 and it worked just fine. More specifically the controller has an [Authorize] attribute that forces Web API to to authenticate the user by validating the JWT before the controller is executed.
The problem I am facing is that with the above proposed solution when an unauthorized user tries to upload a file the Controller action is never called. The Authentication engine returns an Unathorized (401) response to the user and lets the user continue sending file data. The last part is my problem. I would like unauthorized users, which are probably attackers, to receive the 401 response and then have their connection terminated.
So, what I want is to keep the authentication/authorization part as it already works and also terminate the user connection after sending the 401 response. I know (and have also tested it) that from inside a controller action method an http connection can be terminated by calling
HttpContext.Abort();
I suspect that by using a filter, I could do what I want but I am not very familiar with filters so that is why I am asking.
We can achieve that by using an IAuthorizationFilter.
Inside it, we gonna set an special ActionResult called AbortUnauthorizedConnectionResult and in that we set the Status Code to 401 and Content-Length to 0 and by calling Response.Body.Flush() we make sure it's sent to client before we call Abort().
Here we have an AuthorizationFilter called AbortUnauthorizedConnections:
class AbortUnauthorizedConnections : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (context.HttpContext.User?.Identity == null || !context.HttpContext.User.Identity.IsAuthenticated)
{
// by setting this we make sure the pipe-line will get short-circuited.
context.Result = new AbortUnauthorizedConnectionResult();
}
}
}
And because we have inherited from Attribute we can use it on the upload action like this:
[Authorize]
[AbortUnauthorizedConnections]
public async Task<IActionResult> UploadFile()
{
// we do whatever we want.
}
Here is the code for AbortUnauthorizedConnectionResult:
class AbortUnauthorizedConnectionResult : StatusCodeResult
{
public AbortUnauthorizedConnectionResult() : base(401)
{
}
public override async Task ExecuteResultAsync(ActionContext context)
{
await base.ExecuteResultAsync(context);
context.HttpContext.Response.Headers.Add("Content-Length", "0");
context.HttpContext.Response.Body.Flush();
context.HttpContext.Abort();
}
}
Now if an unauthorized user try to access this controller will get 401 and it's connection gets aborted.
This is the solution I actually implemented due to its simplicity, following #Tratcher's advice:
First, I deleted the [Authorize] attribute from my Controller Action method. Then I wrote the beginning of my Controller Action method as follows:
public async Task<string> UploadFile()
{
if (!(await HttpContext.AuthenticateAsync()).Succeeded)
{
HttpContext.Response.StatusCode = 401; //Unauthorized
HttpContext.Response.Headers.Add("Content-Length", "0");
HttpContext.Response.Body.Flush();
HttpContext.Abort();
return null;
}
...
}
I am new to RESTful services using WebApi. I have a front-end web application that uses FormsAuthentication to authenticate users. I am able to use the User.Identity property without any problems in my MVC controller methods.
However, I want to use Angular to make Ajax calls from the browser to the Restful methods in WebApi. The problem occurs with the user principal in these methods - HttpRequestMessage.GetUserIdentity() always returns null. By contrast, Thread.CurrentPrincipal in these methods correctly returns the currently authenticated user identity. My WebApi controller is decorated with the Authorize attribute.
What am I missing that is stopping GetUserIdentity() from working? Here is my controller.
[Authorize]
public class CategoryController : ApiController
{
public IEnumerable<ICategoryJson> Get(HttpRequestMessage request)
{
var user = request.GetUserPrincipal(); // returns null
var user1 = System.Threading.Thread.CurrentPrincipal; // returns authenticated user identity
return null;
}
}
And here is my Ajax call.
$http.get("/api/Category", config).then(function (response) {
Array.prototype.push.apply(service.list, response.data);
service.listLoading = false;
});
MVC controller inherits from a different base class so that's why it works in the MVC controller and not the Web API.
In Web API 2 you can use RequestContext.Principal or as you have used the Thread.CurrentPrincipal within your controller action to get the users Identity.
I don't think this issue is related to the ajax or angular call. you can try calling the same MVC controller action from the angular code and it should still return the user's identity.
I have created 2 very simple methods:
[Authorize]
[HttpGet]
public string getUser()
{
return User.Identity.Name;
}
[HttpPost]
public bool SignIn(Credentials cred)
{
var user = userRepository.ValidateUser(cred);
if (user != null)
{
if (user.IsActive)
{
FormsAuthentication.SetAuthCookie(userRepository.GetUserIdByEmail(cred.Email).ToString(), cred.RememberMe);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
user.UserId.ToString(),
DateTime.UtcNow,
DateTime.UtcNow.AddDays(Convert.ToInt32(ConfigurationManager.AppSettings["CookieTimeoutInDays"])),
true,
"MyTicket",
FormsAuthentication.FormsCookiePath);
//Encrypt the ticket.
string encTicket = FormsAuthentication.Encrypt(ticket);
//Create the cookie.
HttpCookie mycookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
// Set the cookie's expiration time to the tickets expiration time
if (ticket.IsPersistent)
mycookie.Expires = ticket.Expiration;
Response.AddHeader(FormsAuthentication.FormsCookieName, encTicket);
return true;
}
else
return false;
}
else
{
return false;
}
}
I put these functions in an API controller and a normal controller (the only line thats different is HttpContext.Current.Response.AddHeader(FormsAuthentication.FormsCookieName, encTicket); when its in the api controller). When I authenticate with the normal controller and pass the same cookie back to call getUser() it works, but when I do it to the API controller it does not work..I am using mobile devices to call both of these controllers, not a browser. Now I understand API controller usually uses basic authentication by passing in username and password in the headers in each call, but is there anything wrong with doing it from a normal controller? What are the advantages of using the asp.net web API over just a normal controller?
When I authenticate with the normal controller and pass the same cookie back to call getUser() it works, but when I do it to the API controller it does not work
I'm not sure why that's the case. If you're using the normal templates, note that your ApiController will live under an /api route, and note that the action name won't be part of the URL. If your WebApiConfig says:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
and your controller class is named FooController, then your ApiController URLs will be something like:
http://:33504/api/Foo
(for both the GET and the POST).
Using cookies with Web API generally isn't the best approach, but it is possible if you have a particular need for it.
There are a couple of other places you might be getting tripped up:
You're generating a forms auth cookie twice. The SetAuthCookie line is doing it once, and putting it in a cookie header. Then, the Response.AddHeader is doing it again, putting it in a custom header (not a cookie header).
When you say Response.AddHeader(FormsCookieName) I think you meant: Response.SetCookie(myCookie). Your current code is adding a custom header named FormsCookieName; it's not adding a cookie (in the Set-Cookie header) with that cookie name.
With Web API, using HttpContext.Current.Response isn't generally recommended. Instead, consider returning HttpResponseMessage and setting the header properties on that object.
Now I understand API controller usually uses basic authentication by passing in username and password in the headers in each call, but is there anything wrong with doing it from a normal controller?
When you use Web API, you're generally doing REST and cookies don't fit very well with the hypermedia ideas there. If you're not doing hypermedia/REST, then I suppose you could use cookies, though again it generally isn't the best fit.
What are the advantages of using the asp.net web API over just a normal controller?
Web API gives you a self-host story and the ability to do content negotiation and a nice HTTP programming model. MVC was more designed around HTML specifically (not other content types). If you're returning HTML, MVC probably makes sense. If nothing in your app returns HTML, Web API is probably a better fit.
For this one controller, though, I'd do whatever you do in the rest of your app. (I wouldn't pick a different framework for just this one controller.)
i have a web method that check user in data base via a jquery-ajax method i wanna if client exists in db i create a cookie in client side with user name but i know that response is not available in staticmethod .how can i create a cookie in a method that call with jquery ajax and must be static. its my code that does not work cuz response is not accesible
if (olduser.Trim() == username.Trim() && password.Trim()==oldpass.Trim())
{ retval =olduser;
HttpContext context = HttpContext.Current;
context.Session[retval.ToString()] = retval.ToString();
HttpCookie cook = new HttpCookie("userath");
cook["submituser"] = "undifiend";
Response.Cookies.Add(cook);
}
You can access the Response object in the same way you are accessing the Session object from the current HtppContext.
Your code should end like this:
context.Response.Cookies.Add(cook);
You could pass the HttpContext into the static method from the Web Method that the AJax call first enters.
EDIT: or, don't use a static method. Either way, the HttpContext will be available from the instanced Web Method that the Ajax call sees via [WebMethod] annotation.
First make an ajax call. You can read this great tutorial 5 Ways to Make Ajax Calls with jQuery
Second get the server respond. For example if the callback was '1', it means you should set the cookie and if it was '0' you should not.
At Last you can easily set the cookie using this jquery plugin: jquery.cookie.
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.