#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()
|
||||
{
|
||||
string cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);
|
||||
if (string.IsNullOrWhiteSpace(cookie))
|
||||
AuthenticationTicket ticket = null;
|
||||
try
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
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)
|
||||
string cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);
|
||||
if (string.IsNullOrWhiteSpace(cookie))
|
||||
{
|
||||
_logger.WriteWarning(@"SessoinId missing");
|
||||
return null;
|
||||
}
|
||||
_sessionKey = claim.Value;
|
||||
ticket = await Options.SessionStore.RetrieveAsync(_sessionKey);
|
||||
|
||||
ticket = Options.TicketDataFormat.Unprotect(cookie);
|
||||
|
||||
if (ticket == null)
|
||||
{
|
||||
_logger.WriteWarning(@"Identity missing in session store");
|
||||
_logger.WriteWarning(@"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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
TimeSpan timeElapsed = currentUtc.Subtract(issuedUtc.Value);
|
||||
TimeSpan timeRemaining = expiresUtc.Value.Subtract(currentUtc);
|
||||
DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
|
||||
DateTimeOffset? issuedUtc = ticket.Properties.IssuedUtc;
|
||||
DateTimeOffset? expiresUtc = ticket.Properties.ExpiresUtc;
|
||||
|
||||
if (timeRemaining < timeElapsed)
|
||||
if (expiresUtc != null && expiresUtc.Value < currentUtc)
|
||||
{
|
||||
_shouldRenew = true;
|
||||
_renewIssuedUtc = currentUtc;
|
||||
TimeSpan timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value);
|
||||
_renewExpiresUtc = currentUtc.Add(timeSpan);
|
||||
if (Options.SessionStore != null)
|
||||
{
|
||||
await Options.SessionStore.RemoveAsync(_sessionKey);
|
||||
}
|
||||
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()
|
||||
|
|
@ -118,9 +133,14 @@ namespace Microsoft.AspNet.Security.Cookies
|
|||
var signout = SignOutContext;
|
||||
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
|
||||
{
|
||||
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)
|
||||
|
|
@ -308,24 +338,37 @@ namespace Microsoft.AspNet.Security.Cookies
|
|||
loginUri = new AuthenticationProperties(ChallengeContext.Properties).RedirectUri;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(loginUri))
|
||||
try
|
||||
{
|
||||
string currentUri =
|
||||
Request.PathBase +
|
||||
Request.Path +
|
||||
Request.QueryString;
|
||||
if (string.IsNullOrWhiteSpace(loginUri))
|
||||
{
|
||||
string currentUri =
|
||||
Request.PathBase +
|
||||
Request.Path +
|
||||
Request.QueryString;
|
||||
|
||||
loginUri =
|
||||
Request.Scheme +
|
||||
"://" +
|
||||
Request.Host +
|
||||
Request.PathBase +
|
||||
Options.LoginPath +
|
||||
new QueryString(Options.ReturnUrlParameter, currentUri);
|
||||
loginUri =
|
||||
Request.Scheme +
|
||||
"://" +
|
||||
Request.Host +
|
||||
Request.PathBase +
|
||||
Options.LoginPath +
|
||||
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 => { };
|
||||
OnResponseSignOut = context => { };
|
||||
OnApplyRedirect = DefaultBehavior.ApplyRedirect;
|
||||
OnException = context => { };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -51,6 +52,11 @@ namespace Microsoft.AspNet.Security.Cookies
|
|||
/// </summary>
|
||||
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>
|
||||
/// Implements the interface method by invoking the related delegate method
|
||||
/// </summary>
|
||||
|
|
@ -96,5 +102,14 @@ namespace Microsoft.AspNet.Security.Cookies
|
|||
{
|
||||
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>
|
||||
/// <param name="context">Contains information about the login session as well as information about the authentication cookie.</param>
|
||||
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