Reputation: 2562
I have a need to read/write cookies during the authentication step of a WebAPI pipeline. I have created a custom filter for this.
In an attempt to comply with self-hosting concepts, what would be a safe way to access and write cookies out to the client? Rick Strahl commented that if we use HttpContext.Current.Response.Cookies.Add()
, and my application is self-hosted, the context may/will not exist.
So how would I write a cookie out to the client using HttpAuthenticationContext
and still be self-host safe?
Upvotes: 3
Views: 3509
Reputation: 3881
You can't access authContext.ActionContext.Response
from within IAuthenticationFilter.AuthenticateAsync()
. Well, actually you can, but only to set a new response and shortcut the rest of the pipeline.
I had the same problem (needed to set a cookie after a successful authentication) and solved it by implementing IActionFilter
in addition to IAuthenticationFilter
:
async Task<HttpResponseMessage> IActionFilter.ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
// Process the request pipeline and get the response (this causes the action to be executed)
HttpResponseMessage response = await continuation();
// Here you get access to:
// - The request (actionContext.Request)
// - The response (response) and its cookies (response.Headers.AddCookies())
// - The principal (actionContext.ControllerContext.RequestContext.Principal)
return response;
}
See: Set cookie from Web Api 2 IAuthenticationFilter AuthenticateAsync method
Upvotes: 2
Reputation: 1136
HttpAuthenticationContext authContext;
authContext.ActionContext.Response.Headers.AddCookies(/*cookies */);
edit2
HttpAuthenticationContext authContext;
var myCookie = new CookieHeaderValue("key", "value")
authContext.ActionContext.Response.Headers.Add("Set-Cookie", myCookie.ToString());
edit
AddCookie is an extension method located in System.Net.Http.Formatting.dll (as of version v5.2.2.0), and the extension method is declared by static class HttpResponseHeadersExtensions, located in namespace System.Net.Http.
If you cannot find the extension method, try locate HttpResponseHeadersExtensions class.
If you cannot find HttpResponseHeadersExtensions class, try upgrade Web Api 2 libraries. The most efficient way to upgrade all nuget packages of WebApi2 of every projects (for those who hate upgrading nuget packages like me), is to do a global search/replace on .config files of term 'version="x.x.x" targetFramework="net45"' (where x.x.x is an older version replaced by 'version="5.2.2" targetFramework="net45"'
In worst case scenario if your boss or your mom won't let you upgrade the nuget packages, you can always adopt a rebel attitude and decompile the code containing AddCookie, it appear to look like this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Http.Headers;
using System.Net.Http.Properties;
using System.Web.Http;
namespace System.Net.Http
{
/// <summary> Provides extension methods for the <see cref="T:System.Net.Http.Headers.HttpResponseHeaders" /> class. </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class HttpResponseHeadersExtensions
{
private const string SetCookie = "Set-Cookie";
/// <summary> Adds cookies to a response. Each Set-Cookie header is represented as one <see cref="T:System.Net.Http.Headers.CookieHeaderValue" /> instance. A <see cref="T:System.Net.Http.Headers.CookieHeaderValue" /> contains information about the domain, path, and other cookie information as well as one or more <see cref="T:System.Net.Http.Headers.CookieState" /> instances. Each <see cref="T:System.Net.Http.Headers.CookieState" /> instance contains a cookie name and whatever cookie state is associate with that name. The state is in the form of a <see cref="T:System.Collections.Specialized.NameValueCollection" /> which on the wire is encoded as HTML Form URL-encoded data. This representation allows for multiple related "cookies" to be carried within the same Cookie header while still providing separation between each cookie state. A sample Cookie header is shown below. In this example, there are two <see cref="T:System.Net.Http.Headers.CookieState" /> with names state1 and state2 respectively. Further, each cookie state contains two name/value pairs (name1/value1 and name2/value2) and (name3/value3 and name4/value4). <code> Set-Cookie: state1:name1=value1&amp;name2=value2; state2:name3=value3&amp;name4=value4; domain=domain1; path=path1; </code></summary>
/// <param name="headers">The response headers</param>
/// <param name="cookies">The cookie values to add to the response.</param>
public static void AddCookies(this HttpResponseHeaders headers, IEnumerable<CookieHeaderValue> cookies)
{
if (headers == null)
{
throw Error.ArgumentNull("headers");
}
if (cookies == null)
{
throw Error.ArgumentNull("cookies");
}
foreach (CookieHeaderValue current in cookies)
{
if (current == null)
{
throw Error.Argument("cookies", Resources.CookieNull, new object[0]);
}
headers.TryAddWithoutValidation("Set-Cookie", current.ToString());
}
}
}
}
headers.TryAddWithoutValidation("Set-Cookie", new CookieHeaderValue("key", "value")); //where headers is a HttpResponseHeaders
Upvotes: 1