Cookies now always redirects to Login/AccessDenied Paths
This commit is contained in:
parent
b1013ed976
commit
b883920bef
|
|
@ -28,5 +28,19 @@ namespace Microsoft.AspNet.Builder
|
||||||
Name = optionsName
|
Name = optionsName
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a cookie-based authentication middleware to your web application pipeline.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="app">The IApplicationBuilder passed to your configuration method</param>
|
||||||
|
/// <param name="configureOptions">Used to configure the options for the middleware</param>
|
||||||
|
/// <param name="optionsName">The name of the options class that controls the middleware behavior, null will use the default options</param>
|
||||||
|
/// <returns>The original app parameter</returns>
|
||||||
|
public static IApplicationBuilder UseCookieAuthentication([NotNull] this IApplicationBuilder app, IOptions<CookieAuthenticationOptions> options)
|
||||||
|
{
|
||||||
|
return app.UseMiddleware<CookieAuthenticationMiddleware>(options,
|
||||||
|
new ConfigureOptions<CookieAuthenticationOptions>(o => { }));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -22,19 +22,26 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
public const string CookiePrefix = ".AspNet.";
|
public const string CookiePrefix = ".AspNet.";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default value used by UseApplicationSignInCookie for the
|
/// The default value used by CookieAuthenticationMiddleware for the
|
||||||
/// CookieAuthenticationOptions.LoginPath
|
/// CookieAuthenticationOptions.LoginPath
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "By design")]
|
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "By design")]
|
||||||
public static readonly PathString LoginPath = new PathString("/Account/Login");
|
public static readonly PathString LoginPath = new PathString("/Account/Login");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default value used by UseApplicationSignInCookie for the
|
/// The default value used by CookieAuthenticationMiddleware for the
|
||||||
/// CookieAuthenticationOptions.LogoutPath
|
/// CookieAuthenticationOptions.LogoutPath
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Logout", Justification = "By design")]
|
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Logout", Justification = "By design")]
|
||||||
public static readonly PathString LogoutPath = new PathString("/Account/Logout");
|
public static readonly PathString LogoutPath = new PathString("/Account/Logout");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default value used by CookieAuthenticationMiddleware for the
|
||||||
|
/// CookieAuthenticationOptions.AccessDeniedPath
|
||||||
|
/// </summary>
|
||||||
|
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "By design")]
|
||||||
|
public static readonly PathString AccessDeniedPath = new PathString("/Account/AccessDenied");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default value of the CookieAuthenticationOptions.ReturnUrlParameter
|
/// The default value of the CookieAuthenticationOptions.ReturnUrlParameter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -376,12 +376,6 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
|
|
||||||
protected override Task<bool> HandleForbiddenAsync(ChallengeContext context)
|
protected override Task<bool> HandleForbiddenAsync(ChallengeContext context)
|
||||||
{
|
{
|
||||||
// HandleForbidden by redirecting to AccessDeniedPath if set
|
|
||||||
if (!Options.AccessDeniedPath.HasValue)
|
|
||||||
{
|
|
||||||
return base.HandleForbiddenAsync(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var accessDeniedUri =
|
var accessDeniedUri =
|
||||||
|
|
@ -409,11 +403,6 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
|
|
||||||
protected override Task<bool> HandleUnauthorizedAsync([NotNull] ChallengeContext context)
|
protected override Task<bool> HandleUnauthorizedAsync([NotNull] ChallengeContext context)
|
||||||
{
|
{
|
||||||
if (!Options.LoginPath.HasValue)
|
|
||||||
{
|
|
||||||
return base.HandleUnauthorizedAsync(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
var redirectUri = new AuthenticationProperties(context.Properties).RedirectUri;
|
var redirectUri = new AuthenticationProperties(context.Properties).RedirectUri;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,18 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
{
|
{
|
||||||
Options.CookieManager = new ChunkingCookieManager(urlEncoder);
|
Options.CookieManager = new ChunkingCookieManager(urlEncoder);
|
||||||
}
|
}
|
||||||
|
if (!Options.LoginPath.HasValue)
|
||||||
|
{
|
||||||
|
Options.LoginPath = CookieAuthenticationDefaults.LoginPath;
|
||||||
|
}
|
||||||
|
if (!Options.LogoutPath.HasValue)
|
||||||
|
{
|
||||||
|
Options.LogoutPath = CookieAuthenticationDefaults.LogoutPath;
|
||||||
|
}
|
||||||
|
if (!Options.AccessDeniedPath.HasValue)
|
||||||
|
{
|
||||||
|
Options.AccessDeniedPath = CookieAuthenticationDefaults.AccessDeniedPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override AuthenticationHandler<CookieAuthenticationOptions> CreateHandler()
|
protected override AuthenticationHandler<CookieAuthenticationOptions> CreateHandler()
|
||||||
|
|
|
||||||
|
|
@ -88,9 +88,6 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
/// to the LoginPath as a query string parameter named by the ReturnUrlParameter. Once a request to the
|
/// to the LoginPath as a query string parameter named by the ReturnUrlParameter. Once a request to the
|
||||||
/// LoginPath grants a new SignIn identity, the ReturnUrlParameter value is used to redirect the browser back
|
/// LoginPath grants a new SignIn identity, the ReturnUrlParameter value is used to redirect the browser back
|
||||||
/// to the url which caused the original unauthorized status code.
|
/// to the url which caused the original unauthorized status code.
|
||||||
///
|
|
||||||
/// If the LoginPath is null or empty, the middleware will not look for 401 Unauthorized status codes, and it will
|
|
||||||
/// not redirect automatically when a login occurs.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "By design")]
|
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "By design")]
|
||||||
public PathString LoginPath { get; set; }
|
public PathString LoginPath { get; set; }
|
||||||
|
|
@ -104,9 +101,6 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The AccessDeniedPath property informs the middleware that it should change an outgoing 403 Forbidden status
|
/// The AccessDeniedPath property informs the middleware that it should change an outgoing 403 Forbidden status
|
||||||
/// code into a 302 redirection onto the given path.
|
/// code into a 302 redirection onto the given path.
|
||||||
///
|
|
||||||
/// If the AccessDeniedPath is null or empty, the middleware will not look for 403 Forbidden status codes, and it will
|
|
||||||
/// not redirect
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PathString AccessDeniedPath { get; set; }
|
public PathString AccessDeniedPath { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,11 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
||||||
ConfigureOptions<FacebookAuthenticationOptions> configureOptions = null)
|
ConfigureOptions<FacebookAuthenticationOptions> configureOptions = null)
|
||||||
: base(next, dataProtectionProvider, loggerFactory, encoder, sharedOptions, options, configureOptions)
|
: base(next, dataProtectionProvider, loggerFactory, encoder, sharedOptions, options, configureOptions)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Options.AppId))
|
if (string.IsNullOrEmpty(Options.AppId))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.AppId)));
|
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.AppId)));
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(Options.AppSecret))
|
if (string.IsNullOrEmpty(Options.AppSecret))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.AppSecret)));
|
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.AppSecret)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ namespace Microsoft.AspNet.Authentication.MicrosoftAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
var email = MicrosoftAccountAuthenticationHelper.GetEmail(payload);
|
var email = MicrosoftAccountAuthenticationHelper.GetEmail(payload);
|
||||||
if (!string.IsNullOrWhiteSpace(email))
|
if (!string.IsNullOrEmpty(email))
|
||||||
{
|
{
|
||||||
identity.AddClaim(new Claim(ClaimTypes.Email, email, ClaimValueTypes.String, Options.ClaimsIssuer));
|
identity.AddClaim(new Claim(ClaimTypes.Email, email, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
|
|
||||||
var tokens = await ExchangeCodeAsync(code, BuildRedirectUri(Options.CallbackPath));
|
var tokens = await ExchangeCodeAsync(code, BuildRedirectUri(Options.CallbackPath));
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(tokens.AccessToken))
|
if (string.IsNullOrEmpty(tokens.AccessToken))
|
||||||
{
|
{
|
||||||
Logger.LogWarning("Access token was not found");
|
Logger.LogWarning("Access token was not found");
|
||||||
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
||||||
|
|
@ -279,7 +279,7 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
{
|
{
|
||||||
var correlationKey = Constants.CorrelationPrefix + Options.AuthenticationScheme;
|
var correlationKey = Constants.CorrelationPrefix + Options.AuthenticationScheme;
|
||||||
var correlationCookie = Request.Cookies[correlationKey];
|
var correlationCookie = Request.Cookies[correlationKey];
|
||||||
if (string.IsNullOrWhiteSpace(correlationCookie))
|
if (string.IsNullOrEmpty(correlationCookie))
|
||||||
{
|
{
|
||||||
Logger.LogWarning("{0} cookie not found.", correlationKey);
|
Logger.LogWarning("{0} cookie not found.", correlationKey);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -38,27 +38,27 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
: base(next, options, loggerFactory, encoder, configureOptions)
|
: base(next, options, loggerFactory, encoder, configureOptions)
|
||||||
{
|
{
|
||||||
// todo: review error handling
|
// todo: review error handling
|
||||||
if (string.IsNullOrWhiteSpace(Options.AuthenticationScheme))
|
if (string.IsNullOrEmpty(Options.AuthenticationScheme))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.AuthenticationScheme)));
|
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.AuthenticationScheme)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Options.ClientId))
|
if (string.IsNullOrEmpty(Options.ClientId))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.ClientId)));
|
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.ClientId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Options.ClientSecret))
|
if (string.IsNullOrEmpty(Options.ClientSecret))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.ClientSecret)));
|
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.ClientSecret)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Options.AuthorizationEndpoint))
|
if (string.IsNullOrEmpty(Options.AuthorizationEndpoint))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.AuthorizationEndpoint)));
|
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.AuthorizationEndpoint)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Options.TokenEndpoint))
|
if (string.IsNullOrEmpty(Options.TokenEndpoint))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.TokenEndpoint)));
|
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.TokenEndpoint)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
||||||
var validationParameters = Options.TokenValidationParameters.Clone();
|
var validationParameters = Options.TokenValidationParameters.Clone();
|
||||||
if (_configuration != null)
|
if (_configuration != null)
|
||||||
{
|
{
|
||||||
if (validationParameters.ValidIssuer == null && !string.IsNullOrWhiteSpace(_configuration.Issuer))
|
if (validationParameters.ValidIssuer == null && !string.IsNullOrEmpty(_configuration.Issuer))
|
||||||
{
|
{
|
||||||
validationParameters.ValidIssuer = _configuration.Issuer;
|
validationParameters.ValidIssuer = _configuration.Issuer;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
||||||
Options.SecurityTokenValidators = new List<ISecurityTokenValidator> { new JwtSecurityTokenHandler() };
|
Options.SecurityTokenValidators = new List<ISecurityTokenValidator> { new JwtSecurityTokenHandler() };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Options.TokenValidationParameters.ValidAudience) && !string.IsNullOrWhiteSpace(Options.Audience))
|
if (string.IsNullOrEmpty(Options.TokenValidationParameters.ValidAudience) && !string.IsNullOrEmpty(Options.Audience))
|
||||||
{
|
{
|
||||||
Options.TokenValidationParameters.ValidAudience = Options.Audience;
|
Options.TokenValidationParameters.ValidAudience = Options.Audience;
|
||||||
}
|
}
|
||||||
|
|
@ -59,9 +59,9 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
||||||
{
|
{
|
||||||
Options.ConfigurationManager = new StaticConfigurationManager<OpenIdConnectConfiguration>(Options.Configuration);
|
Options.ConfigurationManager = new StaticConfigurationManager<OpenIdConnectConfiguration>(Options.Configuration);
|
||||||
}
|
}
|
||||||
else if (!(string.IsNullOrWhiteSpace(Options.MetadataAddress) && string.IsNullOrWhiteSpace(Options.Authority)))
|
else if (!(string.IsNullOrEmpty(Options.MetadataAddress) && string.IsNullOrEmpty(Options.Authority)))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Options.MetadataAddress) && !string.IsNullOrWhiteSpace(Options.Authority))
|
if (string.IsNullOrEmpty(Options.MetadataAddress) && !string.IsNullOrEmpty(Options.Authority))
|
||||||
{
|
{
|
||||||
Options.MetadataAddress = Options.Authority;
|
Options.MetadataAddress = Options.Authority;
|
||||||
if (!Options.MetadataAddress.EndsWith("/", StringComparison.Ordinal))
|
if (!Options.MetadataAddress.EndsWith("/", StringComparison.Ordinal))
|
||||||
|
|
|
||||||
|
|
@ -251,7 +251,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
// response_mode=query (explicit or not) and a response_type containing id_token
|
// response_mode=query (explicit or not) and a response_type containing id_token
|
||||||
// or token are not considered as a safe combination and MUST be rejected.
|
// or token are not considered as a safe combination and MUST be rejected.
|
||||||
// See http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Security
|
// See http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Security
|
||||||
if (!string.IsNullOrWhiteSpace(message.IdToken) || !string.IsNullOrWhiteSpace(message.Token))
|
if (!string.IsNullOrEmpty(message.IdToken) || !string.IsNullOrEmpty(message.Token))
|
||||||
{
|
{
|
||||||
Logger.LogError("An OpenID Connect response cannot contain an identity token " +
|
Logger.LogError("An OpenID Connect response cannot contain an identity token " +
|
||||||
"or an access token when using response_mode=query");
|
"or an access token when using response_mode=query");
|
||||||
|
|
@ -260,7 +260,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
// assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small.
|
// assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small.
|
||||||
else if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)
|
else if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)
|
||||||
&& !string.IsNullOrWhiteSpace(Request.ContentType)
|
&& !string.IsNullOrEmpty(Request.ContentType)
|
||||||
// May have media/type; charset=utf-8, allow partial match.
|
// May have media/type; charset=utf-8, allow partial match.
|
||||||
&& Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)
|
&& Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)
|
||||||
&& Request.Body.CanRead)
|
&& Request.Body.CanRead)
|
||||||
|
|
@ -643,7 +643,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
{
|
{
|
||||||
// assume a well formed query string: <a=b&>OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey=kasjd;fljasldkjflksdj<&c=d>
|
// assume a well formed query string: <a=b&>OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey=kasjd;fljasldkjflksdj<&c=d>
|
||||||
var startIndex = 0;
|
var startIndex = 0;
|
||||||
if (string.IsNullOrWhiteSpace(state) || (startIndex = state.IndexOf(OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey, StringComparison.Ordinal)) == -1)
|
if (string.IsNullOrEmpty(state) || (startIndex = state.IndexOf(OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey, StringComparison.Ordinal)) == -1)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -934,7 +934,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect back to the original secured resource, if any.
|
// Redirect back to the original secured resource, if any.
|
||||||
if (!string.IsNullOrWhiteSpace(ticket.Properties.RedirectUri))
|
if (!string.IsNullOrEmpty(ticket.Properties.RedirectUri))
|
||||||
{
|
{
|
||||||
Response.Redirect(ticket.Properties.RedirectUri);
|
Response.Redirect(ticket.Properties.RedirectUri);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
Options.Notifications = new OpenIdConnectAuthenticationNotifications();
|
Options.Notifications = new OpenIdConnectAuthenticationNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Options.TokenValidationParameters.ValidAudience) && !string.IsNullOrWhiteSpace(Options.ClientId))
|
if (string.IsNullOrEmpty(Options.TokenValidationParameters.ValidAudience) && !string.IsNullOrEmpty(Options.ClientId))
|
||||||
{
|
{
|
||||||
Options.TokenValidationParameters.ValidAudience = Options.ClientId;
|
Options.TokenValidationParameters.ValidAudience = Options.ClientId;
|
||||||
}
|
}
|
||||||
|
|
@ -116,9 +116,9 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
{
|
{
|
||||||
Options.ConfigurationManager = new StaticConfigurationManager<OpenIdConnectConfiguration>(Options.Configuration);
|
Options.ConfigurationManager = new StaticConfigurationManager<OpenIdConnectConfiguration>(Options.Configuration);
|
||||||
}
|
}
|
||||||
else if (!(string.IsNullOrWhiteSpace(Options.MetadataAddress) && string.IsNullOrWhiteSpace(Options.Authority)))
|
else if (!(string.IsNullOrEmpty(Options.MetadataAddress) && string.IsNullOrEmpty(Options.Authority)))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Options.MetadataAddress) && !string.IsNullOrWhiteSpace(Options.Authority))
|
if (string.IsNullOrEmpty(Options.MetadataAddress) && !string.IsNullOrEmpty(Options.Authority))
|
||||||
{
|
{
|
||||||
Options.MetadataAddress = Options.Authority;
|
Options.MetadataAddress = Options.Authority;
|
||||||
if (!Options.MetadataAddress.EndsWith("/", StringComparison.Ordinal))
|
if (!Options.MetadataAddress.EndsWith("/", StringComparison.Ordinal))
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
||||||
properties = requestToken.Properties;
|
properties = requestToken.Properties;
|
||||||
|
|
||||||
var returnedToken = query.Get("oauth_token");
|
var returnedToken = query.Get("oauth_token");
|
||||||
if (string.IsNullOrWhiteSpace(returnedToken))
|
if (string.IsNullOrEmpty(returnedToken))
|
||||||
{
|
{
|
||||||
Logger.LogWarning("Missing oauth_token");
|
Logger.LogWarning("Missing oauth_token");
|
||||||
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
||||||
|
|
@ -75,7 +75,7 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
||||||
}
|
}
|
||||||
|
|
||||||
var oauthVerifier = query.Get("oauth_verifier");
|
var oauthVerifier = query.Get("oauth_verifier");
|
||||||
if (string.IsNullOrWhiteSpace(oauthVerifier))
|
if (string.IsNullOrEmpty(oauthVerifier))
|
||||||
{
|
{
|
||||||
Logger.LogWarning("Missing or blank oauth_verifier");
|
Logger.LogWarning("Missing or blank oauth_verifier");
|
||||||
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,11 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
||||||
ConfigureOptions<TwitterAuthenticationOptions> configureOptions = null)
|
ConfigureOptions<TwitterAuthenticationOptions> configureOptions = null)
|
||||||
: base(next, options, loggerFactory, encoder, configureOptions)
|
: base(next, options, loggerFactory, encoder, configureOptions)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Options.ConsumerSecret))
|
if (string.IsNullOrEmpty(Options.ConsumerSecret))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.ConsumerSecret)));
|
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.ConsumerSecret)));
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(Options.ConsumerKey))
|
if (string.IsNullOrEmpty(Options.ConsumerKey))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.ConsumerKey)));
|
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.ConsumerKey)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ namespace Microsoft.AspNet.Authentication
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for the per-request work performed by most authentication middleware.
|
/// Base class for the per-request work performed by most authentication middleware.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class AuthenticationHandler : IAuthenticationHandler
|
/// <typeparam name="TOptions">Specifies which type for of AuthenticationOptions property</typeparam>
|
||||||
|
public abstract class AuthenticationHandler<TOptions> : IAuthenticationHandler where TOptions : AuthenticationOptions
|
||||||
{
|
{
|
||||||
private Task<AuthenticationTicket> _authenticateTask;
|
private Task<AuthenticationTicket> _authenticateTask;
|
||||||
private bool _finishCalled;
|
private bool _finishCalled;
|
||||||
private AuthenticationOptions _baseOptions;
|
|
||||||
|
|
||||||
protected bool SignInAccepted { get; set; }
|
protected bool SignInAccepted { get; set; }
|
||||||
protected bool SignOutAccepted { get; set; }
|
protected bool SignOutAccepted { get; set; }
|
||||||
|
|
@ -44,11 +44,6 @@ namespace Microsoft.AspNet.Authentication
|
||||||
|
|
||||||
protected IUrlEncoder UrlEncoder { get; private set; }
|
protected IUrlEncoder UrlEncoder { get; private set; }
|
||||||
|
|
||||||
internal AuthenticationOptions BaseOptions
|
|
||||||
{
|
|
||||||
get { return _baseOptions; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public IAuthenticationHandler PriorHandler { get; set; }
|
public IAuthenticationHandler PriorHandler { get; set; }
|
||||||
|
|
||||||
protected string CurrentUri
|
protected string CurrentUri
|
||||||
|
|
@ -59,9 +54,18 @@ namespace Microsoft.AspNet.Authentication
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task BaseInitializeAsync([NotNull] AuthenticationOptions options, [NotNull] HttpContext context, [NotNull] ILogger logger, [NotNull] IUrlEncoder encoder)
|
protected TOptions Options { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize is called once per request to contextualize this instance with appropriate state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The original options passed by the application control behavior</param>
|
||||||
|
/// <param name="context">The utility object to observe the current request and response</param>
|
||||||
|
/// <param name="logger">The logging factory used to create loggers</param>
|
||||||
|
/// <returns>async completion</returns>
|
||||||
|
public async Task InitializeAsync([NotNull] TOptions options, [NotNull] HttpContext context, [NotNull] ILogger logger, [NotNull] IUrlEncoder encoder)
|
||||||
{
|
{
|
||||||
_baseOptions = options;
|
Options = options;
|
||||||
Context = context;
|
Context = context;
|
||||||
OriginalPathBase = Request.PathBase;
|
OriginalPathBase = Request.PathBase;
|
||||||
OriginalPath = Request.Path;
|
OriginalPath = Request.Path;
|
||||||
|
|
@ -90,7 +94,7 @@ namespace Microsoft.AspNet.Authentication
|
||||||
|
|
||||||
private static async Task OnStartingCallback(object state)
|
private static async Task OnStartingCallback(object state)
|
||||||
{
|
{
|
||||||
var handler = (AuthenticationHandler)state;
|
var handler = (AuthenticationHandler<TOptions>)state;
|
||||||
await handler.FinishResponseOnce();
|
await handler.FinishResponseOnce();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,9 +119,9 @@ namespace Microsoft.AspNet.Authentication
|
||||||
|
|
||||||
private async Task HandleAutomaticChallengeIfNeeded()
|
private async Task HandleAutomaticChallengeIfNeeded()
|
||||||
{
|
{
|
||||||
if (!ChallengeCalled && BaseOptions.AutomaticAuthentication && Response.StatusCode == 401)
|
if (!ChallengeCalled && Options.AutomaticAuthentication && Response.StatusCode == 401)
|
||||||
{
|
{
|
||||||
await HandleUnauthorizedAsync(new ChallengeContext(BaseOptions.AuthenticationScheme));
|
await HandleUnauthorizedAsync(new ChallengeContext(Options.AuthenticationScheme));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,7 +156,7 @@ namespace Microsoft.AspNet.Authentication
|
||||||
|
|
||||||
public void GetDescriptions(DescribeSchemesContext describeContext)
|
public void GetDescriptions(DescribeSchemesContext describeContext)
|
||||||
{
|
{
|
||||||
describeContext.Accept(BaseOptions.Description.Items);
|
describeContext.Accept(Options.Description.Items);
|
||||||
|
|
||||||
if (PriorHandler != null)
|
if (PriorHandler != null)
|
||||||
{
|
{
|
||||||
|
|
@ -162,8 +166,8 @@ namespace Microsoft.AspNet.Authentication
|
||||||
|
|
||||||
public bool ShouldHandleScheme(string authenticationScheme)
|
public bool ShouldHandleScheme(string authenticationScheme)
|
||||||
{
|
{
|
||||||
return string.Equals(BaseOptions.AuthenticationScheme, authenticationScheme, StringComparison.Ordinal) ||
|
return string.Equals(Options.AuthenticationScheme, authenticationScheme, StringComparison.Ordinal) ||
|
||||||
(BaseOptions.AutomaticAuthentication && string.IsNullOrEmpty(authenticationScheme));
|
(Options.AutomaticAuthentication && string.IsNullOrEmpty(authenticationScheme));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AuthenticateAsync(AuthenticateContext context)
|
public async Task AuthenticateAsync(AuthenticateContext context)
|
||||||
|
|
@ -174,7 +178,7 @@ namespace Microsoft.AspNet.Authentication
|
||||||
var ticket = await HandleAuthenticateOnceAsync();
|
var ticket = await HandleAuthenticateOnceAsync();
|
||||||
if (ticket?.Principal != null)
|
if (ticket?.Principal != null)
|
||||||
{
|
{
|
||||||
context.Authenticated(ticket.Principal, ticket.Properties.Items, BaseOptions.Description.Items);
|
context.Authenticated(ticket.Principal, ticket.Properties.Items, Options.Description.Items);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.Framework.Logging;
|
|
||||||
using Microsoft.Framework.WebEncoders;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Base class for the per-request work performed by most authentication middleware.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TOptions">Specifies which type for of AuthenticationOptions property</typeparam>
|
|
||||||
public abstract class AuthenticationHandler<TOptions> : AuthenticationHandler where TOptions : AuthenticationOptions
|
|
||||||
{
|
|
||||||
protected TOptions Options { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize is called once per request to contextualize this instance with appropriate state.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="options">The original options passed by the application control behavior</param>
|
|
||||||
/// <param name="context">The utility object to observe the current request and response</param>
|
|
||||||
/// <param name="logger">The logging factory used to create loggers</param>
|
|
||||||
/// <returns>async completion</returns>
|
|
||||||
public Task Initialize(TOptions options, HttpContext context, ILogger logger, IUrlEncoder encoder)
|
|
||||||
{
|
|
||||||
Options = options;
|
|
||||||
return BaseInitializeAsync(options, context, logger, encoder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNet.Authentication
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(HttpContext context)
|
||||||
{
|
{
|
||||||
var handler = CreateHandler();
|
var handler = CreateHandler();
|
||||||
await handler.Initialize(Options, context, Logger, UrlEncoder);
|
await handler.InitializeAsync(Options, context, Logger, UrlEncoder);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!await handler.InvokeAsync())
|
if (!await handler.InvokeAsync())
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,12 @@ namespace Microsoft.Framework.DependencyInjection
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddAuthentication([NotNull] this IServiceCollection services, [NotNull] Action<SharedAuthenticationOptions> configureOptions)
|
||||||
|
{
|
||||||
|
services.Configure(configureOptions);
|
||||||
|
return services.AddAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
public static IServiceCollection ConfigureClaimsTransformation([NotNull] this IServiceCollection services, [NotNull] Action<ClaimsTransformationOptions> configure)
|
public static IServiceCollection ConfigureClaimsTransformation([NotNull] this IServiceCollection services, [NotNull] Action<ClaimsTransformationOptions> configure)
|
||||||
{
|
{
|
||||||
return services.Configure(configure);
|
return services.Configure(configure);
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNet.Authentication
|
||||||
foreach (var chainElement in chain.ChainElements)
|
foreach (var chainElement in chain.ChainElements)
|
||||||
{
|
{
|
||||||
string subjectKeyIdentifier = GetSubjectKeyIdentifier(chainElement.Certificate);
|
string subjectKeyIdentifier = GetSubjectKeyIdentifier(chainElement.Certificate);
|
||||||
if (string.IsNullOrWhiteSpace(subjectKeyIdentifier))
|
if (string.IsNullOrEmpty(subjectKeyIdentifier))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Features.Authentication;
|
using Microsoft.AspNet.Http.Features.Authentication;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using Microsoft.Framework.Logging;
|
using Microsoft.Framework.Logging;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication
|
namespace Microsoft.AspNet.Authentication
|
||||||
|
|
@ -13,12 +15,12 @@ namespace Microsoft.AspNet.Authentication
|
||||||
public class AuthenticationHandlerFacts
|
public class AuthenticationHandlerFacts
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ShouldHandleSchemeAreDeterminedOnlyByMatchingAuthenticationScheme()
|
public async Task ShouldHandleSchemeAreDeterminedOnlyByMatchingAuthenticationScheme()
|
||||||
{
|
{
|
||||||
var handler = new TestHandler("Alpha");
|
var handler = await TestHandler.Create("Alpha");
|
||||||
var passiveNoMatch = handler.ShouldHandleScheme("Beta");
|
var passiveNoMatch = handler.ShouldHandleScheme("Beta");
|
||||||
|
|
||||||
handler = new TestHandler("Alpha");
|
handler = await TestHandler.Create("Alpha");
|
||||||
var passiveWithMatch = handler.ShouldHandleScheme("Alpha");
|
var passiveWithMatch = handler.ShouldHandleScheme("Alpha");
|
||||||
|
|
||||||
Assert.False(passiveNoMatch);
|
Assert.False(passiveNoMatch);
|
||||||
|
|
@ -26,44 +28,44 @@ namespace Microsoft.AspNet.Authentication
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AutomaticHandlerInAutomaticModeHandlesEmptyChallenges()
|
public async Task AutomaticHandlerInAutomaticModeHandlesEmptyChallenges()
|
||||||
{
|
{
|
||||||
var handler = new TestAutoHandler("ignored", true);
|
var handler = await TestAutoHandler.Create("ignored", true);
|
||||||
Assert.True(handler.ShouldHandleScheme(""));
|
Assert.True(handler.ShouldHandleScheme(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AutomaticHandlerHandlesNullScheme()
|
public async Task AutomaticHandlerHandlesNullScheme()
|
||||||
{
|
{
|
||||||
var handler = new TestAutoHandler("ignored", true);
|
var handler = await TestAutoHandler.Create("ignored", true);
|
||||||
Assert.True(handler.ShouldHandleScheme(null));
|
Assert.True(handler.ShouldHandleScheme(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AutomaticHandlerIgnoresWhitespaceScheme()
|
public async Task AutomaticHandlerIgnoresWhitespaceScheme()
|
||||||
{
|
{
|
||||||
var handler = new TestAutoHandler("ignored", true);
|
var handler = await TestAutoHandler.Create("ignored", true);
|
||||||
Assert.False(handler.ShouldHandleScheme(" "));
|
Assert.False(handler.ShouldHandleScheme(" "));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AutomaticHandlerShouldHandleSchemeWhenSchemeMatches()
|
public async Task AutomaticHandlerShouldHandleSchemeWhenSchemeMatches()
|
||||||
{
|
{
|
||||||
var handler = new TestAutoHandler("Alpha", true);
|
var handler = await TestAutoHandler.Create("Alpha", true);
|
||||||
Assert.True(handler.ShouldHandleScheme("Alpha"));
|
Assert.True(handler.ShouldHandleScheme("Alpha"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AutomaticHandlerShouldNotHandleChallengeWhenSchemeDoesNotMatches()
|
public async Task AutomaticHandlerShouldNotHandleChallengeWhenSchemeDoesNotMatches()
|
||||||
{
|
{
|
||||||
var handler = new TestAutoHandler("Dog", true);
|
var handler = await TestAutoHandler.Create("Dog", true);
|
||||||
Assert.False(handler.ShouldHandleScheme("Alpha"));
|
Assert.False(handler.ShouldHandleScheme("Alpha"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AutomaticHandlerShouldNotHandleChallengeWhenSchemesNotEmpty()
|
public async Task AutomaticHandlerShouldNotHandleChallengeWhenSchemesNotEmpty()
|
||||||
{
|
{
|
||||||
var handler = new TestAutoHandler(null, true);
|
var handler = await TestAutoHandler.Create(null, true);
|
||||||
Assert.False(handler.ShouldHandleScheme("Alpha"));
|
Assert.False(handler.ShouldHandleScheme("Alpha"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,22 +74,31 @@ namespace Microsoft.AspNet.Authentication
|
||||||
[InlineData("")]
|
[InlineData("")]
|
||||||
public async Task AuthHandlerAuthenticateCachesTicket(string scheme)
|
public async Task AuthHandlerAuthenticateCachesTicket(string scheme)
|
||||||
{
|
{
|
||||||
var handler = new CountHandler(scheme);
|
var handler = await CountHandler.Create(scheme);
|
||||||
var context = new AuthenticateContext(scheme);
|
var context = new AuthenticateContext(scheme);
|
||||||
await handler.AuthenticateAsync(context);
|
await handler.AuthenticateAsync(context);
|
||||||
await handler.AuthenticateAsync(context);
|
await handler.AuthenticateAsync(context);
|
||||||
Assert.Equal(1, handler.AuthCount);
|
Assert.Equal(1, handler.AuthCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CountHandler : AuthenticationHandler<AuthenticationOptions>
|
private class CountOptions : AuthenticationOptions { }
|
||||||
|
|
||||||
|
private class CountHandler : AuthenticationHandler<CountOptions>
|
||||||
{
|
{
|
||||||
public int AuthCount = 0;
|
public int AuthCount = 0;
|
||||||
|
|
||||||
public CountHandler(string scheme)
|
private CountHandler() { }
|
||||||
|
|
||||||
|
public static async Task<CountHandler> Create(string scheme)
|
||||||
{
|
{
|
||||||
Initialize(new TestOptions(), new DefaultHttpContext(), new LoggerFactory().CreateLogger("TestHandler"), Framework.WebEncoders.UrlEncoder.Default);
|
var handler = new CountHandler();
|
||||||
Options.AuthenticationScheme = scheme;
|
var context = new Mock<HttpContext>();
|
||||||
Options.AutomaticAuthentication = true;
|
context.Setup(c => c.Request).Returns(new Mock<HttpRequest>().Object);
|
||||||
|
context.Setup(c => c.Response).Returns(new Mock<HttpResponse>().Object);
|
||||||
|
await handler.InitializeAsync(new CountOptions(), context.Object, new LoggerFactory().CreateLogger("CountHandler"), Framework.WebEncoders.UrlEncoder.Default);
|
||||||
|
handler.Options.AuthenticationScheme = scheme;
|
||||||
|
handler.Options.AutomaticAuthentication = true;
|
||||||
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task<AuthenticationTicket> HandleAuthenticateAsync()
|
protected override Task<AuthenticationTicket> HandleAuthenticateAsync()
|
||||||
|
|
@ -98,17 +109,24 @@ namespace Microsoft.AspNet.Authentication
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestHandler : AuthenticationHandler<AuthenticationOptions>
|
private class TestHandler : AuthenticationHandler<TestOptions>
|
||||||
{
|
{
|
||||||
public TestHandler(string scheme)
|
private TestHandler() { }
|
||||||
|
|
||||||
|
public static async Task<TestHandler> Create(string scheme)
|
||||||
{
|
{
|
||||||
Initialize(new TestOptions(), new DefaultHttpContext(), new LoggerFactory().CreateLogger("TestHandler"), Framework.WebEncoders.UrlEncoder.Default);
|
var handler = new TestHandler();
|
||||||
Options.AuthenticationScheme = scheme;
|
var context = new Mock<HttpContext>();
|
||||||
|
context.Setup(c => c.Request).Returns(new Mock<HttpRequest>().Object);
|
||||||
|
context.Setup(c => c.Response).Returns(new Mock<HttpResponse>().Object);
|
||||||
|
await handler.InitializeAsync(new TestOptions(), context.Object, new LoggerFactory().CreateLogger("TestHandler"), Framework.WebEncoders.UrlEncoder.Default);
|
||||||
|
handler.Options.AuthenticationScheme = scheme;
|
||||||
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task<AuthenticationTicket> HandleAuthenticateAsync()
|
protected override Task<AuthenticationTicket> HandleAuthenticateAsync()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return Task.FromResult<AuthenticationTicket>(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,16 +142,23 @@ namespace Microsoft.AspNet.Authentication
|
||||||
|
|
||||||
private class TestAutoHandler : AuthenticationHandler<TestAutoOptions>
|
private class TestAutoHandler : AuthenticationHandler<TestAutoOptions>
|
||||||
{
|
{
|
||||||
public TestAutoHandler(string scheme, bool auto)
|
private TestAutoHandler() { }
|
||||||
|
|
||||||
|
public static async Task<TestAutoHandler> Create(string scheme, bool auto)
|
||||||
{
|
{
|
||||||
Initialize(new TestAutoOptions(), new DefaultHttpContext(), new LoggerFactory().CreateLogger("TestHandler"), Framework.WebEncoders.UrlEncoder.Default);
|
var handler = new TestAutoHandler();
|
||||||
Options.AuthenticationScheme = scheme;
|
var context = new Mock<HttpContext>();
|
||||||
Options.AutomaticAuthentication = auto;
|
context.Setup(c => c.Request).Returns(new Mock<HttpRequest>().Object);
|
||||||
|
context.Setup(c => c.Response).Returns(new Mock<HttpResponse>().Object);
|
||||||
|
await handler.InitializeAsync(new TestAutoOptions(), context.Object, new LoggerFactory().CreateLogger("TestAutoHandler"), Framework.WebEncoders.UrlEncoder.Default);
|
||||||
|
handler.Options.AuthenticationScheme = scheme;
|
||||||
|
handler.Options.AutomaticAuthentication = auto;
|
||||||
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task<AuthenticationTicket> HandleAuthenticateAsync()
|
protected override Task<AuthenticationTicket> HandleAuthenticateAsync()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return Task.FromResult<AuthenticationTicket>(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,26 +57,16 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Fact]
|
||||||
[InlineData(true)]
|
public async Task ProtectedCustomRequestShouldRedirectToCustomRedirectUri()
|
||||||
[InlineData(false)]
|
|
||||||
public async Task ProtectedCustomRequestShouldRedirectToCustomRedirectUri(bool auto)
|
|
||||||
{
|
{
|
||||||
var server = CreateServer(options =>
|
var server = CreateServer(options => options.AutomaticAuthentication = true);
|
||||||
{
|
|
||||||
options.LoginPath = new PathString("/login");
|
|
||||||
options.AutomaticAuthentication = auto;
|
|
||||||
});
|
|
||||||
|
|
||||||
var transaction = await SendAsync(server, "http://example.com/protected/CustomRedirect");
|
var transaction = await SendAsync(server, "http://example.com/protected/CustomRedirect");
|
||||||
|
|
||||||
// REVIEW: Now when Cookies are not in auto, noone handles the challenge so the Status stays OK, is that reasonable??
|
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||||
transaction.Response.StatusCode.ShouldBe(auto ? HttpStatusCode.Redirect : HttpStatusCode.OK);
|
var location = transaction.Response.Headers.Location;
|
||||||
if (auto)
|
location.ToString().ShouldBe("http://example.com/Account/Login?ReturnUrl=%2FCustomRedirect");
|
||||||
{
|
|
||||||
var location = transaction.Response.Headers.Location;
|
|
||||||
location.ToString().ShouldBe("http://example.com/login?ReturnUrl=%2FCustomRedirect");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SignInAsAlice(HttpContext context)
|
private Task SignInAsAlice(HttpContext context)
|
||||||
|
|
@ -588,7 +578,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(true)]
|
[InlineData(true)]
|
||||||
[InlineData(false)]
|
[InlineData(false)]
|
||||||
public async Task CookieTurns401To403WithCookie(bool automatic)
|
public async Task CookieTurnsChallengeIntoForbidWithCookie(bool automatic)
|
||||||
{
|
{
|
||||||
var clock = new TestClock();
|
var clock = new TestClock();
|
||||||
var server = CreateServer(options =>
|
var server = CreateServer(options =>
|
||||||
|
|
@ -603,7 +593,9 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
var url = "http://example.com/challenge";
|
var url = "http://example.com/challenge";
|
||||||
var transaction2 = await SendAsync(server, url, transaction1.CookieNameValue);
|
var transaction2 = await SendAsync(server, url, transaction1.CookieNameValue);
|
||||||
|
|
||||||
transaction2.Response.StatusCode.ShouldBe(HttpStatusCode.Forbidden);
|
transaction2.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||||
|
var location = transaction2.Response.Headers.Location;
|
||||||
|
location.LocalPath.ShouldBe("/Account/AccessDenied");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -614,9 +606,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
var clock = new TestClock();
|
var clock = new TestClock();
|
||||||
var server = CreateServer(options =>
|
var server = CreateServer(options =>
|
||||||
{
|
{
|
||||||
options.LoginPath = new PathString("/login");
|
|
||||||
options.AutomaticAuthentication = automatic;
|
options.AutomaticAuthentication = automatic;
|
||||||
options.AccessDeniedPath = new PathString("/accessdenied");
|
|
||||||
options.SystemClock = clock;
|
options.SystemClock = clock;
|
||||||
},
|
},
|
||||||
SignInAsAlice);
|
SignInAsAlice);
|
||||||
|
|
@ -626,13 +616,13 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
|
|
||||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||||
var location = transaction.Response.Headers.Location;
|
var location = transaction.Response.Headers.Location;
|
||||||
location.LocalPath.ShouldBe("/login");
|
location.LocalPath.ShouldBe("/Account/Login");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(true)]
|
[InlineData(true)]
|
||||||
[InlineData(false)]
|
[InlineData(false)]
|
||||||
public async Task CookieForbidTurns401To403WithoutCookie(bool automatic)
|
public async Task CookieForbidRedirectsWithoutCookie(bool automatic)
|
||||||
{
|
{
|
||||||
var clock = new TestClock();
|
var clock = new TestClock();
|
||||||
var server = CreateServer(options =>
|
var server = CreateServer(options =>
|
||||||
|
|
@ -645,7 +635,9 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
var url = "http://example.com/forbid";
|
var url = "http://example.com/forbid";
|
||||||
var transaction = await SendAsync(server, url);
|
var transaction = await SendAsync(server, url);
|
||||||
|
|
||||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Forbidden);
|
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||||
|
var location = transaction.Response.Headers.Location;
|
||||||
|
location.LocalPath.ShouldBe("/Account/AccessDenied");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -670,19 +662,20 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CookieChallengeDoesNothingIfNotAuthenticated()
|
public async Task CookieChallengeRedirectsWithLoginPath()
|
||||||
{
|
{
|
||||||
var clock = new TestClock();
|
var clock = new TestClock();
|
||||||
var server = CreateServer(options =>
|
var server = CreateServer(options =>
|
||||||
{
|
{
|
||||||
options.SystemClock = clock;
|
options.SystemClock = clock;
|
||||||
|
options.LoginPath = new PathString("/page");
|
||||||
});
|
});
|
||||||
|
|
||||||
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
||||||
|
|
||||||
var transaction2 = await SendAsync(server, "http://example.com/challenge", transaction1.CookieNameValue);
|
var transaction2 = await SendAsync(server, "http://example.com/challenge", transaction1.CookieNameValue);
|
||||||
|
|
||||||
transaction2.Response.StatusCode.ShouldBe(HttpStatusCode.Unauthorized);
|
transaction2.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -692,13 +685,14 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
var server = CreateServer(options =>
|
var server = CreateServer(options =>
|
||||||
{
|
{
|
||||||
options.SystemClock = clock;
|
options.SystemClock = clock;
|
||||||
|
options.LoginPath = new PathString("/page");
|
||||||
});
|
});
|
||||||
|
|
||||||
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
||||||
|
|
||||||
var transaction2 = await SendAsync(server, "http://example.com/unauthorized", transaction1.CookieNameValue);
|
var transaction2 = await SendAsync(server, "http://example.com/unauthorized", transaction1.CookieNameValue);
|
||||||
|
|
||||||
transaction2.Response.StatusCode.ShouldBe(HttpStatusCode.Unauthorized);
|
transaction2.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -720,6 +714,53 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
location.Query.ShouldBe("?ReturnUrl=%2F");
|
location.Query.ShouldBe("?ReturnUrl=%2F");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ChallengeDoesNotSet401OnUnauthorized()
|
||||||
|
{
|
||||||
|
var server = TestServer.Create(app =>
|
||||||
|
{
|
||||||
|
app.UseCookieAuthentication();
|
||||||
|
app.Run(async context => {
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(() => context.Authentication.ChallengeAsync());
|
||||||
|
});
|
||||||
|
}, services => services.AddAuthentication());
|
||||||
|
|
||||||
|
var transaction = await server.SendAsync("http://example.com");
|
||||||
|
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [Fact]
|
||||||
|
public async Task UseCookieWithInstanceDoesntUseSharedOptions()
|
||||||
|
{
|
||||||
|
var server = TestServer.Create(app =>
|
||||||
|
{
|
||||||
|
app.UseCookieAuthentication(options => options.CookieName = "One");
|
||||||
|
app.UseCookieAuthentication(new CookieAuthenticationOptions());
|
||||||
|
app.Run(context => context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(new ClaimsIdentity())));
|
||||||
|
}, services => services.AddAuthentication());
|
||||||
|
|
||||||
|
var transaction = await server.SendAsync("http://example.com");
|
||||||
|
|
||||||
|
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||||
|
Assert.True(transaction.SetCookie[0].StartsWith(".AspNet.Cookies="));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UseCookieWithOutInstanceDoesUseSharedOptions()
|
||||||
|
{
|
||||||
|
var server = TestServer.Create(app =>
|
||||||
|
{
|
||||||
|
app.UseCookieAuthentication(options => options.CookieName = "One");
|
||||||
|
app.UseCookieAuthentication(options => options.AuthenticationScheme = "Two");
|
||||||
|
app.Run(context => context.Authentication.SignInAsync("Two", new ClaimsPrincipal(new ClaimsIdentity())));
|
||||||
|
}, services => services.AddAuthentication());
|
||||||
|
|
||||||
|
var transaction = await server.SendAsync("http://example.com");
|
||||||
|
|
||||||
|
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||||
|
Assert.True(transaction.SetCookie[0].StartsWith("One="));
|
||||||
|
}*/
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task MapWithSignInOnlyRedirectToReturnUrlOnLoginPath()
|
public async Task MapWithSignInOnlyRedirectToReturnUrlOnLoginPath()
|
||||||
{
|
{
|
||||||
|
|
@ -870,6 +911,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
var server = TestServer.Create(app =>
|
var server = TestServer.Create(app =>
|
||||||
{
|
{
|
||||||
app.UseCookieAuthentication(configureOptions);
|
app.UseCookieAuthentication(configureOptions);
|
||||||
|
// app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationScheme = "Cookie2" });
|
||||||
|
|
||||||
if (claimsTransform != null)
|
if (claimsTransform != null)
|
||||||
{
|
{
|
||||||
|
|
@ -961,17 +1003,13 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<Transaction> SendAsync(TestServer server, string uri, string cookieHeader = null, bool ajaxRequest = false)
|
private static async Task<Transaction> SendAsync(TestServer server, string uri, string cookieHeader = null)
|
||||||
{
|
{
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, uri);
|
var request = new HttpRequestMessage(HttpMethod.Get, uri);
|
||||||
if (!string.IsNullOrEmpty(cookieHeader))
|
if (!string.IsNullOrEmpty(cookieHeader))
|
||||||
{
|
{
|
||||||
request.Headers.Add("Cookie", cookieHeader);
|
request.Headers.Add("Cookie", cookieHeader);
|
||||||
}
|
}
|
||||||
if (ajaxRequest)
|
|
||||||
{
|
|
||||||
request.Headers.Add("X-Requested-With", "XMLHttpRequest");
|
|
||||||
}
|
|
||||||
var transaction = new Transaction
|
var transaction = new Transaction
|
||||||
{
|
{
|
||||||
Request = request,
|
Request = request,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
|
|
||||||
AuthenticationProperties ISecureDataFormat<AuthenticationProperties>.Unprotect(string protectedText)
|
AuthenticationProperties ISecureDataFormat<AuthenticationProperties>.Unprotect(string protectedText)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(protectedText))
|
if (string.IsNullOrEmpty(protectedText))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,15 +27,6 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
return await base.HandleUnauthorizedAsync(context);
|
return await base.HandleUnauthorizedAsync(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task HandleSignInAsync(SignInContext context)
|
|
||||||
{
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task HandleSignOutAsync(SignOutContext context)
|
|
||||||
{
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
protected override async Task<OpenIdConnectTokenEndpointResponse> RedeemAuthorizationCodeAsync(string authorizationCode, string redirectUri)
|
protected override async Task<OpenIdConnectTokenEndpointResponse> RedeemAuthorizationCodeAsync(string authorizationCode, string redirectUri)
|
||||||
{
|
{
|
||||||
var jsonResponse = new JObject();
|
var jsonResponse = new JObject();
|
||||||
|
|
@ -53,10 +44,5 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
claimsIdentity.AddClaim(new Claim("test claim", "test value"));
|
claimsIdentity.AddClaim(new Claim("test claim", "test value"));
|
||||||
return new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), ticket.Properties, ticket.AuthenticationScheme);
|
return new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), ticket.Properties, ticket.AuthenticationScheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
//public override bool ShouldHandleScheme(string authenticationScheme)
|
|
||||||
//{
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue