Redesign security contracts.
This commit is contained in:
parent
50f4fff510
commit
83855f8eac
23
Security.sln
23
Security.sln
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,5 @@ namespace Microsoft.AspNet.Security.Notifications
|
|||
|
||||
public bool Cancel { get; set; }
|
||||
public TMessage ProtocolMessage { get; set; }
|
||||
public int StatusCode { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -10,8 +10,6 @@ namespace Microsoft.AspNet.Security.Notifications
|
|||
|
||||
public bool Cancel { get; set; }
|
||||
|
||||
public int StatusCode { get; set; }
|
||||
|
||||
public bool IsRequestCompleted { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,5 @@ namespace Microsoft.AspNet.Security.Notifications
|
|||
|
||||
public bool Cancel { get; set; }
|
||||
public TMessage ProtocolMessage { get; set; }
|
||||
public int StatusCode { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,5 @@ namespace Microsoft.AspNet.Security.Notifications
|
|||
|
||||
public bool Cancel { get; set; }
|
||||
public string SecurityToken { get; set; }
|
||||
public int StatusCode { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,5 @@ namespace Microsoft.AspNet.Security.Notifications
|
|||
|
||||
public AuthenticationTicket AuthenticationTicket { get; set; }
|
||||
public bool Cancel { get; set; }
|
||||
public int StatusCode { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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": {},
|
||||
|
|
|
|||
Loading…
Reference in New Issue