Using Sitecore 8.2 Update 3

I am using the Sitecore Services Client EntityService with Token authentication. When I try to access to the service after the token has expired, I should get a 403, but instead I get:

"Message": "An error has occurred.",
"ExceptionMessage": "Input string was not in a correct format.",
"ExceptionType": "System.FormatException",
 "StackTrace": "   at 
System.Text.StringBuilder.AppendFormatHelper(IFormatProvider provider, 
String format, ParamsArray args)   at 
System.String.FormatHelper(IFormatProvider provider, String format, ParamsArray args)
 at System.String.Format(String format, Object[] args)   at   
Sitecore.Services.Infrastructure.Sitecore.Diagnostics.SitecoreLogger.Debug( String messageFormat, Object[] args) 
at  Sitecore.Services. Infrastructure.Sitecore.Security.SigningTokenProvider.ValidateToken(String token) 
at Sitecore.Services.Infrastructure.Sitecore.Security.TokenDelegatingHandler.AttemptLoginWithToken(HttpRequestMessage request)\r\n  
 at Sitecore.Services. Infrastructure.Sitecore.Security.TokenDelegatingHandler.<SendAsync>d__0.MoveNext()\r\n
 ---  End of stack trace from previous location where exception was thrown ---   
 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at System.Web.Http.HttpServer.<SendAsync>d__0.MoveNext()"

So basically, the stringbuilder blows up when trying to write to the log. How do I intercept this to handle this error more appripriately? I want the end user to get a 403.

UPDATE

This is occurring on the ValidateToken method. The token is expired, so it throws a SecurityTokenInvalidLifetimeException which then logs like os:

 this._logger.Debug(string.Format("Token invalid: {0}", (object) ex), (object) this);

This is in the class SigningTokenProvider, which I could maybe replace if I could figure out how to swap it out with my own version. Its not referenced anywhere in ShowConfig, so I dont really know where to look.

UPDATE 2

I did implement replacing Sitecore's token provider as per @zacharykneibel. Once that was replaced, I determined the problem was how they are generating the error message. It generated the error message with some fixed string and an anonymous object. They then log like this:

this._logger.Debug(string.Format("Token invalid: {0}", (object) ex), (object) this);

The problem is that exception, the message contains a string representation of an object that contains items in double quotes and also brackets ({ and }) which are not escaped. This would be fine in an api call, it would com back as json, but they are logging it to the Sitecore log, where things go awry. This feels a bit hacky, but I fixed this by replacing the characters in the error message. For the above mentioned reason, I editing the logging method, and the the error itself:

his._logger.Debug(string.Format("Token invalid: {0}", ex.Message.Replace("{","{{").Replace("}","}}").Replace(@"""",@"""""")), (object)this);

I might write a method to better convert this object to a cleaner string.

To make matters worse, once this was resolved, it seemed there was no logic to prevent a call to the api if the token authentication failed. It noted that the token was no good, but it still processed the call to the api, so I had to rewrite that method as well.

Here's Sitecore method:

rotected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
  this.AttemptLoginWithToken(request);
  return await base.SendAsync(request, cancellationToken);
}

I will need to do something lime this:

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (!AttemptLoginWithToken(request))
        {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            return response;
        }
        return await base.SendAsync(request, cancellationToken);
    }
up vote 1 down vote accepted

TL;DR

The SigningTokenProvider is instantiated in the constructors of the Sitecore.Services.Infrastructure.Sitecore.Security.TokenDelegatingHandler (which you discovered and shared with me offline). The type used for the TokenDelegatingHandler is set in the api/services/configuration/delegatingHandlers node list in the config, which means that you can add a patch to use your own implementation. Your implementation would then be used to instantiate your own SigningTokenProvider type, which you could then customize to suit your needs.

Implementing the SigningTokenProvider

The first thing that you will need to do is create your own implementation of the SigningTokenProvider class. Unfortunately, since the ValidateToken method that you are looking to override is not marked virtual and - even if it was - all of the properties and fields that it uses are either private or internal, you are going to need to completely replace the native SigningTokenProvider type with your own implementation. IMHO, the easiest way to do this is to decompile the Sitecore.Services.Infrastructure.Sitecore.dll assembly and copy and paste the class' body into your own. For clarity, let's assume that your copy of this is named MySigningTokenProvider, and that you have performed the necessary refactoring of the class name and constructor names.

Once you have added/resolved any and all missing references, you will notice a slight problem in your parameterless constructor: the ApplicationContainer.ConfigurationSettingsLoader delegate is marked internal, and thus cannot be referenced from your implementation. The delegate looks as follows:

internal static Func<Sitecore.Services.Core.Configuration.ConfigurationSettings> ConfigurationSettingsLoader = (Func<Sitecore.Services.Core.Configuration.ConfigurationSettings>) (() => new ConfigurationReader((IConfigurationSectionProvider) new ConfigurationSectionReader()).Load());

After removing the namespaces and explicit type-casts and formatting a little to make it a little easier to read on SSE:

internal static Func<ConfigurationSettings> ConfigurationSettingsLoader = 
    () => 
        new ConfigurationReader(new ConfigurationSectionReader()).Load();

There are a few ways around this:

  • NO: Replace the ApplicationContainer with your own that marks the delegate public
  • NO: Rename your project to Sitecore.Services.Infrastructure.Sitecore.Test so that you can reference internal members
  • YES: Copy the very simple delegate that simply instantiates a new instance of a public type and doesn't pass in anything internal, private, etc. from the ApplicationContainer

Once you have replaced the invocation of ApplicationContainer.ConfigurationSettingsLoader with the invocation of a local, duplicate implementation of the delegate, you should be free to implement your own version of the ValidateToken method. All that should remain is to make sure that your new MySigningTokenProvider class is used instead of the native SigningTokenProvider.

Implementing the TokenDelegatingHandler

To make sure that your new SigningTokenProvider implementation is used instead of the native one, there are two constructors in the native TokenDelegatingHandler type that you will need to replace. Fortunately, both of the constructors are marked protected and are bodiless. Since the constructors that you need to replace do not have property-setting bodies and none of the methods, properties, etc. need to be replaced, you can get away with simply inheriting from the native TokenDelegatingHandler type and overriding the two constructors.

The constructors that you will need to replace are the following:

public TokenDelegatingHandler()
  : this((ITokenProvider) new ConfiguredOrNullTokenProvider((ITokenProvider) new SigningTokenProvider()), (IUserService) new UserService())
{
}

...

protected TokenDelegatingHandler(HttpMessageHandler innerHandler)
  : this(innerHandler, (ITokenProvider) new ConfiguredOrNullTokenProvider((ITokenProvider) new SigningTokenProvider()), (IUserService) new UserService())
{
}

Your TokenDelegatingHandler should then look something like the following:

public class MyTokenDelegatingHandler : Sitecore.Services.Infrastructure.Sitecore.Security.TokenDelegatingHandler 
{
    public MyTokenDelegatingHandler()
        : this((ITokenProvider) new ConfiguredOrNullTokenProvider((ITokenProvider) new MySigningTokenProvider()), (IUserService) new UserService())
    {
    }

    protected MyTokenDelegatingHandler(HttpMessageHandler innerHandler)
        : this(innerHandler, (ITokenProvider) new ConfiguredOrNullTokenProvider((ITokenProvider) new MySigningTokenProvider()), (IUserService) new UserService())
    {
    }
}

Sitecore Support

The behavior that you described sounds to me like a bug, since the code is blowing up when trying to write to the log. I recommend that you file a Sitecore Support ticket for this issue, in order to make them aware and get their teams working on a patch. They may also provide you with a Support patch that you can use in place of rolling your own SigningTokenProvider and TokenDelegatingHandler types, which - IMHO - is a better long-term solution.

If you reach out to Sitecore Support and they provide you with a patch and/or a reference number, be sure to update your post with that information for other members to use as a reference if they experience the same issue ;)

  • Do you have an example of how to patch this Handler? – Michael West Feb 18 at 3:30
  • I don’t @MichaelWest - I wrote this solution based on the theory of what was in the code and possible for the OP to be able to override the class. I remember that I did find the way to apply the handler - I just can’t remember how and don’t seem to have my notes anymore. DM me over Slack if you’re having trouble patching this in, yourself, and I’ll help work the problem to find the entry point. – Zachary Kniebel Feb 18 at 3:50
  • 1
    The patch here might do the trick. sitecore.stackexchange.com/a/8363/95 – Michael West Feb 18 at 4:46
  • Yes, I worked with Ethan on that in person. That’s why I couldn’t find my notes. Great find! – Zachary Kniebel Feb 18 at 12:34
  • Getting the same issue, does turning off DEBUG logging work around it? – geedubb Nov 2 at 16:25

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.