FrankO
FrankO

Reputation: 2562

Setting Cookie values in HttpAuthenticationContext for IAuthenticationFilter

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

Answers (2)

Maxime Rossini
Maxime Rossini

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

uzul
uzul

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). &lt;code&gt; Set-Cookie: state1:name1=value1&amp;amp;name2=value2; state2:name3=value3&amp;amp;name4=value4; domain=domain1; path=path1; &lt;/code&gt;</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());
                    }
                }
            }
        }
    
    • In the end you feel a little stupid spending so much time looking for an extension method, when you realize that adding a cookie in webapi2 is simply done in a line of code:

headers.TryAddWithoutValidation("Set-Cookie", new CookieHeaderValue("key", "value")); //where headers is a HttpResponseHeaders

Upvotes: 1

Related Questions