Rejaie
Dec 18, 2017
  4478
(2 votes)

Web API + Service API + Cookie Auth Gotcha

I ran into an interesting problem while on a past project this year - when we turned on Service API via OWIN, my authorized Web API endpoints were returning a 401. We were using cookie authentication for the Web API endpoints. This baffled me a bit until I peeled back the code for Service API.

When one of the Service API's initialization modules executes, it bypasses the default host authentication by calling SupportDefaultHostAuthentication on the Global HTTP Configuration. This explains why my API endpoints weren't recognizing the logged in user's cookie. I came up with a workaround that ignored this call and used cookie authentication instead. See below - during this project, we were using Episerver 10.9.1, and Service API 3.0.1:

  • Add the following code to your OWIN startup. This code ultimately re-adds cookie auth to the Web API filter pipeline

    GlobalConfiguration.Configuration.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ApplicationCookie));
  • In your API controller, decorate your controller or action with the System.Web.Http's Authorize Attribute

After I made these changes, I was able to access my authorized Web API endpoints. As a quick note, if you do use cookie authentication for your Web API endpoints, you may want to send an antiforgery token to protect against XSRF attacks. In this project, I sent the antiforgery token as a header and created a filter attribute that validated the token against the logged in user's cookie. In case anyone is interested, here was my setup:

  • In my master layout, I included a global antiforgery token. I placed this right after the <body> tag, but you can place this anywhere.

  • In my client side code, I sent the antiforgery token as a header. I called the __RequestVerificationToken.

  • On the server side, I created an attribute called UserValidateAntiForgeryTokenAttribute that inherited from FilterAttribute and IAuthorizationFilter. In the ExecuteAuthorizationFilterAsync method, I grabbed the cookie and token values and validated them against each other.
    public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext,
                CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
            {
                try
                {
                    // get headers
                    var headers = actionContext.Request.Headers;
    
                    // get cookie value
                    var cookieRvtValue = headers
                        .GetCookies()
                        .Select(c => c[AntiForgeryConfig.CookieName])
                        .FirstOrDefault()
                        ?.Value;
    
                    // get header value
                    var headerRvtValue = headers.GetValues("__RequestVerificationToken").FirstOrDefault();
    
                    // validate
                    AntiForgery.Validate(cookieRvtValue, headerRvtValue);
                }
                catch
                {
                    actionContext.Response = new HttpResponseMessage
                    {
                        StatusCode = HttpStatusCode.Forbidden,
                        RequestMessage = actionContext.ControllerContext.Request
                    };
                    var source = new TaskCompletionSource<HttpResponseMessage>();
                    source.SetResult(result);
                    return source.Task;
                }
                return continuation();
            }
        }
  • Decorate your Web API controller action with the attribute

    [UserValidateAntiForgeryToken]
    [Authorize]
    [HttpGet]
    public string DoGetAction()
    {
         // do something
         return string.empty;
    }
    

Thanks for reading. Hope you find this helpful!

Dec 18, 2017

Comments

Morten Holmgaard
Morten Holmgaard Jan 29, 2018 12:59 PM

It does not work for me - episerver does not have the same logged in context. What else do you have in web.config and in OWIN startup file?

Please login to comment.
Latest blogs
Multiple Anonymous Carts created from external Head front fetching custom Api

Scenario and Problem Working in a custom headless architecture where a NextJs application hosted in Vercel consumes a custom API built in a...

David Ortiz | Oct 11, 2024

Content Search with Optimizely Graph

Optimizely Graph lets you fetch content and sync data from other Optimizely products. For content search, this lets you create custom search tools...

Dileep D | Oct 9, 2024 | Syndicated blog

Omnichannel Analytics Simplified – Optimizely Acquires Netspring

Recently, the news broke that Optimizely acquired Netspring, a warehouse-native analytics platform. I’ll admit, I hadn’t heard of Netspring before,...

Alex Harris - Perficient | Oct 9, 2024 | Syndicated blog

Problem with language file localization after upgrading to Optimizely CMS 12

Avoid common problems with xml file localization when upgrading from Optimizely CMS 11 to CMS 12.

Tomas Hensrud Gulla | Oct 9, 2024 | Syndicated blog

Optimizely Autocomplete (Statistics)

A user starts typing in the search input, and it returns suggestions for phrases they might be searching for. How to achieve this?

Damian Smutek | Oct 9, 2024 | Syndicated blog

Optimizely Forms: You cannot submit this form because an administrator has turned off data storage.

Do not let this error message scare you, the solution is quite simple!

Tomas Hensrud Gulla | Oct 4, 2024 | Syndicated blog