Ensures that written cookies are done so consistently based on the UmbracoBackOfficeCookieAuthOptions. Ensures that when a webforms page requests token renewal that the token is not always renewed for the request, it checks if the tokens expiry correctly and only renews when necessary so the cookie is not written each time. Fixes the ForceRenewalCookieAuthenticationHandler to only write a cookie if the request is for a request that is not normally auth'd (i.e. is a webforms form that exists outside the normal /umbraco path ... legacy).

This commit is contained in:
Shannon
2015-11-27 16:25:39 +01:00
parent 2c4403f892
commit 8e6bbc3df9
6 changed files with 90 additions and 49 deletions

View File

@@ -172,7 +172,7 @@ namespace Umbraco.Web.Security.Identity
app.UseStageMarker(PipelineStage.Authenticate);
//Then our custom middlewares
app.Use(typeof(ForceRenewalCookieAuthenticationMiddleware), app, options);
app.Use(typeof(ForceRenewalCookieAuthenticationMiddleware), app, options, new SingletonUmbracoContextAccessor());
app.UseStageMarker(PipelineStage.Authenticate);
app.Use(typeof(FixWindowsAuthMiddlware));
app.UseStageMarker(PipelineStage.Authenticate);

View File

@@ -50,6 +50,7 @@ namespace Umbraco.Web.Security.Identity
/// </summary>
/// <param name="ctx"></param>
/// <param name="originalRequestUrl"></param>
/// <param name="checkForceAuthTokens"></param>
/// <returns></returns>
/// <remarks>
/// We auth the request when:
@@ -58,14 +59,14 @@ namespace Umbraco.Web.Security.Identity
/// * it is a /base request
/// * it is a preview request
/// </remarks>
internal static bool ShouldAuthenticateRequest(IOwinContext ctx, Uri originalRequestUrl)
internal bool ShouldAuthenticateRequest(IOwinContext ctx, Uri originalRequestUrl, bool checkForceAuthTokens = true)
{
var request = ctx.Request;
var httpCtx = ctx.TryGetHttpContext();
if (//check the explicit flag
ctx.Get<bool?>("umbraco-force-auth") != null
|| (httpCtx.Success && httpCtx.Result.Items["umbraco-force-auth"] != null)
(checkForceAuthTokens && ctx.Get<bool?>("umbraco-force-auth") != null)
|| (checkForceAuthTokens && httpCtx.Success && httpCtx.Result.Items["umbraco-force-auth"] != null)
//check back office
|| request.Uri.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath)
//check installer

View File

@@ -1,3 +1,5 @@
using System;
using Umbraco.Core;
using System.Threading.Tasks;
using Microsoft.Owin;
using Microsoft.Owin.Security;
@@ -11,6 +13,13 @@ namespace Umbraco.Web.Security.Identity
/// </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>
@@ -45,7 +54,20 @@ namespace Umbraco.Web.Security.Identity
/// <returns></returns>
protected override Task ApplyResponseGrantAsync()
{
//Now we need to check if we should force renew this based on a flag in the context
if (_umbracoContextAccessor.Value == 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.Value.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
@@ -62,45 +84,39 @@ namespace Umbraco.Web.Security.Identity
if (shouldSignin == false && shouldSignout == false)
{
//get the ticket
var model = GetTicket();
if (model != null)
var ticket = GetTicket();
if (ticket != null)
{
var currentUtc = Options.SystemClock.UtcNow;
var issuedUtc = model.Properties.IssuedUtc;
var expiresUtc = model.Properties.ExpiresUtc;
var issuedUtc = ticket.Properties.IssuedUtc;
var expiresUtc = ticket.Properties.ExpiresUtc;
if (expiresUtc.HasValue && issuedUtc.HasValue)
{
//renew the date/times
model.Properties.IssuedUtc = currentUtc;
var timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value);
model.Properties.ExpiresUtc = currentUtc.Add(timeSpan);
var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
var timeRemaining = expiresUtc.Value.Subtract(currentUtc);
//now save back all the required cookie details
var cookieValue = Options.TicketDataFormat.Protect(model);
var cookieOptions = new CookieOptions
//if it's time to renew, then do it
if (timeRemaining < timeElapsed)
{
Domain = Options.CookieDomain,
HttpOnly = Options.CookieHttpOnly,
Path = Options.CookiePath ?? "/",
};
if (Options.CookieSecure == CookieSecureOption.SameAsRequest)
{
cookieOptions.Secure = Request.IsSecure;
}
else
{
cookieOptions.Secure = Options.CookieSecure == CookieSecureOption.Always;
}
if (model.Properties.IsPersistent)
{
cookieOptions.Expires = model.Properties.ExpiresUtc.Value.ToUniversalTime().DateTime;
}
Options.CookieManager.AppendResponseCookie(
Context,
Options.CookieName,
cookieValue,
cookieOptions);
//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);
}
}
}
}

View File

@@ -10,13 +10,20 @@ namespace Umbraco.Web.Security.Identity
/// </summary>
internal class ForceRenewalCookieAuthenticationMiddleware : CookieAuthenticationMiddleware
{
public ForceRenewalCookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options) : base(next, app, options)
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
public ForceRenewalCookieAuthenticationMiddleware(
OwinMiddleware next,
IAppBuilder app,
UmbracoBackOfficeCookieAuthOptions options,
IUmbracoContextAccessor umbracoContextAccessor) : base(next, app, options)
{
}
_umbracoContextAccessor = umbracoContextAccessor;
}
protected override AuthenticationHandler<CookieAuthenticationOptions> CreateHandler()
{
return new ForceRenewalCookieAuthenticationHandler();
return new ForceRenewalCookieAuthenticationHandler(_umbracoContextAccessor);
}
}
}

View File

@@ -80,15 +80,7 @@ namespace Umbraco.Web.Security.Identity
var cookieValue = _authOptions.TicketDataFormat.Protect(ticket);
var cookieOptions = new CookieOptions
{
Path = "/",
Domain = _authOptions.CookieDomain ?? null,
Expires = DateTime.Now.AddMinutes(30),
HttpOnly = true,
Secure = _authOptions.CookieSecure == CookieSecureOption.Always
|| (_authOptions.CookieSecure == CookieSecureOption.SameAsRequest && request.Uri.Scheme.InvariantEquals("https")),
};
var cookieOptions = _authOptions.CreateRequestCookieOptions(context, ticket);
_authOptions.CookieManager.AppendResponseCookie(
context,

View File

@@ -1,6 +1,8 @@
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Umbraco.Core;
using Umbraco.Core.Configuration;
@@ -20,6 +22,29 @@ namespace Umbraco.Web.Security.Identity
{
}
public CookieOptions CreateRequestCookieOptions(IOwinContext ctx, AuthenticationTicket ticket)
{
if (ctx == null) throw new ArgumentNullException("ctx");
if (ticket == null) throw new ArgumentNullException("ticket");
var cookieOptions = new CookieOptions
{
Path = "/",
Domain = this.CookieDomain ?? null,
Expires = DateTime.Now.AddMinutes(30),
HttpOnly = true,
Secure = this.CookieSecure == CookieSecureOption.Always
|| (this.CookieSecure == CookieSecureOption.SameAsRequest && ctx.Request.IsSecure),
};
if (ticket.Properties.IsPersistent && ticket.Properties.ExpiresUtc.HasValue)
{
cookieOptions.Expires = ticket.Properties.ExpiresUtc.Value.ToUniversalTime().DateTime;
}
return cookieOptions;
}
public UmbracoBackOfficeCookieAuthOptions(
ISecuritySection securitySection,
int loginTimeoutMinutes,