HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideLegal TermsGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Authorization Service

Describes the authorization service of the Optimizely Content Delivery API (version 2).

📘

Note

This section applies only if you are still using Content Delivery Api v2.x.x. For users using Content Delivery API v3.x.x, see API Authentication.

Optimizely Content Delivery API supports OAuth authentication (by installing the ContentDeliveryApi.OAuth package, see Install Content Delivery API and cookie-based authentication. However, ContentDeliveryApi exposes APIs that let you customize the authorization flow and use your preferred authorization mechanisms like AzureAD or GitHub.

📘

Note

By default, the ContentDeliveryApi.OAuth package depends on ASP.NET Identity to work and it is not compatible with the old Membership provider.

The following diagram describes the Authorization flow of Content Delivery API:

  • When you request an endpoint (such as api/episerver/v2.0/content/5), the request is handled by ContentApiAuthorizationAttribute before being passed to the corresponding controller.

  • At ContentApiAuthorizationAttribute, the flow is like the following:

    • The user principal is initialized from HttpActionContext using ISecurityPrincipal.InitializePrincipal(), and this principal is assigned to the current http context.

    • The user principal is validated by ContentApiAuthorizationService. There are two main steps at ContentApiAuthorizationService:

      1. The user principal is retrieved from ISecurityPrincipal.
      2. The principal is validated.

      The final result should be a Tuple<HttpStatusCode,string> that contains HttpStatusCode and the error message if any errors occur.

  • Based on the authorization result, ContentApiAuthorizationAttribute returns an error or forwards the request to the controller endpoint.

ISecurityPrincipal and ContentApiAuthorizationService are important components for customizing the authorization flow:

  • ISecurityPrincipal defines signatures for user principal manipulation. If you want to use another OAuth system, you should create your own implementation for this interface and define how to initialize and retrieve user principal:
    • Create a new class inheriting ISecurityPrincipal and decorate the class with attribute ServiceConfiguration(typeof(ISecurityPrincipal)).
    • Override the method InitializePrincipal to initialize user claims. For example, a real-life scenario might be a customized OAuth system where the user’s access token is retrieved by calling one service, and another service is called for authentication and authorization. In this case, those operations should be put in InitializePrincipal and the final result should be that the principal for the current request is set (for example, HttpContext.Current.User = principal and Thread.CurrentPrincipal = principal).
    • Implement GetCurrentPrincipal() and GetAnonymousPrincipal() in line with your systems.
    • If your use case is complicated and this interface is not enough, you can customize the Authorize() method of ContentApiAuthorizationService,

This is the default implementation of ISecurityPrincipal:

using EPiServer.ServiceLocation;
using System.Security.Principal;
using System.Threading;
using System.Web;
using System.Web.Http.Controllers;

namespace EPiServer.ContentApi.Core.Security.Internal {
  /// <summary>
  /// Default implementation of <see cref="ISecurityPrincipal"/> for initialzing and accessing <see cref="IPrincipal"/> within ContentApi's scope.
  /// </summary>
  [ServiceConfiguration(typeof (ISecurityPrincipal))]
  public class DefaultSecurityPrincipal: ISecurityPrincipal {
    /// <summary>
    /// Get <see cref="IPrincipal"/> from provided <see cref="HttpActionContext"/>. Set Principal on current thread and current <see cref="HttpContext"/>
    /// </summary>
    /// <param name="actionContext"></param>
    public virtual void InitializePrincipal(HttpActionContext actionContext) {
      // custom authentication logic, we must set the principal on two places: Current thread & current httpContext
      // for reference, go here https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api
      Thread.CurrentPrincipal = actionContext.RequestContext.Principal;
      // in case the application is not self-hosted
      if (HttpContext.Current != null) {
        HttpContext.Current.User = actionContext.RequestContext.Principal;
      }
    }

    /// <inheritdoc />
    public virtual IPrincipal GetCurrentPrincipal() {
      return HttpContext.Current?.User ?? Thread.CurrentPrincipal;
    }

    /// <summary>
    /// Retrieve an anonymous Principal with Anonymous and Everyone roles
    /// </summary>
    /// <returns></returns>
    public virtual IPrincipal GetAnonymousPrincipal() {
      return new GenericPrincipal(new GenericIdentity("Anonymous"), new [] {
        "Everyone"
      });
    }
  }
}
  • ContentApiAuthorizationService is responsible for authorizing requests. It will check two main things: the user validity, and the user’s privileges to access the API. User principal is extracted by ISecurityPrincipal.GetCurrentPrincipal(). If the process becomes complicated, this service should be customized. Otherwise, ISecurityPrincipal should be enough.

See also: Content Delivery API and Custom Authorization blog post by Johan Kronberg