I'm trying to remove specific Set-Cookie header from HttpResponseHeaders in OnActionExecuted method of ActionFilter.
I'm having few issues with that:
I cannot see the way of enumerate headers. The collection is always
empty, even if I see headers in debugger.
Because I cannot
enumerate, I cannot remove specific header. I can only remove all
headers with the same key, but Set-Cookie can have multiple
entries.
Currently I'm removing all cookies, but this is not what I want.
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
HttpResponseHeaders headers = actionExecutedContext.Response.Headers;
IEnumerable<string> values;
if (headers.TryGetValues("Set-Cookie", out values))
{
actionExecutedContext.Response.Headers.Remove("Set-Cookie");
}
base.OnActionExecuted(actionExecutedContext);
}
From the link:
You cannot directly delete a cookie on a user's computer. However, you can direct the user's browser to delete the cookie by setting the cookie's expiration date to a past date. The next time a user makes a request to a page within the domain or path that set the cookie, the browser will determine that the cookie has expired and remove it.
So, how to remove/delete cookie in ASP.NET Web Api at action filter level, just try to set expiration date of cookie to a past date:
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var response = actionExecutedContext.Response;
var request = actionExecutedContext.Request;
var currentCookie = request.Headers.GetCookies("yourCookieName").FirstOrDefault();
if (currentCookie != null)
{
var cookie = new CookieHeaderValue("yourCookieName", "")
{
Expires = DateTimeOffset.Now.AddDays(-1),
Domain = currentCookie.Domain,
Path = currentCookie.Path
};
response.Headers.AddCookies(new[] { cookie });
}
base.OnActionExecuted(actionExecutedContext);
}
Related
I have a question about verifying an Auth Token. Currently, I'm generating my Auth Token at /token and sending it back to the client side where I store it in session storage. To make the process as secure as possible I'm looking to store the token in a HttpOnly and Secure cookie to protect against XSS.
I'm sending the cookie along with a XSRF token where on my client side (Angular) I set the X-XSRF-TOKEN header to the XSRF token value. The browser then automatically send the Auth Cookie back to me where I can read the token.
My main question is how do I extract the token and validate it? Before I was using Authentication Bear auth_token with the [Authorize] attribute but that doesn't work now since the token is in a cookie. I made my own Attribute (see below, but I'm stuck on the line where I actually verify the token. I'm not sure what function to call and pass the token to, to validate it.
public class ValidateToken : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var headers = actionContext.Request.Headers;
CookieHeaderValue authToken = headers.GetCookies().FirstOrDefault();
CookieState authCookie = authToken.Cookies.Where(p => p.Name == "AUTH-TOKEN").FirstOrDefault();
if (authCookie.Value is valid -> need help here) {
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
}
}
}
}
Edit: I've tried the following
actionContext.Request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authCookie.Value);
bool isAuth = base.IsAuthorized(actionContext);
But the RequestContext is already set once OnAuthorization is called and it doesn't update to use the new Header I added.
I'm implementing Angular Project the Service Side I'm using .NET Web API. The Web API is a Stateless, so I'm using Cookie to maintain the Authentication. AuthLogin Controller Checks the Credential is correct, If it is TRUE then it create the Cookie and pass the cookie via HTTP Response. Additionally I added ActionFilterAttribute to validate the User. If any request the Web API received then it triggers the ActionFilterAttribute before entering into the Controller. Their I'm checking, the Request is containing any Cookie and it checks it with the database. Once the Cookie is validated I need to extend the cookie expiry to 30 mins.
My Web API Controller Methods
[HttpPost]
public HttpResponseMessage AuthLogin()
{
serverCookie = new CookieHeaderValue
("Test", "Super");
serverCookie.Expires = DateTimeOffset.Now.AddMinutes(15);
serverCookie.Domain = Request.RequestUri.Host;
serverCookie.Path = "/";
HttpResponseMessage response = Request.CreateResponse(_credential.Item2 ? HttpStatusCode.OK : HttpStatusCode.Unauthorized);
response.Headers.AddCookies(new CookieHeaderValue[] { serverCookie });
return response;
}
[HttpPost]
[Authenticate]
public string SecurePost()
{
return "Success";
}
The ActionFilterAttribute C# Code:
public class AuthenticateAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
Debug.WriteLine("Cookie Life Time Extended Successfully to 30 Mins.");
}
}
My Requirement is to extended the life span of Cookie to 30 Mins and send the information along with the return value. Kindly assist me in this regards.
I was able to extent cookie expiry time in MVC application
HttpCookie cookie = Request.Cookies["MyCookie"];
if (cookie!+null && !cookie.Value.IsEmpty())
{
// Update the cookie expiration
cookie.Expires = DateTime.Now.AddMinutes(Convert.ToInt32(1));
Response.Cookies.Set(cookie);//Request.Cookies.Set(cookie);
}
else
{
}
The HTTP protocol never sends cookie expiration time to the server. So you can not extend cookie expiration time.
Browser will send only cookie name and value to the server. All the other properties like expires, domain, path, httponly can not be accessed once the cookie has been set.
When you assign a Cookie in the response a SetCookie header gets added to the output and that will contain not only the value but also the path and the expires value.
But when the client browser sends the cookie back to the server, it simply includes the cookie name and value only and It does not send path or expires info.
I'm following along Chapter 8 in Pro ASP .NET Web API Security by Badri L., trying to implement basic authentication for a web application that will be consumed by HTTP/JS clients.
I've added the following Authentication Handler to my WebAPI project:
public class AuthenticationHandler : DelegatingHandler
{
private const string SCHEME = "Basic";
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
System.Threading.CancellationToken
cancellationToken)
{
try
{
// Request Processing
var headers = request.Headers;
if (headers.Authorization != null && SCHEME.Equals(headers.Authorization.Scheme))
{
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
// etc
When I decorate methods in my API with [Authorize] and set a breakpoint at the if statement above, headers.Authorization is null upon the first request. If I continue on this break, the if statement gets hit again, this time with headers.Authorization.Scheme as "Negotiate", instead of "Basic":
I have registered my Handler in WebApiConfig:
config.MessageHandlers.Add(new AuthenticationHandler());
But I'm at a loss as to why the Authorize attribute is not respecting basic authentication, or why - since the scheme is not "basic" and the if() in my handler returns false - I'm getting data from my API controller when I should be getting 401 Unauthorized.
I have not specified any authenticationType in my web.config.
Any idea what I'm doing wrong?
Edit: Full Handler:
public class AuthenticationHandler : DelegatingHandler
{
private const string SCHEME = "Basic";
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
System.Threading.CancellationToken
cancellationToken)
{
try
{
// Request Processing
var headers = request.Headers;
if (headers.Authorization != null && SCHEME.Equals(headers.Authorization.Scheme))
{
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string credentials = encoding.GetString(Convert.FromBase64String(headers.Authorization.Parameter));
string[] parts = credentials.Split(':');
string userId = parts[0].Trim();
string password = parts[1].Trim();
// TODO: Authentication of userId and Pasword against credentials store here
if (true)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, userId),
new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.Password)
};
var principal = new ClaimsPrincipal(new[] {new ClaimsIdentity(claims, SCHEME)});
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
HttpContext.Current.User = principal;
}
}
var response = await base.SendAsync(request, cancellationToken);
// Response processing
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(SCHEME));
}
return response;
}
catch (Exception)
{
// Error processing
var response = request.CreateResponse(HttpStatusCode.Unauthorized);
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(SCHEME));
return response;
}
}
}
When I decorate methods in my API with [Authorize] and set a breakpoint at the if statement above, headers.Authorization is null upon the first request.
This is expected. This is how it is supposed to work. Browser will show the popup to get credentials from the user only when it receives a 401. Subsequent request will have the authorization header with credentials in the basic scheme.
If I continue on this break, the if statement gets hit again, this time with headers.Authorization.Scheme as "Negotiate", instead of "Basic":
Yes, as answered by Dominick (is it Dominick?), you have Windows Authentication enabled and that is the reason for you getting the Negotiate scheme back from the browser. You must disable all authentication methods either in the config or using IIS manager.
But I'm at a loss as to why the Authorize attribute is not respecting basic authentication, or why - since the scheme is not "basic" and the if() in my handler returns false - I'm getting data from my API controller when I should be getting 401 Unauthorized.
Authorize attribute knows nothing about basic authentication. All it cares is if the identity is authenticated or not. Since you have anonymous authentication enabled (I guess that is the case), Authorize attribute is happy and there is no 401 for the message handler response handling part to add the WWW-Authenticate response header indicating that web API expects credentials in the Basic scheme.
Looks like you have Windows authentication enabled for your app in IIS. Disable all authentication methods in config (system.web and system.webServer) and allow anonymous since you do your own authentication in the message handler.
i think you need to register the handler on global.asa
This article looks good: http://byterot.blogspot.com.br/2012/05/aspnet-web-api-series-messagehandler.html
your global.asa.cs would have something like this:
public static void Application_Start(GlobalFilterCollection filters) {
//...
GlobalConfiguration.Configuration.MessageHandlers.Add(new AuthenticationHandler());
}
I am working with MVC 3 and I have just implemented a wrapper for the FormsAuthenticationService.
Something similar to the following.
public void SignIn(string username, bool createPersistantCookie)
{
if (string.IsNullOrEmpty(username))
throw new ArgumentException("Value Cannot be null or empty", "username");
FormsAuthentication.SetAuthCookie(username, createPersistantCookie);
}
Reluctantly, I have gotten this to work, but now I am not quite sure how to get the information that I have stored.
Once the user is in my system, how can I now safely retrieve this information if I need to grab their UserID out of the database?
Based on the additional information provided, you want to store additional data with the FormsAuthentication ticket. To do so, you need first create a custom FormsAuthentication ticket:
Storing Data
Grab the current HttpContext (not worrying about testability)
var httpContext = HttpContext.Current;
Determine when the ticket should expire:
var expires = isPersistent
? DateTime.Now.Add(FormsAuthentication.Timeout)
: NoPersistenceExpiryDate; // NoPersistenceExpiryDate = DateTime.MinValue
Create a new FormsAuthentication ticket to hold your custom data.
var authenticationTicket = new FormsAuthenticationTicket(
1,
username,
DateTime.Now,
DateTime.Now.Add(FormsAuthentication.Timeout),
isPersistent,
"My Custom Data String"); //Limit to about 1200 bytes max
Create your HTTP cookie
new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(authenticationTicket))
{
Path = FormsAuthentication.FormsCookiePath,
Domain = FormsAuthentication.CookieDomain,
Secure = FormsAuthentication.RequireSSL,
Expires = expires,
HttpOnly = true
};
And finally add to the response
httpContext.Response.Cookies.Add(cookie);
Retrieving Data
Then you can retrieve your data on subsequent requests by parsing the stored authentication ticket...
Again, grab current HttpContext
var httpContext = HttpContext.Current
Check to see if the request has been authenticated (call in Application_AuthenticateRequest or OnAuthorize)
if (!httpContext.Request.IsAuthenticated)
return false;
Check to see if you have a FormsAuthentication ticket available and that it has not expired:
var formsCookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
if (formsCookie == null)
return false;
Retrieve the FormsAuthentication ticket:
var authenticationTicket = FormsAuthentication.Decrypt(formsCookie.Value);
if (authenticationTicket.Expired)
return false;
And finally retrieve your data:
var data = authenticationTicket.UserData;
You haven't actually stored a user id in the database. All the code that you've written does is store an authentication cookie on the users computer, either as a session cookie (not persistent) or as a persistent one.
When your page refreshes, it will get the cookie automatically, decode it, and populate the IPrincipal object which you access from the User.Current property of your controller.
I have logout handler which used to work fine:
public void ProcessRequest(HttpContext context)
{
//// Sign out
System.Web.Security.FormsAuthentication.SignOut();
//// Clear Session
if (context.Session != null)
{
context.Session.Clear();
}
/// Expire all the cookies so browser visits us as a brand new user
List<string> cookiesToClear = new List<string>();
foreach (string cookieName in context.Request.Cookies)
{
HttpCookie cookie = context.Request.Cookies[cookieName];
cookiesToClear.Add(cookie.Name);
}
foreach (string name in cookiesToClear)
{
HttpCookie cookie = new HttpCookie(name, string.Empty);
cookie.Expires = DateTime.Today.AddYears(-1);
context.Response.Cookies.Set(cookie);
}
context.Response.Redirect("~/default.aspx");
}
}
Once I added "domain" parameter to the authentication section of web.config:
<forms timeout="50000000"
loginUrl="~/login"
domain='mysite.com'/>
... it is no longer logging the user out - after it redirects to "~/default.aspx" I can still see the user logged in (I put a breakpoint to Load event of that page and check HttpContext.Current.User.Identity.IsAuthenticated, and its still = true).
Then I remove "domain='mysite.com'" and it logs the user out without problems.
I do need to specify the domain because I added a subdomain with its own application but I want it to share authentication cookie.
Any ideas are highly appreciated!
When I recreate cookies to expire, I need to specify the domain:
cookie.Domain = FormsAuthentication.CookieDomain;
That solves the problem.
Please specify domain =".mysite.com"