Files
Umbraco-CMS/src/Umbraco.Web/Security/ForceRenewalCookieAuthenticationHandler.cs
2018-11-22 14:05:51 +00:00

128 lines
5.3 KiB
C#

using System.Threading.Tasks;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Infrastructure;
using Umbraco.Core;
using Umbraco.Core.Security;
namespace Umbraco.Web.Security
{
/// <summary>
/// If a flag is set on the context to force renew the ticket, this will do it
/// </summary>
internal class ForceRenewalCookieAuthenticationHandler : AuthenticationHandler<CookieAuthenticationOptions>
{
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
public ForceRenewalCookieAuthenticationHandler(IUmbracoContextAccessor umbracoContextAccessor)
{
_umbracoContextAccessor = umbracoContextAccessor;
}
/// <summary>
/// This handler doesn't actually do any auth so we return null;
/// </summary>
/// <returns></returns>
protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
{
return Task.FromResult((AuthenticationTicket)null);
}
/// <summary>
/// Gets the ticket from the request
/// </summary>
/// <returns></returns>
private AuthenticationTicket GetTicket()
{
var cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);
if (string.IsNullOrWhiteSpace(cookie))
{
return null;
}
var ticket = Options.TicketDataFormat.Unprotect(cookie);
if (ticket == null)
{
return null;
}
return ticket;
}
/// <summary>
/// This will check if the token exists in the request to force renewal
/// </summary>
/// <returns></returns>
protected override Task ApplyResponseGrantAsync()
{
if (_umbracoContextAccessor.UmbracoContext == null || Context.Request.Uri.IsClientSideRequest())
{
return Task.FromResult(0);
}
//Now we need to check if we should force renew this based on a flag in the context and whether this is a request that is not normally renewed by OWIN...
// which means that it is not a normal URL that is authenticated.
var normalAuthUrl = ((BackOfficeCookieManager) Options.CookieManager)
.ShouldAuthenticateRequest(Context, _umbracoContextAccessor.UmbracoContext.OriginalRequestUrl,
//Pass in false, we want to know if this is a normal auth'd page
checkForceAuthTokens: false);
//This is auth'd normally, so OWIN will naturally take care of the cookie renewal
if (normalAuthUrl) return Task.FromResult(0);
var httpCtx = Context.TryGetHttpContext();
//check for the special flag in either the owin or http context
var shouldRenew = Context.Get<bool?>(Constants.Security.ForceReAuthFlag) != null || (httpCtx.Success && httpCtx.Result.Items[Constants.Security.ForceReAuthFlag] != null);
if (shouldRenew)
{
var signin = Helper.LookupSignIn(Options.AuthenticationType);
var shouldSignin = signin != null;
var signout = Helper.LookupSignOut(Options.AuthenticationType, Options.AuthenticationMode);
var shouldSignout = signout != null;
//we don't care about the sign in/sign out scenario, only renewal
if (shouldSignin == false && shouldSignout == false)
{
//get the ticket
var ticket = GetTicket();
if (ticket != null)
{
var currentUtc = Options.SystemClock.UtcNow;
var issuedUtc = ticket.Properties.IssuedUtc;
var expiresUtc = ticket.Properties.ExpiresUtc;
if (expiresUtc.HasValue && issuedUtc.HasValue)
{
var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
var timeRemaining = expiresUtc.Value.Subtract(currentUtc);
//if it's time to renew, then do it
if (timeRemaining < timeElapsed)
{
//renew the date/times
ticket.Properties.IssuedUtc = currentUtc;
var timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value);
ticket.Properties.ExpiresUtc = currentUtc.Add(timeSpan);
//now save back all the required cookie details
var cookieValue = Options.TicketDataFormat.Protect(ticket);
var cookieOptions = ((UmbracoBackOfficeCookieAuthOptions)Options).CreateRequestCookieOptions(Context, ticket);
Options.CookieManager.AppendResponseCookie(
Context,
Options.CookieName,
cookieValue,
cookieOptions);
}
}
}
}
}
return Task.FromResult(0);
}
}
}