Redesign security contracts.

This commit is contained in:
Chris Ross 2014-03-27 15:48:57 -07:00
parent 50f4fff510
commit 83855f8eac
44 changed files with 1381 additions and 236 deletions

View File

@ -9,6 +9,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4D2B6A51-2F9
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Security.k10", "src\Microsoft.AspNet.Security\Microsoft.AspNet.Security.k10.csproj", "{F2391907-4463-4650-AEA4-E1B5733105C4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Security.Cookies.net45", "src\Microsoft.AspNet.Security.Cookies\Microsoft.AspNet.Security.Cookies.net45.csproj", "{4579FEC1-3C4C-4F56-ADDB-850F947FDC2D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Security.Cookies.k10", "src\Microsoft.AspNet.Security.Cookies\Microsoft.AspNet.Security.Cookies.k10.csproj", "{17DD587C-CFB8-407C-A674-1C99F5CE9C7E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CookieSample.net45", "samples\CookieSample\CookieSample.net45.csproj", "{3C25520E-1AF0-4535-A111-6A1C04D31364}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -23,6 +31,18 @@ Global
{F2391907-4463-4650-AEA4-E1B5733105C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2391907-4463-4650-AEA4-E1B5733105C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2391907-4463-4650-AEA4-E1B5733105C4}.Release|Any CPU.Build.0 = Release|Any CPU
{4579FEC1-3C4C-4F56-ADDB-850F947FDC2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4579FEC1-3C4C-4F56-ADDB-850F947FDC2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4579FEC1-3C4C-4F56-ADDB-850F947FDC2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4579FEC1-3C4C-4F56-ADDB-850F947FDC2D}.Release|Any CPU.Build.0 = Release|Any CPU
{17DD587C-CFB8-407C-A674-1C99F5CE9C7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17DD587C-CFB8-407C-A674-1C99F5CE9C7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17DD587C-CFB8-407C-A674-1C99F5CE9C7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17DD587C-CFB8-407C-A674-1C99F5CE9C7E}.Release|Any CPU.Build.0 = Release|Any CPU
{3C25520E-1AF0-4535-A111-6A1C04D31364}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C25520E-1AF0-4535-A111-6A1C04D31364}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C25520E-1AF0-4535-A111-6A1C04D31364}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C25520E-1AF0-4535-A111-6A1C04D31364}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -30,5 +50,8 @@ Global
GlobalSection(NestedProjects) = preSolution
{70640501-CCBB-4FD7-8231-329B6A436DE4} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{F2391907-4463-4650-AEA4-E1B5733105C4} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{4579FEC1-3C4C-4F56-ADDB-850F947FDC2D} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{17DD587C-CFB8-407C-A674-1C99F5CE9C7E} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{3C25520E-1AF0-4535-A111-6A1C04D31364} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,42 @@
using System;
using System.Linq;
using System.Security.Claims;
using Microsoft.AspNet;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Abstractions.Security;
using Microsoft.AspNet.HttpFeature.Security;
using Microsoft.AspNet.Security;
using Microsoft.AspNet.Security.Cookies;
using Microsoft.AspNet.Security.Infrastructure;
namespace CookieSample
{
public class Startup
{
public void Configuration(IBuilder app)
{
Console.WriteLine("Attach");
Console.ReadKey();
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
});
app.Run(async context =>
{
if (context.User == null || !context.User.Identity.IsAuthenticated)
{
context.Authentication.SignIn(new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("name", "bob") }, CookieAuthenticationDefaults.AuthenticationType)));
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync("Hello First timer");
return;
}
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync("Hello old timer");
});
}
}
}

View File

@ -0,0 +1,39 @@
{
"version": "0.1-alpha-*",
"dependencies": {
"Microsoft.AspNet.Abstractions": "0.1-alpha-*",
"Microsoft.AspNet.Security": "",
"Microsoft.AspNet.Security.Cookies": "",
"Microsoft.AspNet.Hosting": "0.1-alpha-*",
"Microsoft.AspNet.PipelineCore": "0.1-alpha-*",
"Microsoft.AspNet.Abstractions": "0.1-alpha-*",
"Microsoft.AspNet.FeatureModel": "0.1-alpha-*",
"Microsoft.AspNet.HttpFeature": "0.1-alpha-*",
"Microsoft.AspNet.Server.WebListener": "0.1-alpha-*"
},
"commands": { "web": "Microsoft.AspNet.Hosting server.name=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:12345" },
"configurations": {
"net45": {
},
"k10": {
"dependencies": {
"System.Console": "4.0.0.0",
"System.Collections": "4.0.0.0",
"System.Diagnostics.Debug": "4.0.10.0",
"System.Diagnostics.Tools": "4.0.0.0",
"System.Globalization": "4.0.10.0",
"System.IO": "4.0.0.0",
"System.IO.FileSystem": "4.0.0.0",
"System.IO.FileSystem.Primitives": "4.0.0.0",
"System.Linq": "4.0.0.0",
"System.Reflection": "4.0.10.0",
"System.Resources.ResourceManager": "4.0.0.0",
"System.Runtime": "4.0.20.0",
"System.Runtime.Extensions": "4.0.10.0",
"System.Runtime.InteropServices": "4.0.10.0",
"System.Text.Encoding": "4.0.10.0",
"System.Threading.Tasks": "4.0.0.0"
}
}
}
}

View File

@ -0,0 +1,42 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Security.Cookies
{
/// <summary>
/// Default values related to cookie-based authentication middleware
/// </summary>
public static class CookieAuthenticationDefaults
{
/// <summary>
/// The default value used for CookieAuthenticationOptions.AuthenticationType
/// </summary>
public const string AuthenticationType = "Cookies";
/// <summary>
/// The prefix used to provide a default CookieAuthenticationOptions.CookieName
/// </summary>
public const string CookiePrefix = ".AspNet.";
/// <summary>
/// The default value used by UseApplicationSignInCookie for the
/// CookieAuthenticationOptions.LoginPath
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "By design")]
public static readonly PathString LoginPath = new PathString("/Account/Login");
/// <summary>
/// The default value used by UseApplicationSignInCookie for the
/// CookieAuthenticationOptions.LogoutPath
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Logout", Justification = "By design")]
public static readonly PathString LogoutPath = new PathString("/Account/Logout");
/// <summary>
/// The default value of the CookieAuthenticationOptions.ReturnUrlParameter
/// </summary>
public const string ReturnUrlParameter = "ReturnUrl";
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Logging;
using Microsoft.AspNet.Security.Cookies;
using Microsoft.AspNet.Security.DataHandler;
using Microsoft.AspNet.Security.DataProtection;
namespace Microsoft.AspNet
{
/// <summary>
/// Extension methods provided by the cookies authentication middleware
/// </summary>
public static class CookieAuthenticationExtensions
{
/// <summary>
/// Adds a cookie-based authentication middleware to your web application pipeline.
/// </summary>
/// <param name="app">The IAppBuilder passed to your configuration method</param>
/// <param name="options">An options class that controls the middleware behavior</param>
/// <returns>The original app parameter</returns>
public static IBuilder UseCookieAuthentication(this IBuilder app, CookieAuthenticationOptions options)
{
if (app == null)
{
throw new ArgumentNullException("app");
}
/*
// TODO: Extension methods for this?
var loggerFactory = (ILoggerFactory)app.ServiceProvider.GetService(typeof(ILoggerFactory));
ILogger logger = loggerFactory.Create(typeof(CookieAuthenticationMiddleware).FullName);
*/
ILogger logger = null;
if (options.TicketDataFormat == null)
{
/* TODO: Add DPP extensions
IDataProtector dataProtector = app.CreateDataProtector(
typeof(CookieAuthenticationMiddleware).FullName,
options.AuthenticationType, "v1");
*/
var dataProtectionProvider = (IDataProtectionProvider)app.ServiceProvider.GetService(typeof(IDataProtectionProvider));
IDataProtector dataProtector = dataProtectionProvider.CreateProtector(string.Join(";", typeof(CookieAuthenticationMiddleware).FullName, options.AuthenticationType, "v1"));
options.TicketDataFormat = new TicketDataFormat(dataProtector);
}
app.Use(next => new CookieAuthenticationMiddleware(next, logger, options).Invoke);
// TODO: ? app.UseStageMarker(PipelineStage.Authenticate);
return app;
}
}
}

View File

@ -0,0 +1,249 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Security.Infrastructure;
using Microsoft.AspNet.Logging;
using Microsoft.AspNet.PipelineCore.Collections;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.HttpFeature.Security;
namespace Microsoft.AspNet.Security.Cookies
{
internal class CookieAuthenticationHandler : AuthenticationHandler<CookieAuthenticationOptions>
{
private const string HeaderNameCacheControl = "Cache-Control";
private const string HeaderNamePragma = "Pragma";
private const string HeaderNameExpires = "Expires";
private const string HeaderValueNoCache = "no-cache";
private const string HeaderValueMinusOne = "-1";
private readonly ILogger _logger;
private bool _shouldRenew;
private DateTimeOffset _renewIssuedUtc;
private DateTimeOffset _renewExpiresUtc;
public CookieAuthenticationHandler(ILogger logger)
{
/*
if (logger == null)
{
throw new ArgumentNullException("logger");
}*/
_logger = logger;
}
protected override AuthenticationTicket AuthenticateCore()
{
return AuthenticateCoreAsync().Result;
}
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
IReadableStringCollection cookies = Request.Cookies;
string cookie = cookies[Options.CookieName];
if (string.IsNullOrWhiteSpace(cookie))
{
return null;
}
AuthenticationTicket ticket = Options.TicketDataFormat.Unprotect(cookie);
if (ticket == null)
{
// TODO: _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)
{
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);
}
protected override void ApplyResponseGrant()
{
ApplyResponseGrantAsync().Wait();
}
protected override async Task ApplyResponseGrantAsync()
{
var signin = SignInIdentityContext;
bool shouldSignin = signin != null;
var signout = SignOutContext;
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,
cookieOptions);
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)
{
var context = new CookieResponseSignOutContext(
Context,
Options,
cookieOptions);
Options.Provider.ResponseSignOut(context);
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 static bool IsHostRelative(string path)
{
if (string.IsNullOrEmpty(path))
{
return false;
}
if (path.Length == 1)
{
return path[0] == '/';
}
return path[0] == '/' && path[1] != '/' && path[1] != '\\';
}
protected override void ApplyResponseChallenge()
{
if (Response.StatusCode != 401 || !Options.LoginPath.HasValue || ChallengeContext == null)
{
return;
}
string currentUri =
Request.PathBase +
Request.Path +
Request.QueryString;
string loginUri =
Request.Scheme +
"://" +
Request.Host +
Request.PathBase +
Options.LoginPath +
new QueryString(Options.ReturnUrlParameter, currentUri);
var redirectContext = new CookieApplyRedirectContext(Context, Options, loginUri);
Options.Provider.ApplyRedirect(redirectContext);
}
}
}

View File

@ -0,0 +1,39 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Logging;
using Microsoft.AspNet.Security.DataHandler;
using Microsoft.AspNet.Security.DataProtection;
using Microsoft.AspNet.Security.Infrastructure;
namespace Microsoft.AspNet.Security.Cookies
{
internal class CookieAuthenticationMiddleware : AuthenticationMiddleware<CookieAuthenticationOptions>
{
private readonly ILogger _logger;
public CookieAuthenticationMiddleware(RequestDelegate next, ILogger logger, CookieAuthenticationOptions options)
: base(next, options)
{
if (Options.Provider == null)
{
Options.Provider = new CookieAuthenticationProvider();
}
if (String.IsNullOrEmpty(Options.CookieName))
{
Options.CookieName = CookieAuthenticationDefaults.CookiePrefix + Options.AuthenticationType;
}/*
if (logger == null)
{
throw new ArgumentNullException("logger");
}*/
_logger = logger;
}
protected override AuthenticationHandler<CookieAuthenticationOptions> CreateHandler()
{
return new CookieAuthenticationHandler(_logger);
}
}
}

View File

@ -0,0 +1,137 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Security.Infrastructure;
namespace Microsoft.AspNet.Security.Cookies
{
/// <summary>
/// Contains the options used by the CookiesAuthenticationMiddleware
/// </summary>
public class CookieAuthenticationOptions : AuthenticationOptions
{
private string _cookieName;
/// <summary>
/// Create an instance of the options initialized with the default values
/// </summary>
public CookieAuthenticationOptions()
: base(CookieAuthenticationDefaults.AuthenticationType)
{
ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
CookiePath = "/";
ExpireTimeSpan = TimeSpan.FromDays(14);
SlidingExpiration = true;
CookieHttpOnly = true;
CookieSecure = CookieSecureOption.SameAsRequest;
SystemClock = new SystemClock();
Provider = new CookieAuthenticationProvider();
}
/// <summary>
/// Determines the cookie name used to persist the identity. The default value is ".AspNet.Cookies".
/// This value should be changed if you change the name of the AuthenticationType, especially if your
/// system uses the cookie authentication middleware multiple times.
/// </summary>
public string CookieName
{
get { return _cookieName; }
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_cookieName = value;
}
}
/// <summary>
/// Determines the domain used to create the cookie. Is not provided by default.
/// </summary>
public string CookieDomain { get; set; }
/// <summary>
/// Determines the path used to create the cookie. The default value is "/" for highest browser compatability.
/// </summary>
public string CookiePath { get; set; }
/// <summary>
/// Determines if the browser should allow the cookie to be accessed by client-side javascript. The
/// default is true, which means the cookie will only be passed to http requests and is not made available
/// to script on the page.
/// </summary>
public bool CookieHttpOnly { get; set; }
/// <summary>
/// Determines if the cookie should only be transmitted on HTTPS request. The default is to limit the cookie
/// to HTTPS requests if the page which is doing the SignIn is also HTTPS. If you have an HTTPS sign in page
/// and portions of your site are HTTP you may need to change this value.
/// </summary>
public CookieSecureOption CookieSecure { get; set; }
/// <summary>
/// Controls how much time the cookie will remain valid from the point it is created. The expiration
/// information is in the protected cookie ticket. Because of that an expired cookie will be ignored
/// even if it is passed to the server after the browser should have purged it
/// </summary>
public TimeSpan ExpireTimeSpan { get; set; }
/// <summary>
/// The SlidingExpiration is set to true to instruct the middleware to re-issue a new cookie with a new
/// expiration time any time it processes a request which is more than halfway through the expiration window.
/// </summary>
public bool SlidingExpiration { get; set; }
/// <summary>
/// The LoginPath property informs the middleware that it should change an outgoing 401 Unauthorized status
/// code into a 302 redirection onto the given login path. The current url which generated the 401 is added
/// 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
/// 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>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "By design")]
public PathString LoginPath { get; set; }
/// <summary>
/// If the LogoutPath is provided the middleware then a request to that path will redirect based on the ReturnUrlParameter.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Logout", Justification = "By design")]
public PathString LogoutPath { get; set; }
/// <summary>
/// The ReturnUrlParameter determines the name of the query string parameter which is appended by the middleware
/// when a 401 Unauthorized status code is changed to a 302 redirect onto the login path. This is also the query
/// string parameter looked for when a request arrives on the login path or logout path, in order to return to the
/// original url after the action is performed.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "ReturnUrl is the name of a querystring parameter")]
public string ReturnUrlParameter { get; set; }
/// <summary>
/// The Provider may be assigned to an instance of an object created by the application at startup time. The middleware
/// calls methods on the provider which give the application control at certain points where processing is occuring.
/// If it is not provided a default instance is supplied which does nothing when the methods are called.
/// </summary>
public ICookieAuthenticationProvider Provider { get; set; }
/// <summary>
/// The TicketDataFormat is used to protect and unprotect the identity and other properties which are stored in the
/// cookie value. If it is not provided a default data handler is created using the data protection service contained
/// in the IAppBuilder.Properties. The default data protection service is based on machine key when running on ASP.NET,
/// and on DPAPI when running in a different process.
/// </summary>
public ISecureDataFormat<AuthenticationTicket> TicketDataFormat { get; set; }
/// <summary>
/// The SystemClock provides access to the system's current time coordinates. If it is not provided a default instance is
/// used which calls DateTimeOffset.UtcNow. This is typically not replaced except for unit testing.
/// </summary>
public ISystemClock SystemClock { get; set; }
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Security.Cookies
{
/// <summary>
/// Determines how the identity cookie's security property is set.
/// </summary>
public enum CookieSecureOption
{
/// <summary>
/// If the URI that provides the cookie is HTTPS, then the cookie will only be returned to the server on
/// subsequent HTTPS requests. Otherwise if the URI that provides the cookie is HTTP, then the cookie will
/// be returned to the server on all HTTP and HTTPS requests. This is the default value because it ensures
/// HTTPS for all authenticated requests on deployed servers, and also supports HTTP for localhost development
/// and for servers that do not have HTTPS support.
/// </summary>
SameAsRequest,
/// <summary>
/// CookieOptions.Secure is never marked true. Use this value when your login page is HTTPS, but other pages
/// on the site which are HTTP also require authentication information. This setting is not recommended because
/// the authentication information provided with an HTTP request may be observed and used by other computers
/// on your local network or wireless connection.
/// </summary>
Never,
/// <summary>
/// CookieOptions.Secure is always marked true. Use this value when your login page and all subsequent pages
/// requiring the authenticated identity are HTTPS. Local development will also need to be done with HTTPS urls.
/// </summary>
Always,
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Security.Provider;
namespace Microsoft.AspNet.Security.Cookies
{
/// <summary>
/// Context passed when a Challenge, SignIn, or SignOut causes a redirect in the cookie middleware
/// </summary>
public class CookieApplyRedirectContext : BaseContext<CookieAuthenticationOptions>
{
/// <summary>
/// Creates a new context object.
/// </summary>
/// <param name="context">The OWIN request context</param>
/// <param name="options">The cookie middleware options</param>
/// <param name="redirectUri">The initial redirect URI</param>
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "Represents header value")]
public CookieApplyRedirectContext(HttpContext context, CookieAuthenticationOptions options, string redirectUri)
: base(context, options)
{
RedirectUri = redirectUri;
}
/// <summary>
/// Gets or Sets the URI used for the redirect operation.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Represents header value")]
public string RedirectUri { get; set; }
}
}

View File

@ -0,0 +1,83 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Security.Cookies
{
/// <summary>
/// This default implementation of the ICookieAuthenticationProvider may be used if the
/// application only needs to override a few of the interface methods. This may be used as a base class
/// or may be instantiated directly.
/// </summary>
public class CookieAuthenticationProvider : ICookieAuthenticationProvider
{
/// <summary>
/// Create a new instance of the default provider.
/// </summary>
public CookieAuthenticationProvider()
{
OnValidateIdentity = context => Task.FromResult(0);
OnResponseSignIn = context => { };
OnResponseSignOut = context => { };
OnApplyRedirect = DefaultBehavior.ApplyRedirect;
}
/// <summary>
/// A delegate assigned to this property will be invoked when the related method is called
/// </summary>
public Func<CookieValidateIdentityContext, Task> OnValidateIdentity { get; set; }
/// <summary>
/// A delegate assigned to this property will be invoked when the related method is called
/// </summary>
public Action<CookieResponseSignInContext> OnResponseSignIn { get; set; }
/// <summary>
/// A delegate assigned to this property will be invoked when the related method is called
/// </summary>
public Action<CookieResponseSignOutContext> OnResponseSignOut { get; set; }
/// <summary>
/// A delegate assigned to this property will be invoked when the related method is called
/// </summary>
public Action<CookieApplyRedirectContext> OnApplyRedirect { get; set; }
/// <summary>
/// Implements the interface method by invoking the related delegate method
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public virtual Task ValidateIdentity(CookieValidateIdentityContext context)
{
return OnValidateIdentity.Invoke(context);
}
/// <summary>
/// Implements the interface method by invoking the related delegate method
/// </summary>
/// <param name="context"></param>
public virtual void ResponseSignIn(CookieResponseSignInContext context)
{
OnResponseSignIn.Invoke(context);
}
/// <summary>
/// Implements the interface method by invoking the related delegate method
/// </summary>
/// <param name="context"></param>
public virtual void ResponseSignOut(CookieResponseSignOutContext context)
{
OnResponseSignOut.Invoke(context);
}
/// <summary>
/// Called when a Challenge, SignIn, or SignOut causes a redirect in the cookie middleware
/// </summary>
/// <param name="context">Contains information about the event</param>
public void ApplyRedirect(CookieApplyRedirectContext context)
{
OnApplyRedirect.Invoke(context);
}
}
}

View File

@ -0,0 +1,62 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Security.Claims;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Abstractions.Security;
using Microsoft.AspNet.Security.Provider;
namespace Microsoft.AspNet.Security.Cookies
{
/// <summary>
/// Context object passed to the ICookieAuthenticationProvider method ResponseSignIn.
/// </summary>
public class CookieResponseSignInContext : BaseContext<CookieAuthenticationOptions>
{
/// <summary>
/// Creates a new instance of the context object.
/// </summary>
/// <param name="context">The OWIN request context</param>
/// <param name="options">The middleware options</param>
/// <param name="authenticationType">Initializes AuthenticationType property</param>
/// <param name="identity">Initializes Identity property</param>
/// <param name="properties">Initializes Extra property</param>
/// <param name="cookieOptions">Initializes options for the authentication cookie.</param>
public CookieResponseSignInContext(
HttpContext context,
CookieAuthenticationOptions options,
string authenticationType,
ClaimsIdentity identity,
AuthenticationProperties properties,
CookieOptions cookieOptions)
: base(context, options)
{
AuthenticationType = authenticationType;
Identity = identity;
Properties = properties;
CookieOptions = cookieOptions;
}
/// <summary>
/// The name of the AuthenticationType creating a cookie
/// </summary>
public string AuthenticationType { get; private set; }
/// <summary>
/// Contains the claims about to be converted into the outgoing cookie.
/// May be replaced or altered during the ResponseSignIn call.
/// </summary>
public ClaimsIdentity Identity { get; set; }
/// <summary>
/// Contains the extra data about to be contained in the outgoing cookie.
/// May be replaced or altered during the ResponseSignIn call.
/// </summary>
public AuthenticationProperties Properties { get; set; }
/// <summary>
/// The options for creating the outgoing cookie.
/// May be replace or altered during the ResponseSignIn call.
/// </summary>
public CookieOptions CookieOptions { get; set; }
}
}

View File

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Security.Provider;
namespace Microsoft.AspNet.Security.Cookies
{
/// <summary>
/// Context object passed to the ICookieAuthenticationProvider method ResponseSignOut
/// </summary>
public class CookieResponseSignOutContext : BaseContext<CookieAuthenticationOptions>
{
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="cookieOptions"></param>
public CookieResponseSignOutContext(HttpContext context, CookieAuthenticationOptions options, CookieOptions cookieOptions)
: base(context, options)
{
CookieOptions = cookieOptions;
}
/// <summary>
/// The options for creating the outgoing cookie.
/// May be replace or altered during the ResponseSignIn call.
/// </summary>
public CookieOptions CookieOptions
{
get;
set;
}
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Security.Claims;
using System.Security.Principal;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Abstractions.Security;
using Microsoft.AspNet.HttpFeature.Security;
using Microsoft.AspNet.Security.Infrastructure;
using Microsoft.AspNet.Security.Provider;
namespace Microsoft.AspNet.Security.Cookies
{
/// <summary>
/// Context object passed to the ICookieAuthenticationProvider method ValidateIdentity.
/// </summary>
public class CookieValidateIdentityContext : BaseContext<CookieAuthenticationOptions>
{
/// <summary>
/// Creates a new instance of the context object.
/// </summary>
/// <param name="context"></param>
/// <param name="ticket">Contains the initial values for identity and extra data</param>
/// <param name="options"></param>
public CookieValidateIdentityContext(HttpContext context, AuthenticationTicket ticket, CookieAuthenticationOptions options)
: base(context, options)
{
if (ticket == null)
{
throw new ArgumentNullException("ticket");
}
Identity = ticket.Identity;
Properties = ticket.Properties;
}
/// <summary>
/// Contains the claims identity arriving with the request. May be altered to change the
/// details of the authenticated user.
/// </summary>
public ClaimsIdentity Identity { get; private set; }
/// <summary>
/// Contains the extra meta-data arriving with the request ticket. May be altered.
/// </summary>
public AuthenticationProperties Properties { get; private set; }
/// <summary>
/// Called to replace the claims identity. The supplied identity will replace the value of the
/// Identity property, which determines the identity of the authenticated request.
/// </summary>
/// <param name="identity">The identity used as the replacement</param>
public void ReplaceIdentity(IIdentity identity)
{
Identity = new ClaimsIdentity(identity);
}
/// <summary>
/// Called to reject the incoming identity. This may be done if the application has determined the
/// account is no longer active, and the request should be treated as if it was anonymous.
/// </summary>
public void RejectIdentity()
{
Identity = null;
}
}
}

View File

@ -0,0 +1,55 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Abstractions;
using Newtonsoft.Json;
namespace Microsoft.AspNet.Security.Cookies
{
internal static class DefaultBehavior
{
internal static readonly Action<CookieApplyRedirectContext> ApplyRedirect = context =>
{
if (IsAjaxRequest(context.Request))
{
string jsonResponse = JsonConvert.SerializeObject(new
{
status = context.Response.StatusCode,
headers = new
{
location = context.RedirectUri
}
}, Formatting.None);
context.Response.StatusCode = 200;
context.Response.Headers.Append("X-Responded-JSON", jsonResponse);
}
else
{
context.Response.Redirect(context.RedirectUri);
}
};
private static bool IsAjaxRequest(HttpRequest request)
{
IReadableStringCollection query = request.Query;
if (query != null)
{
if (query["X-Requested-With"] == "XMLHttpRequest")
{
return true;
}
}
IHeaderDictionary headers = request.Headers;
if (headers != null)
{
if (headers["X-Requested-With"] == "XMLHttpRequest")
{
return true;
}
}
return false;
}
}
}

View File

@ -0,0 +1,39 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Threading.Tasks;
namespace Microsoft.AspNet.Security.Cookies
{
/// <summary>
/// Specifies callback methods which the <see cref="CookieAuthenticationMiddleware"></see> invokes to enable developer control over the authentication process. />
/// </summary>
public interface ICookieAuthenticationProvider
{
/// <summary>
/// Called each time a request identity has been validated by the middleware. By implementing this method the
/// application may alter or reject the identity which has arrived with the request.
/// </summary>
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
Task ValidateIdentity(CookieValidateIdentityContext context);
/// <summary>
/// Called when an endpoint has provided sign in information before it is converted into a cookie. By
/// implementing this method the claims and extra information that go into the ticket may be altered.
/// </summary>
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
void ResponseSignIn(CookieResponseSignInContext context);
/// <summary>
/// Called when a Challenge, SignIn, or SignOut causes a redirect in the cookie middleware
/// </summary>
/// <param name="context">Contains information about the event</param>
void ApplyRedirect(CookieApplyRedirectContext context);
/// <summary>
///
/// </summary>
/// <param name="context">Contains information about the login session as well as information about the authentication cookie.</param>
void ResponseSignOut(CookieResponseSignOutContext context);
}
}

View File

@ -0,0 +1,42 @@
{
"version": "0.1-alpha-*",
"dependencies": {
"Newtonsoft.Json": "5.0.8",
"Microsoft.AspNet.Security": "0.1-alpha-*",
"Microsoft.AspNet.DependencyInjection": "0.1-alpha-*",
"Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*",
"Microsoft.AspNet.PipelineCore": "0.1-alpha-*",
"Microsoft.AspNet.Abstractions": "0.1-alpha-*",
"Microsoft.AspNet.FeatureModel": "0.1-alpha-*",
"Microsoft.AspNet.HttpFeature": "0.1-alpha-*",
"Microsoft.AspNet.Logging": "0.1-alpha-*",
"Microsoft.AspNet.Security.DataProtection": "0.1-alpha-*"
},
"configurations": {
"net45": {},
"k10": {
"dependencies": {
"System.Collections": "4.0.0.0",
"System.Console": "4.0.0.0",
"System.ComponentModel": "4.0.0.0",
"System.Diagnostics.Debug": "4.0.10.0",
"System.Diagnostics.Tools": "4.0.0.0",
"System.Globalization": "4.0.10.0",
"System.IO": "4.0.0.0",
"System.IO.Compression": "4.0.0.0",
"System.IO.FileSystem": "4.0.0.0",
"System.IO.FileSystem.Primitives": "4.0.0.0",
"System.Linq": "4.0.0.0",
"System.Reflection": "4.0.10.0",
"System.Resources.ResourceManager": "4.0.0.0",
"System.Runtime": "4.0.20.0",
"System.Runtime.Extensions": "4.0.10.0",
"System.Runtime.InteropServices": "4.0.10.0",
"System.Security.Claims": "0.1-alpha-*",
"System.Security.Principal" : "4.0.0.0",
"System.Threading": "4.0.0.0",
"System.Threading.Tasks": "4.0.0.0"
}
}
}
}

View File

@ -1,5 +1,11 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Abstractions.Security;
using Microsoft.AspNet.HttpFeature.Security;
using Microsoft.AspNet.PipelineCore.Security;
namespace Microsoft.AspNet.Security
{
/// <summary>

View File

@ -1,6 +1,12 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.AspNet.Abstractions.Security;
using Microsoft.AspNet.HttpFeature.Security;
using Microsoft.AspNet.PipelineCore.Security;
using Microsoft.AspNet.Security.Infrastructure;
namespace Microsoft.AspNet.Security
{

View File

@ -1,5 +1,5 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
/* TODO:
#if NET45
using System;
using System.Collections.Generic;
using System.Net.Security;
@ -85,4 +85,4 @@ namespace Microsoft.AspNet.Security
}
}
}
*/
#endif

View File

@ -1,5 +1,5 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
/* TODO:
#if NET45
using System;
using System.Collections.Generic;
using System.ComponentModel;
@ -132,4 +132,4 @@ namespace Microsoft.AspNet.Security
}
}
}
*/
#endif

View File

@ -1,5 +1,5 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
/* TODO:
#if NET45
using System;
using System.Collections.Generic;
using System.Net.Security;
@ -78,4 +78,4 @@ namespace Microsoft.AspNet.Security
}
}
}
*/
#endif

View File

@ -1,5 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.AspNet.Abstractions.Security;
using Microsoft.AspNet.Security.DataHandler.Encoder;
using Microsoft.AspNet.Security.DataHandler.Serializer;
using Microsoft.AspNet.Security.DataProtection;

View File

@ -1,5 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.AspNet.Abstractions.Security;
namespace Microsoft.AspNet.Security.DataHandler.Serializer
{
public static class DataSerializers

View File

@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.AspNet.Abstractions.Security;
namespace Microsoft.AspNet.Security.DataHandler.Serializer
{

View File

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
@ -98,7 +99,7 @@ namespace Microsoft.AspNet.Security.DataHandler.Serializer
claims[index] = new Claim(type, value, valueType, issuer, originalIssuer);
}
var identity = new ClaimsIdentity(claims, authenticationType, nameClaimType, roleClaimType);
AuthenticationProperties properties = PropertiesSerializer.Read(reader);
var properties = PropertiesSerializer.Read(reader);
return new AuthenticationTicket(identity, properties);
}

View File

@ -1,5 +1,5 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
/* TODO:
#if NET45
using System;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
@ -25,4 +25,4 @@ namespace Microsoft.AspNet.Security
bool Validate(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors);
}
}
*/
#endif

View File

@ -1,25 +1,29 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Abstractions.Security;
using Microsoft.AspNet.HttpFeature.Security;
using Microsoft.AspNet.Logging;
using Microsoft.AspNet.Security.DataHandler.Encoder;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Security.Infrastructure
{
/// <summary>
/// Base class for the per-request work performed by most authentication middleware.
/// </summary>
public abstract class AuthenticationHandler
public abstract class AuthenticationHandler : IAuthenticationHandler
{
#if NET45
private static readonly RNGCryptoServiceProvider Random = new RNGCryptoServiceProvider();
private object _registration;
#endif
private Task<AuthenticationTicket> _authenticate;
private bool _authenticateInitialized;
private object _authenticateSyncLock;
@ -30,6 +34,10 @@ namespace Microsoft.AspNet.Security.Infrastructure
private AuthenticationOptions _baseOptions;
protected IChallengeContext ChallengeContext { get; set; }
protected SignInIdentityContext SignInIdentityContext { get; set; }
protected ISignOutContext SignOutContext { get; set; }
protected HttpContext Context { get; private set; }
protected HttpRequest Request
@ -43,21 +51,21 @@ namespace Microsoft.AspNet.Security.Infrastructure
}
protected PathString RequestPathBase { get; private set; }
protected SecurityHelper Helper { get; private set; }
internal AuthenticationOptions BaseOptions
{
get { return _baseOptions; }
}
public IAuthenticationHandler PriorHandler { get; set; }
protected async Task BaseInitializeAsync(AuthenticationOptions options, HttpContext context)
{
_baseOptions = options;
Context = context;
Helper = new SecurityHelper(context);
RequestPathBase = Request.PathBase;
_registration = Request.RegisterAuthenticationHandler(this);
RegisterAuthenticationHandler();
Response.OnSendingHeaders(OnSendingHeaderCallback, this);
@ -68,7 +76,7 @@ namespace Microsoft.AspNet.Security.Infrastructure
AuthenticationTicket ticket = await AuthenticateAsync();
if (ticket != null && ticket.Identity != null)
{
Helper.AddUserIdentity(ticket.Identity);
Context.AddUserIdentity(ticket.Identity);
}
}
}
@ -76,12 +84,12 @@ namespace Microsoft.AspNet.Security.Infrastructure
private static void OnSendingHeaderCallback(object state)
{
AuthenticationHandler handler = (AuthenticationHandler)state;
handler.ApplyResponseAsync().Wait();
handler.ApplyResponse();
}
protected virtual Task InitializeCoreAsync()
{
return Task.FromResult<object>(null);
return Task.FromResult(0);
}
/// <summary>
@ -92,12 +100,12 @@ namespace Microsoft.AspNet.Security.Infrastructure
{
await ApplyResponseAsync();
await TeardownCoreAsync();
Request.UnregisterAuthenticationHandler(_registration);
UnregisterAuthenticationHandler();
}
protected virtual Task TeardownCoreAsync()
{
return Task.FromResult<object>(null);
return Task.FromResult(0);
}
/// <summary>
@ -113,6 +121,63 @@ namespace Microsoft.AspNet.Security.Infrastructure
return Task.FromResult<bool>(false);
}
public virtual void GetDescriptions(DescriptionDelegate callback, object state)
{
callback(BaseOptions.Description.Dictionary, state);
if (PriorHandler != null)
{
PriorHandler.GetDescriptions(callback, state);
}
}
public virtual void Authenticate(IAuthenticateContext context)
{
if (context.AuthenticationTypes.Contains(BaseOptions.AuthenticationType, StringComparer.Ordinal))
{
AuthenticationTicket ticket = Authenticate();
if (ticket != null && ticket.Identity != null)
{
context.Authenticated(ticket.Identity, ticket.Properties.Dictionary, BaseOptions.Description.Dictionary);
}
}
if (PriorHandler != null)
{
PriorHandler.Authenticate(context);
}
}
public AuthenticationTicket Authenticate()
{
return LazyInitializer.EnsureInitialized(
ref _authenticate,
ref _authenticateInitialized,
ref _authenticateSyncLock,
() =>
{
return Task.FromResult(AuthenticateCore());
}).Result;
}
protected abstract AuthenticationTicket AuthenticateCore();
public virtual async Task AuthenticateAsync(IAuthenticateContext context)
{
if (context.AuthenticationTypes.Contains(BaseOptions.AuthenticationType, StringComparer.Ordinal))
{
AuthenticationTicket ticket = await AuthenticateAsync();
if (ticket != null && ticket.Identity != null)
{
context.Authenticated(ticket.Identity, ticket.Properties.Dictionary, BaseOptions.Description.Dictionary);
}
}
if (PriorHandler != null)
{
await PriorHandler.AuthenticateAsync(context);
}
}
/// <summary>
/// Causes the authentication logic in AuthenticateCore to be performed for the current request
/// at most once and returns the results. Calling Authenticate more than once will always return
@ -135,7 +200,29 @@ namespace Microsoft.AspNet.Security.Infrastructure
/// once per request. Do not call directly, call the wrapping Authenticate method instead.
/// </summary>
/// <returns>The ticket data provided by the authentication logic</returns>
protected abstract Task<AuthenticationTicket> AuthenticateCoreAsync();
protected virtual Task<AuthenticationTicket> AuthenticateCoreAsync()
{
return Task.FromResult(AuthenticateCore());
}
private void ApplyResponse()
{
LazyInitializer.EnsureInitialized(
ref _applyResponse,
ref _applyResponseInitialized,
ref _applyResponseSyncLock,
() =>
{
ApplyResponseCore();
return Task.FromResult(0);
}).Wait(); // Block if the async version is in progress.
}
protected virtual void ApplyResponseCore()
{
ApplyResponseGrant();
ApplyResponseChallenge();
}
/// <summary>
/// Causes the ApplyResponseCore to be invoked at most once per request. This method will be
@ -163,6 +250,8 @@ namespace Microsoft.AspNet.Security.Infrastructure
await ApplyResponseChallengeAsync();
}
protected abstract void ApplyResponseGrant();
/// <summary>
/// Override this method to dela with sign-in/sign-out concerns, if an authentication scheme in question
/// deals with grant/revoke as part of it's request flow. (like setting/deleting cookies)
@ -170,18 +259,67 @@ namespace Microsoft.AspNet.Security.Infrastructure
/// <returns></returns>
protected virtual Task ApplyResponseGrantAsync()
{
return Task.FromResult<object>(null);
ApplyResponseGrant();
return Task.FromResult(0);
}
public virtual void SignIn(ISignInContext context)
{
ClaimsIdentity identity;
if (SecurityHelper.LookupSignIn(context.User, BaseOptions.AuthenticationType, out identity))
{
SignInIdentityContext = new SignInIdentityContext(identity, new AuthenticationProperties(context.Properties));
SignOutContext = null;
context.Ack(BaseOptions.AuthenticationType, BaseOptions.Description.Dictionary);
}
if (PriorHandler != null)
{
PriorHandler.SignIn(context);
}
}
public virtual void SignOut(ISignOutContext context)
{
if (SecurityHelper.LookupSignOut(context.AuthenticationTypes, BaseOptions.AuthenticationType, BaseOptions.AuthenticationMode))
{
SignInIdentityContext = null;
SignOutContext = context;
context.Ack(BaseOptions.AuthenticationType, BaseOptions.Description.Dictionary);
}
if (PriorHandler != null)
{
PriorHandler.SignOut(context);
}
}
public virtual void Challenge(IChallengeContext context)
{
if (SecurityHelper.LookupChallenge(context.AuthenticationTypes, BaseOptions.AuthenticationType, BaseOptions.AuthenticationMode))
{
ChallengeContext = context;
context.Ack(BaseOptions.AuthenticationType, BaseOptions.Description.Dictionary);
}
if (PriorHandler != null)
{
PriorHandler.Challenge(context);
}
}
protected abstract void ApplyResponseChallenge();
/// <summary>
/// Override this method to dela with 401 challenge concerns, if an authentication scheme in question
/// Override this method to deal with 401 challenge concerns, if an authentication scheme in question
/// deals an authentication interaction as part of it's request flow. (like adding a response header, or
/// changing the 401 result to 302 of a login page or external sign-in location.)
/// </summary>
/// <returns></returns>
protected virtual Task ApplyResponseChallengeAsync()
{
return Task.FromResult<object>(null);
ApplyResponseChallenge();
return Task.FromResult(0);
}
protected void GenerateCorrelationId(AuthenticationProperties properties)
@ -194,7 +332,11 @@ namespace Microsoft.AspNet.Security.Infrastructure
string correlationKey = Constants.CorrelationPrefix + BaseOptions.AuthenticationType;
var nonceBytes = new byte[32];
#if NET45
Random.GetBytes(nonceBytes);
#else
Microsoft.AspNet.Security.DataProtection.CryptRand.FillBuffer(new ArraySegment<byte>(nonceBytes));
#endif
string correlationId = TextEncodings.Base64Url.Encode(nonceBytes);
var cookieOptions = new CookieOptions
@ -248,5 +390,18 @@ namespace Microsoft.AspNet.Security.Infrastructure
return true;
}
private void RegisterAuthenticationHandler()
{
var auth = Context.GetAuthentication();
PriorHandler = auth.Handler;
auth.Handler = this;
}
private void UnregisterAuthenticationHandler()
{
var auth = Context.GetAuthentication();
auth.Handler = PriorHandler;
}
}
}

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Security.Infrastructure
public TOptions Options { get; set; }
public override async Task Invoke(HttpContext context)
public async Task Invoke(HttpContext context)
{
AuthenticationHandler<TOptions> handler = CreateHandler();
await handler.Initialize(Options, context);

View File

@ -0,0 +1,20 @@
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.HttpFeature.Security;
using Microsoft.AspNet.PipelineCore.Security;
namespace Microsoft.AspNet.Security.Infrastructure
{
internal static class HttpContextExtensions
{
internal static IHttpAuthentication GetAuthentication(this HttpContext context)
{
var auth = context.GetFeature<IHttpAuthentication>();
if (auth == null)
{
auth = new DefaultHttpAuthentication();
context.SetFeature<IHttpAuthentication>(auth);
}
return auth;
}
}
}

View File

@ -0,0 +1,17 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Security.Infrastructure
{
/// <summary>
/// Abstracts the system clock to facilitate testing.
/// </summary>
public interface ISystemClock
{
/// <summary>
/// Retrieves the current system time in UTC.
/// </summary>
DateTimeOffset UtcNow { get; }
}
}

View File

@ -1,72 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Security.Infrastructure
{
// TODO: comment function documentations
using AuthenticateCallback = Action<IIdentity, IDictionary<string, string>, IDictionary<string, object>, object>;
using AuthenticateDelegate = Func<string[], Action<IIdentity, IDictionary<string, string>, IDictionary<string, object>, object>, object, Task>;
internal static class OwinRequestExtensions
{
public static object RegisterAuthenticationHandler(this HttpRequest request, AuthenticationHandler handler)
{
var chained = request.Get<AuthenticateDelegate>(Constants.SecurityAuthenticate);
var hook = new Hook(handler, chained);
request.Set<AuthenticateDelegate>(Constants.SecurityAuthenticate, hook.AuthenticateAsync);
return hook;
}
public static void UnregisterAuthenticationHandler(this HttpRequest request, object registration)
{
var hook = registration as Hook;
if (hook == null)
{
throw new InvalidOperationException(Resources.Exception_UnhookAuthenticationStateType);
}
request.Set(Constants.SecurityAuthenticate, hook.Chained);
}
private class Hook
{
private readonly AuthenticationHandler _handler;
public Hook(AuthenticationHandler handler, AuthenticateDelegate chained)
{
_handler = handler;
Chained = chained;
}
public AuthenticateDelegate Chained { get; private set; }
public async Task AuthenticateAsync(
string[] authenticationTypes,
AuthenticateCallback callback,
object state)
{
if (authenticationTypes == null)
{
callback(null, null, _handler.BaseOptions.Description.Properties, state);
}
else if (authenticationTypes.Contains(_handler.BaseOptions.AuthenticationType, StringComparer.Ordinal))
{
AuthenticationTicket ticket = await _handler.AuthenticateAsync();
if (ticket != null && ticket.Identity != null)
{
callback(ticket.Identity, ticket.Properties.Dictionary, _handler.BaseOptions.Description.Properties, state);
}
}
if (Chained != null)
{
await Chained(authenticationTypes, callback, state);
}
}
}
}
}

View File

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using Microsoft.AspNet.Abstractions;
@ -10,29 +12,13 @@ namespace Microsoft.AspNet.Security.Infrastructure
/// <summary>
/// Helper code used when implementing authentication middleware
/// </summary>
public struct SecurityHelper
public static class SecurityHelper
{
private readonly HttpContext _context;
/// <summary>
/// Helper code used when implementing authentication middleware
/// </summary>
/// <param name="context"></param>
public SecurityHelper(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
_context = context;
}
/// <summary>
/// Add an additional ClaimsIdentity to the ClaimsPrincipal in the "server.User" environment key
/// Add an additional ClaimsIdentity to the ClaimsPrincipal
/// </summary>
/// <param name="identity"></param>
public void AddUserIdentity(IIdentity identity)
public static void AddUserIdentity(this HttpContext context, IIdentity identity)
{
if (identity == null)
{
@ -40,88 +26,46 @@ namespace Microsoft.AspNet.Security.Infrastructure
}
var newClaimsPrincipal = new ClaimsPrincipal(identity);
IPrincipal existingPrincipal = _context.Request.User;
ClaimsPrincipal existingPrincipal = context.User;
if (existingPrincipal != null)
{
var existingClaimsPrincipal = existingPrincipal as ClaimsPrincipal;
if (existingClaimsPrincipal == null)
foreach (var existingClaimsIdentity in existingPrincipal.Identities)
{
IIdentity existingIdentity = existingPrincipal.Identity;
if (existingIdentity.IsAuthenticated)
if (existingClaimsIdentity.IsAuthenticated)
{
newClaimsPrincipal.AddIdentity(existingIdentity as ClaimsIdentity ?? new ClaimsIdentity(existingIdentity));
}
}
else
{
foreach (var existingClaimsIdentity in existingClaimsPrincipal.Identities)
{
if (existingClaimsIdentity.IsAuthenticated)
{
newClaimsPrincipal.AddIdentity(existingClaimsIdentity);
}
newClaimsPrincipal.AddIdentity(existingClaimsIdentity);
}
}
}
_context.Request.User = newClaimsPrincipal;
context.User = newClaimsPrincipal;
}
/// <summary>
/// Find response challenge details for a specific authentication middleware
/// </summary>
/// <param name="authenticationType">The authentication type to look for</param>
/// <param name="authenticationMode">The authentication mode the middleware is running under</param>
/// <returns>The information instructing the middleware how it should behave</returns>
public AuthenticationResponseChallenge LookupChallenge(string authenticationType, AuthenticationMode authenticationMode)
public static bool LookupChallenge(IList<string> authenticationTypes, string authenticationType, AuthenticationMode authenticationMode)
{
if (authenticationType == null)
bool challengeHasAuthenticationTypes = authenticationTypes != null && authenticationTypes.Any();
if (!challengeHasAuthenticationTypes)
{
throw new ArgumentNullException("authenticationType");
return authenticationMode == AuthenticationMode.Active;
}
AuthenticationResponseChallenge challenge = _context.Authentication.AuthenticationResponseChallenge;
bool challengeHasAuthenticationTypes = challenge != null && challenge.AuthenticationTypes != null && challenge.AuthenticationTypes.Length != 0;
if (challengeHasAuthenticationTypes == false)
{
return authenticationMode == AuthenticationMode.Active ? (challenge ?? new AuthenticationResponseChallenge(null, null)) : null;
}
foreach (var challengeType in challenge.AuthenticationTypes)
{
if (string.Equals(challengeType, authenticationType, StringComparison.Ordinal))
{
return challenge;
}
}
return null;
return authenticationTypes.Contains(authenticationType, StringComparer.Ordinal);
}
/// <summary>
/// Find response sign-in details for a specific authentication middleware
/// </summary>
/// <param name="authenticationType">The authentication type to look for</param>
/// <returns>The information instructing the middleware how it should behave</returns>
public AuthenticationResponseGrant LookupSignIn(string authenticationType)
public static bool LookupSignIn(ClaimsPrincipal user, string authenticationType, out ClaimsIdentity identity)
{
if (authenticationType == null)
{
throw new ArgumentNullException("authenticationType");
}
AuthenticationResponseGrant grant = _context.Authentication.AuthenticationResponseGrant;
if (grant == null)
{
return null;
}
foreach (var claimsIdentity in grant.Principal.Identities)
identity = null;
foreach (var claimsIdentity in user.Identities)
{
if (string.Equals(authenticationType, claimsIdentity.AuthenticationType, StringComparison.Ordinal))
{
return new AuthenticationResponseGrant(claimsIdentity, grant.Properties ?? new AuthenticationProperties());
identity = claimsIdentity;
return true;
}
}
return null;
return false;
}
/// <summary>
@ -129,60 +73,14 @@ namespace Microsoft.AspNet.Security.Infrastructure
/// </summary>
/// <param name="authenticationType">The authentication type to look for</param>
/// <param name="authenticationMode">The authentication mode the middleware is running under</param>
/// <returns>The information instructing the middleware how it should behave</returns>
public AuthenticationResponseRevoke LookupSignOut(string authenticationType, AuthenticationMode authenticationMode)
public static bool LookupSignOut(IList<string> authenticationTypes, string authenticationType, AuthenticationMode authenticationMode)
{
if (authenticationType == null)
bool singOutHasAuthenticationTypes = authenticationTypes != null && authenticationTypes.Any();
if (!singOutHasAuthenticationTypes)
{
throw new ArgumentNullException("authenticationType");
return authenticationMode == AuthenticationMode.Active;
}
AuthenticationResponseRevoke revoke = _context.Authentication.AuthenticationResponseRevoke;
if (revoke == null)
{
return null;
}
if (revoke.AuthenticationTypes == null || revoke.AuthenticationTypes.Length == 0)
{
return authenticationMode == AuthenticationMode.Active ? revoke : null;
}
for (int index = 0; index != revoke.AuthenticationTypes.Length; ++index)
{
if (String.Equals(authenticationType, revoke.AuthenticationTypes[index], StringComparison.Ordinal))
{
return revoke;
}
}
return null;
return authenticationTypes.Contains(authenticationType, StringComparer.Ordinal);
}
#region Value-type equality
public bool Equals(SecurityHelper other)
{
return Equals(_context, other._context);
}
public override bool Equals(object obj)
{
return obj is SecurityHelper && Equals((SecurityHelper)obj);
}
public override int GetHashCode()
{
return (_context != null ? _context.GetHashCode() : 0);
}
public static bool operator ==(SecurityHelper left, SecurityHelper right)
{
return left.Equals(right);
}
public static bool operator !=(SecurityHelper left, SecurityHelper right)
{
return !left.Equals(right);
}
#endregion
}
}

View File

@ -0,0 +1,17 @@
using System.Security.Claims;
using Microsoft.AspNet.Abstractions.Security;
namespace Microsoft.AspNet.Security.Infrastructure
{
public class SignInIdentityContext
{
public SignInIdentityContext(ClaimsIdentity identity, AuthenticationProperties properties)
{
Identity = identity;
Properties = properties;
}
public ClaimsIdentity Identity { get; private set; }
public AuthenticationProperties Properties { get; private set; }
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Security.Infrastructure
{
/// <summary>
/// Provides access to the normal system clock.
/// </summary>
public class SystemClock : ISystemClock
{
/// <summary>
/// Retrieves the current system time in UTC.
/// </summary>
public DateTimeOffset UtcNow
{
get
{
// the clock measures whole seconds only, to have integral expires_in results, and
// because milliseconds do not round-trip serialization formats
DateTimeOffset utcNow = DateTimeOffset.UtcNow;
return utcNow.AddMilliseconds(-utcNow.Millisecond);
}
}
}
}

View File

@ -13,6 +13,5 @@ namespace Microsoft.AspNet.Security.Notifications
public bool Cancel { get; set; }
public Exception Exception { get; set; }
public TMessage ProtocolMessage { get; set; }
public int StatusCode { get; set; }
}
}

View File

@ -10,6 +10,5 @@ namespace Microsoft.AspNet.Security.Notifications
public bool Cancel { get; set; }
public TMessage ProtocolMessage { get; set; }
public int StatusCode { get; set; }
}
}

View File

@ -10,8 +10,6 @@ namespace Microsoft.AspNet.Security.Notifications
public bool Cancel { get; set; }
public int StatusCode { get; set; }
public bool IsRequestCompleted { get; set; }
}
}

View File

@ -10,6 +10,5 @@ namespace Microsoft.AspNet.Security.Notifications
public bool Cancel { get; set; }
public TMessage ProtocolMessage { get; set; }
public int StatusCode { get; set; }
}
}

View File

@ -10,6 +10,5 @@ namespace Microsoft.AspNet.Security.Notifications
public bool Cancel { get; set; }
public string SecurityToken { get; set; }
public int StatusCode { get; set; }
}
}

View File

@ -10,6 +10,5 @@ namespace Microsoft.AspNet.Security.Notifications
public AuthenticationTicket AuthenticationTicket { get; set; }
public bool Cancel { get; set; }
public int StatusCode { get; set; }
}
}

View File

@ -3,6 +3,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Security.Claims;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Abstractions.Security;
namespace Microsoft.AspNet.Security.Provider
{

View File

@ -39,7 +39,7 @@ namespace Microsoft.AspNet.Security {
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.Security.Resources", typeof(Resources).Assembly);
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.Security.Resources", System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(Resources)).Assembly);
resourceMan = temp;
}
return resourceMan;

View File

@ -8,7 +8,7 @@
"Microsoft.AspNet.FeatureModel": "0.1-alpha-*",
"Microsoft.AspNet.HttpFeature": "0.1-alpha-*",
"Microsoft.AspNet.Logging": "0.1-alpha-*",
"Microsoft.AspNet.Security.DataProtection": "0.1-alpha-*"
"Microsoft.AspNet.Security.DataProtection": "0.1-alpha-*"
},
"configurations": {
"net45": {},