diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs index 49511697d7..008f24f492 100644 --- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Core/Security/AuthenticationExtensions.cs @@ -216,7 +216,7 @@ namespace Umbraco.Core.Security GlobalSettings.TimeOutInMinutes, //Umbraco has always persisted it's original cookie for 1 day so we'll keep it that way 1440, - "/", + "/umbraco", UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, UmbracoConfig.For.UmbracoSettings().Security.AuthCookieDomain); } @@ -443,7 +443,8 @@ namespace Umbraco.Core.Security hash) { Expires = DateTime.Now.AddMinutes(minutesPersisted), - Domain = cookieDomain + Domain = cookieDomain, + Path = cookiePath }; if (GlobalSettings.UseSSL) diff --git a/src/Umbraco.Web/Security/Identity/AppBuilderExtensions.cs b/src/Umbraco.Web/Security/Identity/AppBuilderExtensions.cs index dc68a43152..52eadd98da 100644 --- a/src/Umbraco.Web/Security/Identity/AppBuilderExtensions.cs +++ b/src/Umbraco.Web/Security/Identity/AppBuilderExtensions.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Web; +using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; using Microsoft.Owin.Extensions; +using Microsoft.Owin.Security.Cookies; using Owin; using Umbraco.Core; using Umbraco.Core.Configuration; @@ -51,47 +53,28 @@ namespace Umbraco.Web.Security.Identity { if (app == null) throw new ArgumentNullException("app"); - app.Use(typeof (UmbracoBackOfficeAuthenticationMiddleware), - //ctor params - app, - new UmbracoBackOfficeCookieAuthenticationOptions( + + app.UseCookieAuthentication(new UmbracoBackOfficeCookieAuthenticationOptions( UmbracoConfig.For.UmbracoSettings().Security, GlobalSettings.TimeOutInMinutes, - GlobalSettings.UseSSL), - LoggerResolver.Current.Logger); + GlobalSettings.UseSSL, + GlobalSettings.Path) + { + //Provider = new CookieAuthenticationProvider + //{ + // // Enables the application to validate the security stamp when the user + // // logs in. This is a security feature which is used when you + // // change a password or add an external login to your account. + // OnValidateIdentity = SecurityStampValidator + // .OnValidateIdentity, UmbracoApplicationUser, int>( + // TimeSpan.FromMinutes(30), + // (manager, user) => user.GenerateUserIdentityAsync(manager), + // identity => identity.GetUserId()) + //} + }); - app.UseStageMarker(PipelineStage.Authenticate); return app; } - //This is a fix for OWIN mem leak! - //http://stackoverflow.com/questions/24378856/memory-leak-in-owin-appbuilderextensions/24819543#24819543 - private class OwinContextDisposal : IDisposable - where T1 : IDisposable - where T2 : IDisposable - { - private readonly List _disposables = new List(); - private bool _disposed = false; - - public OwinContextDisposal(IOwinContext owinContext) - { - if (HttpContext.Current == null) return; - - _disposables.Add(owinContext.Get()); - _disposables.Add(owinContext.Get()); - - HttpContext.Current.DisposeOnPipelineCompleted(this); - } - - public void Dispose() - { - if (_disposed) return; - foreach (var disposable in _disposables) - { - disposable.Dispose(); - } - _disposed = true; - } - } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationHandler.cs b/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationHandler.cs deleted file mode 100644 index 3393075c14..0000000000 --- a/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationHandler.cs +++ /dev/null @@ -1,236 +0,0 @@ -using System; -using System.Reflection; -using System.Security.Principal; -using System.Threading; -using System.Threading.Tasks; -using System.Web; -using System.Web.Security; -using Microsoft.Owin; -using Microsoft.Owin.Security; -using Microsoft.Owin.Security.Cookies; -using Microsoft.Owin.Security.Infrastructure; -using Newtonsoft.Json; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Security; -using Umbraco.Core; -using Umbraco.Core.Logging; - -namespace Umbraco.Web.Security.Identity -{ - /// - /// Used to allow normal Umbraco back office authentication to work - /// - public class UmbracoBackOfficeAuthenticationHandler : AuthenticationHandler - { - private readonly ILogger _logger; - private bool _shouldRenew; - private DateTimeOffset _renewIssuedUtc; - private DateTimeOffset _renewExpiresUtc; - - public UmbracoBackOfficeAuthenticationHandler(ILogger logger) - { - _logger = logger; - } - - /// - /// Checks if we should authentication the request (i.e. is back office) and if so gets the forms auth ticket in the request - /// and returns an AuthenticationTicket based on that. - /// - /// - /// - /// It's worth noting that the UmbracoModule still executes and performs the authentication, however this also needs to execute - /// so that it assigns the new Principal object on the OWIN request: - /// http://brockallen.com/2013/10/27/host-authentication-and-web-api-with-owin-and-active-vs-passive-authentication-middleware/ - /// - protected override async Task AuthenticateCoreAsync() - { - if (ShouldAuthRequest()) - { - var ticket = GetAuthTicket(Request); - - if (ticket == null) - { - _logger.Warn(@"Unprotect ticket failed"); - return null; - } - - DateTimeOffset currentUtc = Options.SystemClock.UtcNow; - DateTimeOffset? issuedUtc = ticket.Properties.IssuedUtc; - DateTimeOffset? expiresUtc = ticket.Properties.ExpiresUtc; - - if (expiresUtc != null && expiresUtc.Value < currentUtc) - { - return null; - } - - if (issuedUtc != null && expiresUtc != null && Options.SlidingExpiration) - { - TimeSpan timeElapsed = currentUtc.Subtract(issuedUtc.Value); - TimeSpan timeRemaining = expiresUtc.Value.Subtract(currentUtc); - - if (timeRemaining < timeElapsed) - { - _shouldRenew = true; - _renewIssuedUtc = currentUtc; - TimeSpan timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value); - _renewExpiresUtc = currentUtc.Add(timeSpan); - } - } - - var context = new CookieValidateIdentityContext(Context, ticket, Options); - - await Options.Provider.ValidateIdentity(context); - - return new AuthenticationTicket(context.Identity, context.Properties); - } - - return await Task.FromResult(null); - } - - protected override async Task ApplyResponseGrantAsync() - { - AuthenticationResponseGrant signin = Helper.LookupSignIn(Options.AuthenticationType); - bool shouldSignin = signin != null; - AuthenticationResponseRevoke signout = Helper.LookupSignOut(Options.AuthenticationType, Options.AuthenticationMode); - bool shouldSignout = signout != null; - - if (shouldSignin || shouldSignout || _shouldRenew) - { - var cookieOptions = new CookieOptions - { - 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 (shouldSignin) - { - var context = new CookieResponseSignInContext( - Context, - Options, - Options.AuthenticationType, - signin.Identity, - signin.Properties); - - DateTimeOffset issuedUtc = Options.SystemClock.UtcNow; - DateTimeOffset expiresUtc = issuedUtc.Add(Options.ExpireTimeSpan); - - context.Properties.IssuedUtc = issuedUtc; - context.Properties.ExpiresUtc = expiresUtc; - - Options.Provider.ResponseSignIn(context); - - if (context.Properties.IsPersistent) - { - cookieOptions.Expires = expiresUtc.ToUniversalTime().DateTime; - } - - var model = new AuthenticationTicket(context.Identity, context.Properties); - string cookieValue = Options.TicketDataFormat.Protect(model); - - Response.Cookies.Append( - Options.CookieName, - cookieValue, - cookieOptions); - } - else if (shouldSignout) - { - Response.Cookies.Delete( - Options.CookieName, - cookieOptions); - } - else if (_shouldRenew) - { - AuthenticationTicket model = await AuthenticateAsync(); - - model.Properties.IssuedUtc = _renewIssuedUtc; - model.Properties.ExpiresUtc = _renewExpiresUtc; - - string cookieValue = Options.TicketDataFormat.Protect(model); - - if (model.Properties.IsPersistent) - { - cookieOptions.Expires = _renewExpiresUtc.ToUniversalTime().DateTime; - } - - Response.Cookies.Append( - Options.CookieName, - cookieValue, - cookieOptions); - } - - //Response.Headers.Set( - // HeaderNameCacheControl, - // HeaderValueNoCache); - - //Response.Headers.Set( - // HeaderNamePragma, - // HeaderValueNoCache); - - //Response.Headers.Set( - // HeaderNameExpires, - // HeaderValueMinusOne); - - bool shouldLoginRedirect = shouldSignin && Options.LoginPath.HasValue && Request.Path == Options.LoginPath; - bool shouldLogoutRedirect = shouldSignout && Options.LogoutPath.HasValue && Request.Path == Options.LogoutPath; - - if ((shouldLoginRedirect || shouldLogoutRedirect) && Response.StatusCode == 200) - { - IReadableStringCollection query = Request.Query; - string redirectUri = query.Get(Options.ReturnUrlParameter); - if (!string.IsNullOrWhiteSpace(redirectUri) - //&& IsHostRelative(redirectUri) - ) - { - var redirectContext = new CookieApplyRedirectContext(Context, Options, redirectUri); - Options.Provider.ApplyRedirect(redirectContext); - } - } - } - } - - private bool ShouldAuthRequest() - { - var httpContext = Context.HttpContextFromOwinContext(); - - // do not process if client-side request - if (httpContext.Request.Url.IsClientSideRequest()) - return false; - - return UmbracoModule.ShouldAuthenticateRequest(httpContext.Request, Request.Uri); - } - - /// - /// Returns the current FormsAuth ticket in the request - /// - /// - /// - private AuthenticationTicket GetAuthTicket(IOwinRequest request) - { - if (request == null) throw new ArgumentNullException("request"); - - var formsCookie = request.Cookies[Options.CookieName]; - if (string.IsNullOrWhiteSpace(formsCookie)) - { - return null; - } - //get the ticket - try - { - return Options.TicketDataFormat.Unprotect(formsCookie); - } - catch (Exception) - { - return null; - } - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationMiddleware.cs b/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationMiddleware.cs deleted file mode 100644 index ffdce0fc8d..0000000000 --- a/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationMiddleware.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.Owin; -using Microsoft.Owin.Security.Infrastructure; -using Owin; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Logging; - -namespace Umbraco.Web.Security.Identity -{ - /// - /// Used to enable the normal Umbraco back office authentication to operate - /// - public class UmbracoBackOfficeAuthenticationMiddleware : AuthenticationMiddleware - { - private readonly ILogger _logger; - - public UmbracoBackOfficeAuthenticationMiddleware( - OwinMiddleware next, - IAppBuilder app, - UmbracoBackOfficeCookieAuthenticationOptions options, - ILogger logger) - : base(next, options) - { - _logger = logger; - } - - protected override AuthenticationHandler CreateHandler() - { - return new UmbracoBackOfficeAuthenticationHandler(_logger); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeCookieAuthenticationOptions.cs b/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeCookieAuthenticationOptions.cs index e23f30b27f..ac77a49db3 100644 --- a/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeCookieAuthenticationOptions.cs +++ b/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeCookieAuthenticationOptions.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; +using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; @@ -13,22 +14,32 @@ namespace Umbraco.Web.Security.Identity public sealed class UmbracoBackOfficeCookieAuthenticationOptions : CookieAuthenticationOptions { public UmbracoBackOfficeCookieAuthenticationOptions() - : this(UmbracoConfig.For.UmbracoSettings().Security, GlobalSettings.TimeOutInMinutes, GlobalSettings.UseSSL) + : this(UmbracoConfig.For.UmbracoSettings().Security, GlobalSettings.TimeOutInMinutes, GlobalSettings.UseSSL, GlobalSettings.Path) { } - public UmbracoBackOfficeCookieAuthenticationOptions(ISecuritySection securitySection, int loginTimeoutMinutes, bool forceSsl) + public UmbracoBackOfficeCookieAuthenticationOptions( + ISecuritySection securitySection, + int loginTimeoutMinutes, + bool forceSsl, + string umbracoPath, + bool useLegacyFormsAuthDataFormat = true) { AuthenticationType = "UmbracoBackOffice"; - TicketDataFormat = new FormsAuthenticationSecureDataFormat(loginTimeoutMinutes); + if (useLegacyFormsAuthDataFormat) + { + //If this is not explicitly set it will fall back to the default automatically + TicketDataFormat = new FormsAuthenticationSecureDataFormat(loginTimeoutMinutes); + } CookieDomain = securitySection.AuthCookieDomain; CookieName = securitySection.AuthCookieName; CookieHttpOnly = true; CookieSecure = forceSsl ? CookieSecureOption.Always : CookieSecureOption.SameAsRequest; - CookiePath = "/"; - LoginPath = new PathString("/umbraco/login"); //TODO: ?? + + //Ensure the cookie path is set so that it isn't transmitted for anything apart from requests to the back office + CookiePath = umbracoPath.EnsureStartsWith('/'); } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 858561e5e9..5be8656b13 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -543,8 +543,6 @@ - -