Skip to content

Instantly share code, notes, and snippets.

@thinkfreshnick
Created February 5, 2018 13:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thinkfreshnick/e2e3e7998f95c42598146be86ccc5cd3 to your computer and use it in GitHub Desktop.
Save thinkfreshnick/e2e3e7998f95c42598146be86ccc5cd3 to your computer and use it in GitHub Desktop.
A custom implementation of ITokenProvider that doesn't return a 500 response after a SecurityTokenInvalidLifetimeException is thrown but instead returns the intended 403 response.
using Sitecore;
using Sitecore.Services.Core.Configuration;
using Sitecore.Services.Core.Diagnostics;
using Sitecore.Services.Core.Security;
using Sitecore.Services.Infrastructure.Sitecore.Configuration;
using Sitecore.Services.Infrastructure.Sitecore.Diagnostics;
using Sitecore.Services.Infrastructure.Sitecore.Security;
using Sitecore.Services.Infrastructure.Web.Http.Security;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Tokens;
using System.Security.Claims;
namespace My.Assembly.Infrastructure.Security
{
public class MySigningTokenProvider : ITokenProvider
{
private readonly int authMinutes;
private readonly ConfigurationSettings _configurationSettings;
private readonly ILogger _logger;
private readonly IUserService _userService;
private readonly JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
private readonly ISigningProvider _signingProvider;
private TokenValidationParameters _tokenValidationParameters;
internal Func<DateTime> _dateTimeUtcNow = new Func<DateTime>(() => DateTime.UtcNow);
internal Func<string> _contextUserName = new Func<string>(() => Context.User.Name);
public MySigningTokenProvider() :
this(new UserService(), new ConfigurationReader(new ConfigurationSectionReader()).Load(), new SitecoreLogger(), new ConfigurationSigningProviderFactory().Create())
{
}
public MySigningTokenProvider(UserService userService, ConfigurationSettings configurationSettings, ILogger logger, ISigningProvider signingProvider)
{
if (!configurationSettings.SitecoreServices.Security.IsTokenAuthorizationEnabled)
{
return;
}
_userService = userService;
_configurationSettings = configurationSettings;
_logger = logger;
_signingProvider = signingProvider ?? throw new ArgumentNullException("signingProvider");
int.TryParse(_configurationSettings.SitecoreServices.Security.TokenAuthorizationTimeout, out authMinutes);
if (_configurationSettings.SitecoreServices.Security.IsTokenAuthorizationEnabled && _signingProvider == null)
{
logger.Error("Token authentication Signing Provider is not configured");
}
_tokenValidationParameters = _signingProvider.SigningValidationParameters.Clone();
_tokenValidationParameters.ValidateAudience = false;
_tokenValidationParameters.ValidIssuers = new string[] { "Sitecore" };
_tokenValidationParameters.LifetimeValidator = new LifetimeValidator(ValidateLifetime);
}
public string GenerateToken(IEnumerable<Claim> claims)
{
DateTime dateTime = _dateTimeUtcNow();
DateTime dateTime1 = dateTime.AddMinutes((double)authMinutes);
SecurityTokenDescriptor securityTokenDescriptor = new SecurityTokenDescriptor()
{
TokenIssuerName = "Sitecore",
Subject = new ClaimsIdentity(claims),
SigningCredentials = _signingProvider.SigningCredentials,
Lifetime = new Lifetime(dateTime, dateTime1)
};
return tokenHandler.WriteToken(tokenHandler.CreateToken(securityTokenDescriptor));
}
private bool ValidateLifetime(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
bool flag;
try
{
if (validationParameters == null)
{
throw new ArgumentNullException("validationParameters");
}
if (validationParameters.ValidateLifetime)
{
if (!expires.HasValue && validationParameters.RequireExpirationTime)
{
CultureInfo invariantCulture = CultureInfo.InvariantCulture;
object[] objArray = new object[] { (securityToken == null ? "null" : securityToken.GetType().ToString()) };
throw new SecurityTokenNoExpirationException(string.Format(invariantCulture, "IDX10225: Lifetime validation failed. The token is missing an Expiration Time.\nTokentype: '{0}'.", objArray));
}
if (notBefore.HasValue && expires.HasValue && notBefore.Value > expires.Value)
{
CultureInfo cultureInfo = CultureInfo.InvariantCulture;
object[] value = new object[] { notBefore.Value, expires.Value };
throw new SecurityTokenInvalidLifetimeException(string.Format(cultureInfo, "IDX10224: Lifetime validation failed. The NotBefore: '{0}' is after Expires: '{1}'.", value));
}
DateTime dateTime = _dateTimeUtcNow();
if (notBefore.HasValue && notBefore.Value > dateTime.Add(validationParameters.ClockSkew))
{
CultureInfo invariantCulture1 = CultureInfo.InvariantCulture;
object[] value1 = new object[] { notBefore.Value, dateTime };
throw new SecurityTokenNotYetValidException(string.Format(invariantCulture1, "IDX10222: Lifetime validation failed. The token is not yet valid.\nValidFrom: '{0}'\nCurrent time: '{1}'.", value1));
}
if (expires.HasValue && expires.Value < dateTime.Add(validationParameters.ClockSkew.Negate()))
{
CultureInfo cultureInfo1 = CultureInfo.InvariantCulture;
object[] objArray1 = new object[] { expires.Value, dateTime };
throw new SecurityTokenExpiredException(string.Format(cultureInfo1, "IDX10223: Lifetime validation failed. The token is expired.\nValidTo: '{0}'\nCurrent time: '{1}'.", objArray1));
}
}
return true;
}
catch (SecurityTokenException ex)
{
flag = false;
}
return flag;
}
public ITokenValidationResult ValidateToken(string token)
{
SecurityToken securityToken;
try
{
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(token, _tokenValidationParameters, out securityToken);
ValidatedToken validatedToken = new ValidatedToken()
{
Claims = claimsPrincipal.Claims,
IsValid = true
};
return validatedToken;
}
catch (SecurityTokenInvalidLifetimeException ex)
{
_logger.Debug(string.Format("Token invalid: {0}", ex.Message.Replace("{", "{{").Replace("}", "}}").Replace(@"""", @"""""")), (object)this);
}
catch (SecurityTokenValidationException ex)
{
_logger.Debug(string.Format("Token invalid: {0}", ex.Message.Replace("{","{{").Replace("}","}}").Replace(@"""",@"""""")), (object)this);
}
catch (Exception ex)
{
_logger.Error("Unable to validate token", new object[] { ex });
}
return new ValidatedToken()
{
IsValid = false
};
}
}
}
@miller-web
Copy link

Looks good, do you have an example of how to patch this in via .config file?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment