#32 - Port Cookies OnException notification from Katana.
This commit is contained in:
parent
83bffe3542
commit
b10cda80a7
|
|
@ -41,69 +41,84 @@ namespace Microsoft.AspNet.Security.Cookies
|
||||||
|
|
||||||
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
|
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
|
||||||
{
|
{
|
||||||
string cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);
|
AuthenticationTicket ticket = null;
|
||||||
if (string.IsNullOrWhiteSpace(cookie))
|
try
|
||||||
{
|
{
|
||||||
return null;
|
string cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);
|
||||||
}
|
if (string.IsNullOrWhiteSpace(cookie))
|
||||||
|
|
||||||
AuthenticationTicket ticket = Options.TicketDataFormat.Unprotect(cookie);
|
|
||||||
|
|
||||||
if (ticket == null)
|
|
||||||
{
|
|
||||||
_logger.WriteWarning(@"Unprotect ticket failed");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Options.SessionStore != null)
|
|
||||||
{
|
|
||||||
Claim claim = ticket.Identity.Claims.FirstOrDefault(c => c.Type.Equals(SessionIdClaim));
|
|
||||||
if (claim == null)
|
|
||||||
{
|
{
|
||||||
_logger.WriteWarning(@"SessoinId missing");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
_sessionKey = claim.Value;
|
|
||||||
ticket = await Options.SessionStore.RetrieveAsync(_sessionKey);
|
ticket = Options.TicketDataFormat.Unprotect(cookie);
|
||||||
|
|
||||||
if (ticket == null)
|
if (ticket == null)
|
||||||
{
|
{
|
||||||
_logger.WriteWarning(@"Identity missing in session store");
|
_logger.WriteWarning(@"Unprotect ticket failed");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
|
|
||||||
DateTimeOffset? issuedUtc = ticket.Properties.IssuedUtc;
|
|
||||||
DateTimeOffset? expiresUtc = ticket.Properties.ExpiresUtc;
|
|
||||||
|
|
||||||
if (expiresUtc != null && expiresUtc.Value < currentUtc)
|
|
||||||
{
|
|
||||||
if (Options.SessionStore != null)
|
if (Options.SessionStore != null)
|
||||||
{
|
{
|
||||||
await Options.SessionStore.RemoveAsync(_sessionKey);
|
Claim claim = ticket.Identity.Claims.FirstOrDefault(c => c.Type.Equals(SessionIdClaim));
|
||||||
|
if (claim == null)
|
||||||
|
{
|
||||||
|
_logger.WriteWarning(@"SessoinId missing");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
_sessionKey = claim.Value;
|
||||||
|
ticket = await Options.SessionStore.RetrieveAsync(_sessionKey);
|
||||||
|
if (ticket == null)
|
||||||
|
{
|
||||||
|
_logger.WriteWarning(@"Identity missing in session store");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (issuedUtc != null && expiresUtc != null && Options.SlidingExpiration)
|
DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
|
||||||
{
|
DateTimeOffset? issuedUtc = ticket.Properties.IssuedUtc;
|
||||||
TimeSpan timeElapsed = currentUtc.Subtract(issuedUtc.Value);
|
DateTimeOffset? expiresUtc = ticket.Properties.ExpiresUtc;
|
||||||
TimeSpan timeRemaining = expiresUtc.Value.Subtract(currentUtc);
|
|
||||||
|
|
||||||
if (timeRemaining < timeElapsed)
|
if (expiresUtc != null && expiresUtc.Value < currentUtc)
|
||||||
{
|
{
|
||||||
_shouldRenew = true;
|
if (Options.SessionStore != null)
|
||||||
_renewIssuedUtc = currentUtc;
|
{
|
||||||
TimeSpan timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value);
|
await Options.SessionStore.RemoveAsync(_sessionKey);
|
||||||
_renewExpiresUtc = currentUtc.Add(timeSpan);
|
}
|
||||||
|
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.Notifications.ValidateIdentity(context);
|
||||||
|
|
||||||
|
return new AuthenticationTicket(context.Identity, context.Properties);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
CookieExceptionContext exceptionContext = new CookieExceptionContext(Context, Options,
|
||||||
|
CookieExceptionContext.ExceptionLocation.Authenticate, exception, ticket);
|
||||||
|
Options.Notifications.Exception(exceptionContext);
|
||||||
|
if (exceptionContext.Rethrow)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return exceptionContext.Ticket;
|
||||||
}
|
}
|
||||||
|
|
||||||
var context = new CookieValidateIdentityContext(Context, ticket, Options);
|
|
||||||
|
|
||||||
await Options.Notifications.ValidateIdentity(context);
|
|
||||||
|
|
||||||
return new AuthenticationTicket(context.Identity, context.Properties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyResponseGrant()
|
protected override void ApplyResponseGrant()
|
||||||
|
|
@ -118,9 +133,14 @@ namespace Microsoft.AspNet.Security.Cookies
|
||||||
var signout = SignOutContext;
|
var signout = SignOutContext;
|
||||||
bool shouldSignout = signout != null;
|
bool shouldSignout = signout != null;
|
||||||
|
|
||||||
if (shouldSignin || shouldSignout || _shouldRenew)
|
if (!(shouldSignin || shouldSignout || _shouldRenew))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthenticationTicket model = await AuthenticateAsync();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
AuthenticationTicket model = await AuthenticateAsync();
|
|
||||||
var cookieOptions = new CookieOptions
|
var cookieOptions = new CookieOptions
|
||||||
{
|
{
|
||||||
Domain = Options.CookieDomain,
|
Domain = Options.CookieDomain,
|
||||||
|
|
@ -274,6 +294,16 @@ namespace Microsoft.AspNet.Security.Cookies
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
CookieExceptionContext exceptionContext = new CookieExceptionContext(Context, Options,
|
||||||
|
CookieExceptionContext.ExceptionLocation.ApplyResponseGrant, exception, model);
|
||||||
|
Options.Notifications.Exception(exceptionContext);
|
||||||
|
if (exceptionContext.Rethrow)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsHostRelative(string path)
|
private static bool IsHostRelative(string path)
|
||||||
|
|
@ -308,24 +338,37 @@ namespace Microsoft.AspNet.Security.Cookies
|
||||||
loginUri = new AuthenticationProperties(ChallengeContext.Properties).RedirectUri;
|
loginUri = new AuthenticationProperties(ChallengeContext.Properties).RedirectUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(loginUri))
|
try
|
||||||
{
|
{
|
||||||
string currentUri =
|
if (string.IsNullOrWhiteSpace(loginUri))
|
||||||
Request.PathBase +
|
{
|
||||||
Request.Path +
|
string currentUri =
|
||||||
Request.QueryString;
|
Request.PathBase +
|
||||||
|
Request.Path +
|
||||||
|
Request.QueryString;
|
||||||
|
|
||||||
loginUri =
|
loginUri =
|
||||||
Request.Scheme +
|
Request.Scheme +
|
||||||
"://" +
|
"://" +
|
||||||
Request.Host +
|
Request.Host +
|
||||||
Request.PathBase +
|
Request.PathBase +
|
||||||
Options.LoginPath +
|
Options.LoginPath +
|
||||||
new QueryString(Options.ReturnUrlParameter, currentUri);
|
new QueryString(Options.ReturnUrlParameter, currentUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
var redirectContext = new CookieApplyRedirectContext(Context, Options, loginUri);
|
||||||
|
Options.Notifications.ApplyRedirect(redirectContext);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
CookieExceptionContext exceptionContext = new CookieExceptionContext(Context, Options,
|
||||||
|
CookieExceptionContext.ExceptionLocation.ApplyResponseChallenge, exception, ticket: null);
|
||||||
|
Options.Notifications.Exception(exceptionContext);
|
||||||
|
if (exceptionContext.Rethrow)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var redirectContext = new CookieApplyRedirectContext(Context, Options, loginUri);
|
|
||||||
Options.Notifications.ApplyRedirect(redirectContext);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ namespace Microsoft.AspNet.Security.Cookies
|
||||||
OnResponseSignedIn = context => { };
|
OnResponseSignedIn = context => { };
|
||||||
OnResponseSignOut = context => { };
|
OnResponseSignOut = context => { };
|
||||||
OnApplyRedirect = DefaultBehavior.ApplyRedirect;
|
OnApplyRedirect = DefaultBehavior.ApplyRedirect;
|
||||||
|
OnException = context => { };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -51,6 +52,11 @@ namespace Microsoft.AspNet.Security.Cookies
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<CookieApplyRedirectContext> OnApplyRedirect { get; set; }
|
public Action<CookieApplyRedirectContext> OnApplyRedirect { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A delegate assigned to this property will be invoked when the related method is called
|
||||||
|
/// </summary>
|
||||||
|
public Action<CookieExceptionContext> OnException { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements the interface method by invoking the related delegate method
|
/// Implements the interface method by invoking the related delegate method
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -96,5 +102,14 @@ namespace Microsoft.AspNet.Security.Cookies
|
||||||
{
|
{
|
||||||
OnApplyRedirect.Invoke(context);
|
OnApplyRedirect.Invoke(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implements the interface method by invoking the related delegate method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Contains information about the event</param>
|
||||||
|
public virtual void Exception(CookieExceptionContext context)
|
||||||
|
{
|
||||||
|
OnException.Invoke(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Security.Notifications;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Cookies
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Context object passed to the ICookieAuthenticationProvider method Exception.
|
||||||
|
/// </summary>
|
||||||
|
public class CookieExceptionContext : BaseContext<CookieAuthenticationOptions>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the context object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The HTTP request context</param>
|
||||||
|
/// <param name="options">The middleware options</param>
|
||||||
|
/// <param name="location">The location of the exception</param>
|
||||||
|
/// <param name="exception">The exception thrown.</param>
|
||||||
|
/// <param name="ticket">The current ticket, if any.</param>
|
||||||
|
public CookieExceptionContext(
|
||||||
|
HttpContext context,
|
||||||
|
CookieAuthenticationOptions options,
|
||||||
|
ExceptionLocation location,
|
||||||
|
Exception exception,
|
||||||
|
AuthenticationTicket ticket)
|
||||||
|
: base(context, options)
|
||||||
|
{
|
||||||
|
Location = location;
|
||||||
|
Exception = exception;
|
||||||
|
Rethrow = true;
|
||||||
|
Ticket = ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The code paths where exceptions may be reported.
|
||||||
|
/// </summary>
|
||||||
|
[SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type",
|
||||||
|
Target = "Microsoft.Owin.Security.Cookies.CookieExceptionContext+ExceptionLocation", Justification = "It is a directly related option.")]
|
||||||
|
public enum ExceptionLocation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The exception was reported in the Authenticate code path.
|
||||||
|
/// </summary>
|
||||||
|
Authenticate,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The exception was reported in the ApplyResponseGrant code path, during sign-in, sign-out, or refresh.
|
||||||
|
/// </summary>
|
||||||
|
ApplyResponseGrant,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The exception was reported in the ApplyResponseChallenge code path, during redirect generation.
|
||||||
|
/// </summary>
|
||||||
|
ApplyResponseChallenge,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The code path the exception occurred in.
|
||||||
|
/// </summary>
|
||||||
|
public ExceptionLocation Location { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The exception thrown.
|
||||||
|
/// </summary>
|
||||||
|
public Exception Exception { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if the exception should be re-thrown (default), false if it should be suppressed.
|
||||||
|
/// </summary>
|
||||||
|
public bool Rethrow { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current authentication ticket, if any.
|
||||||
|
/// In the AuthenticateAsync code path, if the given exception is not re-thrown then this ticket
|
||||||
|
/// will be returned to the application. The ticket may be replaced if needed.
|
||||||
|
/// </summary>
|
||||||
|
public AuthenticationTicket Ticket { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -43,5 +43,11 @@ namespace Microsoft.AspNet.Security.Cookies
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">Contains information about the login session as well as information about the authentication cookie.</param>
|
/// <param name="context">Contains information about the login session as well as information about the authentication cookie.</param>
|
||||||
void ResponseSignOut(CookieResponseSignOutContext context);
|
void ResponseSignOut(CookieResponseSignOutContext context);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when an exception occurs during request or response processing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Contains information about the exception that occurred</param>
|
||||||
|
void Exception(CookieExceptionContext context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue