Sitecore EntityService with ItemService Style Token Authorization

Security

Sitecore's ItemService and EntityService have been around for a while now and there is a fair amount of community content out there (and Sitecore's documentation) that describe the role of the two service layers and how to configure them so I won't reinvent those wheels here.

What I have recently come to discover however is that the EntityService, whilst providing better support for working with custom business objects and writing custom WebAPI controllers does not support authorization out-of-the-box. The settings in Sitecore.Services.Client.config that relate to authorization all apply to the ItemService only.

What I wanted for my project, was the flexibility of the EntityService with (JWT) token based authorization. Best of both worlds.

Ian Graham wrote an excellent article on enabling token based authorization, which you can read here. The configuration steps mentioned there are all relevant to this scenario so consider them a pre-requisite.

After configuring token based authorization and setting up an EntityService you will immediately notice that you can send a request to your service without a token header and it will return a 200 response and the expected JSON data. So how can we use the out-of-the-box token generation and validation that Sitecore provides with the ItemService within the context of EntityService controllers?

Well first, you need to understand that Sitecore's implementation uses the class Sitecore.Services.Infrastructure.Sitecore.Security.ConfiguredOrNullTokenProvider, which is instantiated in the constructor of Sitecore.Services.Infrastructure.Sitecore.Security.TokenDelegatingHandler

The TokenDelegatingHandler is configured in Sitecore.Services.Client.config

The TokenDelegatingHandler uses the following method to validate a token passed in the request header

What's important here is that an instance of HttpRequestMessage is passed into this method.

Now let's create a custom authorize attribute that inherits from System.Web.Http.AuthorizeAttribute. In this class override the IsAuthorized method, which is passed an instance of HttpActionContext and let's give it a constructor that creates a new privately available instance of ConfiguredOrNullTokenProvider just like the TokenDelegatingHandler (except we don’t also need the UserService, we just want to know if the token is valid or not).

The HttpActionContext instance passed into the IsAuthorized method gives us access to the HttpRequestMessage (which the TokenDelegatingHandler uses to locate the token header, so we can do the same. Putting it all together

Now we simply decorate our entity service controller with our custom token authorization attribute

Now you can request tokens the same way you do with the ItemService:

Request Sitecore token using Postman

Request Sitecore token using Postman

And pass the value of the token response back to Sitecore in the token header when making requests to your entity service controllers or get a 403 response.

As highlighted on Sitecore Stack Exchange you will get a 500 server error response if you pass a token that has expired (expiry is 20 minutes by default but configurable in the setting Sitecore.Services.Token.Authorization.Timeout), well certainly in 8.2 Update 2 and Update 3 you will, I haven't tested other versions. If you don't care about that, you are done here.

If however, you do want to get a proper 403 response for expired tokens (like me and the OP on Stack Exchange) read on.

To make this error go away we have to (unfortunately) do some slightly dirty things. First we need to replace the default implementation of TokenDelegatingHandler, which is straight forward to do because it's just configuration

In our custom handler we want to replace the default SigningTokenProvider with our own implementation in two of the constructors

This is where it gets gory, yes you guessed it, no virtual methods, private members etc so we have to copy paste a bunch of Sitecore's code and replace the bit responsible for the 500 error which as the thread on Stack Exchange explains is in the logging of SecurityTokenInvalidLifetimeException exceptions raised. I've thrown up a gist so you don’t have to go through the pain of tidying it all up.

That's it. Sitecore's EntityService with ItemService style token authorization and proper 403 responses for expired tokens.

Happy… securing.