diff --git a/HttpAbstractions.sln b/HttpAbstractions.sln index 9525343a..6434e357 100644 --- a/HttpAbstractions.sln +++ b/HttpAbstractions.sln @@ -57,6 +57,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{ED7BCAC5 build\Key.snk = build\Key.snk EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.Abstractions", "src\Microsoft.AspNetCore.Authentication.Abstractions\Microsoft.AspNetCore.Authentication.Abstractions.csproj", "{3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.Core", "src\Microsoft.AspNetCore.Authentication.Core\Microsoft.AspNetCore.Authentication.Core.csproj", "{73CA3145-91BD-4DA5-BC74-40008DE7EA98}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.Core.Test", "test\Microsoft.AspNetCore.Authentication.Core.Test\Microsoft.AspNetCore.Authentication.Core.Test.csproj", "{A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -223,6 +229,42 @@ Global {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Release|Mixed Platforms.Build.0 = Release|Any CPU {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Release|x86.ActiveCfg = Release|Any CPU {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Release|x86.Build.0 = Release|Any CPU + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}.Debug|x86.ActiveCfg = Debug|Any CPU + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}.Debug|x86.Build.0 = Debug|Any CPU + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}.Release|Any CPU.Build.0 = Release|Any CPU + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}.Release|x86.ActiveCfg = Release|Any CPU + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852}.Release|x86.Build.0 = Release|Any CPU + {73CA3145-91BD-4DA5-BC74-40008DE7EA98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73CA3145-91BD-4DA5-BC74-40008DE7EA98}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73CA3145-91BD-4DA5-BC74-40008DE7EA98}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {73CA3145-91BD-4DA5-BC74-40008DE7EA98}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {73CA3145-91BD-4DA5-BC74-40008DE7EA98}.Debug|x86.ActiveCfg = Debug|Any CPU + {73CA3145-91BD-4DA5-BC74-40008DE7EA98}.Debug|x86.Build.0 = Debug|Any CPU + {73CA3145-91BD-4DA5-BC74-40008DE7EA98}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73CA3145-91BD-4DA5-BC74-40008DE7EA98}.Release|Any CPU.Build.0 = Release|Any CPU + {73CA3145-91BD-4DA5-BC74-40008DE7EA98}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {73CA3145-91BD-4DA5-BC74-40008DE7EA98}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {73CA3145-91BD-4DA5-BC74-40008DE7EA98}.Release|x86.ActiveCfg = Release|Any CPU + {73CA3145-91BD-4DA5-BC74-40008DE7EA98}.Release|x86.Build.0 = Release|Any CPU + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Debug|x86.ActiveCfg = Debug|Any CPU + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Debug|x86.Build.0 = Debug|Any CPU + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Release|Any CPU.Build.0 = Release|Any CPU + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Release|x86.ActiveCfg = Release|Any CPU + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -244,5 +286,8 @@ Global {E6BB7AD1-BD10-4A23-B780-F4A86ADF00D1} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21} {1D0764B4-1DEB-4232-A714-D4B7E846918A} = {982F09D8-621E-4872-BA7B-BBDEA47D1EFD} {ED7BCAC5-2796-44BD-9954-7C248263BC8B} = {C6C48D5F-B289-4150-A6FC-77A5C7064BCE} + {3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852} = {A5A15F1C-885A-452A-A731-B0173DDBD913} + {73CA3145-91BD-4DA5-BC74-40008DE7EA98} = {A5A15F1C-885A-452A-A731-B0173DDBD913} + {A85950C5-2794-47E2-8EAA-05A1DC7C6DA7} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticateContext.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticateContext.cs new file mode 100644 index 00000000..814d7024 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticateContext.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Base class used by methods. + /// + public class AuthenticateContext : BaseAuthenticationContext + { + /// + /// Constructor. + /// + /// The context. + /// The name of the authentication scheme. + public AuthenticateContext(HttpContext context, string authenticationScheme) : base(context, authenticationScheme, properties: null) + { } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticateResult.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticateResult.cs new file mode 100644 index 00000000..bb9bbb97 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticateResult.cs @@ -0,0 +1,105 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Security.Claims; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Contains the result of an Authenticate call + /// + public class AuthenticateResult + { + private AuthenticateResult() { } + + /// + /// If a ticket was produced, authenticate was successful. + /// + public bool Succeeded => Ticket != null; + + /// + /// The authentication ticket. + /// + public AuthenticationTicket Ticket { get; private set; } + + /// + /// Gets the claims-principal with authenticated user identities. + /// + public ClaimsPrincipal Principal => Ticket?.Principal; + + /// + /// Additional state values for the authentication session. + /// + public AuthenticationProperties Properties => Ticket?.Properties; + + /// + /// Holds failure information from the authentication. + /// + public Exception Failure { get; private set; } + + /// + /// Indicates that stage of authentication was directly handled by user intervention and no + /// further processing should be attempted. + /// + public bool Handled { get; private set; } + + /// + /// Indicates that there was no information returned for this authentication scheme. + /// + public bool Nothing { get; private set; } + + /// + /// Indicates that authentication was successful. + /// + /// The ticket representing the authentication result. + /// The result. + public static AuthenticateResult Success(AuthenticationTicket ticket) + { + if (ticket == null) + { + throw new ArgumentNullException(nameof(ticket)); + } + return new AuthenticateResult() { Ticket = ticket }; + } + + /// + /// Indicates that stage of authentication was directly handled by user intervention and no + /// further processing should be attempted. + /// + /// The result. + public static AuthenticateResult Handle() + { + return new AuthenticateResult() { Handled = true }; + } + + /// + /// Indicates that there was no information returned for this authentication scheme. + /// + /// The result. + public static AuthenticateResult None() + { + return new AuthenticateResult() { Nothing = true }; + } + + /// + /// Indicates that there was a failure during authentication. + /// + /// The failure exception. + /// The result. + public static AuthenticateResult Fail(Exception failure) + { + return new AuthenticateResult() { Failure = failure }; + } + + /// + /// Indicates that there was a failure during authentication. + /// + /// The failure message. + /// The result. + public static AuthenticateResult Fail(string failureMessage) + { + return new AuthenticateResult() { Failure = new Exception(failureMessage) }; + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationHttpContextExtensions.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationHttpContextExtensions.cs new file mode 100644 index 00000000..a1fd3756 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationHttpContextExtensions.cs @@ -0,0 +1,158 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Extension methods to expose Authentication on HttpContext. + /// + public static class AuthenticationHttpContextExtensions + { + /// + /// Extension method for authenticate using the scheme. + /// + /// The context. + /// The . + public static Task AuthenticateAsync(this HttpContext context) => + context.AuthenticateAsync(scheme: null); + + /// + /// Extension method for authenticate. + /// + /// The context. + /// The name of the authentication scheme. + /// The . + public static Task AuthenticateAsync(this HttpContext context, string scheme) => + context.RequestServices.GetRequiredService().AuthenticateAsync(context, scheme); + + /// + /// Extension method for Challenge. + /// + /// The context. + /// The name of the authentication scheme. + /// The result. + public static Task ChallengeAsync(this HttpContext context, string scheme) => + context.ChallengeAsync(scheme, properties: null); + + /// + /// Extension method for authenticate using the scheme. + /// + /// The context. + /// The task. + public static Task ChallengeAsync(this HttpContext context) => + context.ChallengeAsync(scheme: null, properties: null); + + /// + /// Extension method for Challenge. + /// + /// The context. + /// The name of the authentication scheme. + /// The properties. + /// The task. + public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties) => + context.ChallengeAsync(scheme, properties: properties, behavior: ChallengeBehavior.Automatic); + + /// + /// Extension method for Challenge. + /// + /// The context. + /// The name of the authentication scheme. + /// The properties. + /// The behavior. + /// The task. + public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties, ChallengeBehavior behavior) => + context.RequestServices.GetRequiredService().ChallengeAsync(context, scheme, properties, behavior); + + /// + /// Extension method for Forbid. + /// + /// The context. + /// The name of the authentication scheme. + /// The task. + public static Task ForbidAsync(this HttpContext context, string scheme) => + context.ForbidAsync(scheme, properties: null); + + /// + /// Extension method for Forbid. + /// + /// The context. + /// The name of the authentication scheme. + /// The properties. + /// The task. + public static Task ForbidAsync(this HttpContext context, string scheme, AuthenticationProperties properties) => + context.RequestServices.GetRequiredService().ChallengeAsync(context, scheme, properties, ChallengeBehavior.Forbidden); + + /// + /// Extension method for SignIn. + /// + /// The context. + /// The name of the authentication scheme. + /// The user. + /// The task. + public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal) => + context.SignInAsync(scheme, principal, properties: null); + + /// + /// Extension method for SignIn using the . + /// + /// The context. + /// The user. + /// The task. + public static Task SignInAsync(this HttpContext context, ClaimsPrincipal principal) => + context.SignInAsync(scheme: null, principal: principal, properties: null); + + /// + /// Extension method for SignIn using the . + /// + /// The context. + /// The user. + /// The properties. + /// The task. + public static Task SignInAsync(this HttpContext context, ClaimsPrincipal principal, AuthenticationProperties properties) => + context.SignInAsync(scheme: null, principal: principal, properties: properties); + + /// + /// Extension method for SignIn. + /// + /// The context. + /// The name of the authentication scheme. + /// The user. + /// The properties. + /// The task. + public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) => + context.RequestServices.GetRequiredService().SignInAsync(context, scheme, principal, properties); + + /// + /// Extension method for SignOut. + /// + /// The context. + /// The name of the authentication scheme. + /// The task. + public static Task SignOutAsync(this HttpContext context, string scheme) => context.SignOutAsync(scheme, properties: null); + + /// + /// Extension method for SignOut. + /// + /// The context. + /// The name of the authentication scheme. + /// The properties. + /// + public static Task SignOutAsync(this HttpContext context, string scheme, AuthenticationProperties properties) => + context.RequestServices.GetRequiredService().SignOutAsync(context, scheme, properties); + + /// + /// Extension method for getting the value of an authentication token. + /// + /// The context. + /// The name of the authentication scheme. + /// The name of the token. + /// The value of the token. + public static Task GetTokenAsync(this HttpContext context, string scheme, string tokenName) => + context.RequestServices.GetRequiredService().GetTokenAsync(context, scheme, tokenName); + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationOptions.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationOptions.cs new file mode 100644 index 00000000..5f688d0f --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationOptions.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.Authentication +{ + public class AuthenticationOptions + { + private readonly IList _schemes = new List(); + + /// + /// Returns the schemes in the order they were added (important for request handling priority) + /// + public IEnumerable Schemes => _schemes; + + /// + /// Maps schemes by name. + /// + public IDictionary SchemeMap { get; } = new Dictionary(StringComparer.Ordinal); + + /// + /// Adds an . + /// + /// The name of the scheme being added. + /// Configures the scheme. + public void AddScheme(string name, Action configureBuilder) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + if (configureBuilder == null) + { + throw new ArgumentNullException(nameof(configureBuilder)); + } + if (SchemeMap.ContainsKey(name)) + { + throw new InvalidOperationException("Scheme already exists: " + name); + } + + var builder = new AuthenticationSchemeBuilder(name); + configureBuilder(builder); + _schemes.Add(builder); + SchemeMap[name] = builder; + } + + /// + /// Used by as the default scheme by . + /// + public string DefaultAuthenticationScheme { get; set; } + + /// + /// Used by as the default scheme by . + /// + public string DefaultSignInScheme { get; set; } + + /// + /// Used by as the default scheme by . + /// + public string DefaultChallengeScheme { get; set; } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationProperties.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationProperties.cs new file mode 100644 index 00000000..609b6fad --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationProperties.cs @@ -0,0 +1,197 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Dictionary used to store state values about the authentication session. + /// + public class AuthenticationProperties + { + internal const string IssuedUtcKey = ".issued"; + internal const string ExpiresUtcKey = ".expires"; + internal const string IsPersistentKey = ".persistent"; + internal const string RedirectUriKey = ".redirect"; + internal const string RefreshKey = ".refresh"; + internal const string UtcDateTimeFormat = "r"; + + /// + /// Initializes a new instance of the class + /// + public AuthenticationProperties() + : this(items: null) + { + } + + /// + /// Initializes a new instance of the class + /// + /// + public AuthenticationProperties(IDictionary items) + { + Items = items ?? new Dictionary(StringComparer.Ordinal); + } + + /// + /// State values about the authentication session. + /// + public IDictionary Items { get; } + + /// + /// Gets or sets whether the authentication session is persisted across multiple requests. + /// + public bool IsPersistent + { + get { return Items.ContainsKey(IsPersistentKey); } + set + { + if (Items.ContainsKey(IsPersistentKey)) + { + if (!value) + { + Items.Remove(IsPersistentKey); + } + } + else + { + if (value) + { + Items.Add(IsPersistentKey, string.Empty); + } + } + } + } + + /// + /// Gets or sets the full path or absolute URI to be used as an http redirect response value. + /// + public string RedirectUri + { + get + { + string value; + return Items.TryGetValue(RedirectUriKey, out value) ? value : null; + } + set + { + if (value != null) + { + Items[RedirectUriKey] = value; + } + else + { + if (Items.ContainsKey(RedirectUriKey)) + { + Items.Remove(RedirectUriKey); + } + } + } + } + + /// + /// Gets or sets the time at which the authentication ticket was issued. + /// + public DateTimeOffset? IssuedUtc + { + get + { + string value; + if (Items.TryGetValue(IssuedUtcKey, out value)) + { + DateTimeOffset dateTimeOffset; + if (DateTimeOffset.TryParseExact(value, UtcDateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTimeOffset)) + { + return dateTimeOffset; + } + } + return null; + } + set + { + if (value.HasValue) + { + Items[IssuedUtcKey] = value.Value.ToString(UtcDateTimeFormat, CultureInfo.InvariantCulture); + } + else + { + if (Items.ContainsKey(IssuedUtcKey)) + { + Items.Remove(IssuedUtcKey); + } + } + } + } + + /// + /// Gets or sets the time at which the authentication ticket expires. + /// + public DateTimeOffset? ExpiresUtc + { + get + { + string value; + if (Items.TryGetValue(ExpiresUtcKey, out value)) + { + DateTimeOffset dateTimeOffset; + if (DateTimeOffset.TryParseExact(value, UtcDateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTimeOffset)) + { + return dateTimeOffset; + } + } + return null; + } + set + { + if (value.HasValue) + { + Items[ExpiresUtcKey] = value.Value.ToString(UtcDateTimeFormat, CultureInfo.InvariantCulture); + } + else + { + if (Items.ContainsKey(ExpiresUtcKey)) + { + Items.Remove(ExpiresUtcKey); + } + } + } + } + + /// + /// Gets or sets if refreshing the authentication session should be allowed. + /// + public bool? AllowRefresh + { + get + { + string value; + if (Items.TryGetValue(RefreshKey, out value)) + { + bool refresh; + if (bool.TryParse(value, out refresh)) + { + return refresh; + } + } + return null; + } + set + { + if (value.HasValue) + { + Items[RefreshKey] = value.Value.ToString(); + } + else + { + if (Items.ContainsKey(RefreshKey)) + { + Items.Remove(RefreshKey); + } + } + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationScheme.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationScheme.cs new file mode 100644 index 00000000..4d30dd11 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationScheme.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Reflection; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// AuthenticationSchemes are basically a name for a specific + /// handlerType. + /// + public class AuthenticationScheme + { + /// + /// Constructor. + /// + /// The name for the authentication scheme. + /// The type that handles this scheme. + public AuthenticationScheme(string name, Type handlerType) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + if (handlerType == null) + { + throw new ArgumentNullException(nameof(handlerType)); + } + if (!typeof(IAuthenticationHandler).IsAssignableFrom(handlerType)) + { + throw new ArgumentException("handlerType must implement IAuthenticationSchemeHandler."); + } + + Name = name; + HandlerType = handlerType; + } + + // TODO: add display name? + /// + /// The name of the authentication scheme. + /// + public string Name { get; } + + /// + /// The type that handles this scheme. + /// + public Type HandlerType { get; } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationSchemeBuilder.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationSchemeBuilder.cs new file mode 100644 index 00000000..e1ea0cbc --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationSchemeBuilder.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Used to build s. + /// + public class AuthenticationSchemeBuilder + { + /// + /// Constructor. + /// + /// The name of the scheme being built. + public AuthenticationSchemeBuilder(string name) + { + Name = name; + } + + /// + /// The name of the scheme being built. + /// + public string Name { get; } + + /// + /// The type responsible for this scheme. + /// + public Type HandlerType { get; set; } + + /// + /// Builds the instance. + /// + /// + public AuthenticationScheme Build() => new AuthenticationScheme(Name, HandlerType); + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationTicket.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationTicket.cs new file mode 100644 index 00000000..c31f15ec --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationTicket.cs @@ -0,0 +1,56 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Security.Claims; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Contains user identity information as well as additional authentication state. + /// + public class AuthenticationTicket + { + /// + /// Initializes a new instance of the class + /// + /// the that represents the authenticated user. + /// additional properties that can be consumed by the user or runtime. + /// the authentication middleware that was responsible for this ticket. + public AuthenticationTicket(ClaimsPrincipal principal, AuthenticationProperties properties, string authenticationScheme) + { + if (principal == null) + { + throw new ArgumentNullException(nameof(principal)); + } + + AuthenticationScheme = authenticationScheme; + Principal = principal; + Properties = properties ?? new AuthenticationProperties(); + } + + /// + /// Initializes a new instance of the class + /// + /// the that represents the authenticated user. + /// the authentication middleware that was responsible for this ticket. + public AuthenticationTicket(ClaimsPrincipal principal, string authenticationScheme) + : this(principal, properties: null, authenticationScheme: authenticationScheme) + { } + + /// + /// Gets the authentication type. + /// + public string AuthenticationScheme { get; private set; } + + /// + /// Gets the claims-principal with authenticated user identities. + /// + public ClaimsPrincipal Principal { get; private set; } + + /// + /// Additional state values for the authentication session. + /// + public AuthenticationProperties Properties { get; private set; } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationToken.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationToken.cs new file mode 100644 index 00000000..555da9e0 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationToken.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Name/Value representing an token. + /// + public class AuthenticationToken + { + /// + /// Name. + /// + public string Name { get; set; } + + /// + /// Value. + /// + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/BaseAuthenticationContext.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/BaseAuthenticationContext.cs new file mode 100644 index 00000000..cfe5809c --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/BaseAuthenticationContext.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Base context for authentication. + /// + public abstract class BaseAuthenticationContext : BaseContext + { + /// + /// Constructor. + /// + /// The context. + /// The name of the scheme. + /// The properties. + protected BaseAuthenticationContext(HttpContext context, string authenticationScheme, AuthenticationProperties properties) : base(context) + { + if (string.IsNullOrEmpty(authenticationScheme)) + { + throw new ArgumentException(nameof(authenticationScheme)); + } + + AuthenticationScheme = authenticationScheme; + Properties = properties ?? new AuthenticationProperties(); + } + + /// + /// The name of the scheme. + /// + public string AuthenticationScheme { get; } + + /// + /// Contains the extra meta-data arriving with the authentication. May be altered. + /// + public AuthenticationProperties Properties { get; protected set; } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/BaseContext.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/BaseContext.cs new file mode 100644 index 00000000..3d65f0dd --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/BaseContext.cs @@ -0,0 +1,49 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Base class used by other context classes. + /// + public abstract class BaseContext + { + /// + /// Constructor. + /// + /// The request context. + protected BaseContext(HttpContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + HttpContext = context; + } + + /// + /// The context. + /// + public HttpContext HttpContext { get; } + + /// + /// The request. + /// + public HttpRequest Request + { + get { return HttpContext.Request; } + } + + /// + /// The response. + /// + public HttpResponse Response + { + get { return HttpContext.Response; } + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/ChallengeBehavior.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/ChallengeBehavior.cs new file mode 100644 index 00000000..1506021d --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/ChallengeBehavior.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Controls how challenge will behave (i.e. 401 vs 403). + /// + public enum ChallengeBehavior + { + Automatic, + Unauthorized, + Forbidden + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/ChallengeContext.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/ChallengeContext.cs new file mode 100644 index 00000000..ee2392eb --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/ChallengeContext.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Context used for challenges. + /// + public class ChallengeContext : BaseAuthenticationContext + { + /// + /// Constructor. + /// + /// The context. + /// The name of the scheme. + public ChallengeContext(HttpContext httpContext, string authenticationScheme) + : this(httpContext, authenticationScheme, properties: null, behavior: ChallengeBehavior.Automatic) + { } + + /// + /// Constructor + /// + /// The context. + /// The name of the scheme. + /// The properties. + /// The challenge behavior. + public ChallengeContext(HttpContext httpContext, string authenticationScheme, AuthenticationProperties properties, ChallengeBehavior behavior) + : base(httpContext, authenticationScheme, properties) + { + if (string.IsNullOrEmpty(authenticationScheme)) + { + throw new ArgumentException(nameof(authenticationScheme)); + } + Behavior = behavior; + } + + /// + /// The challenge behavior. + /// + public ChallengeBehavior Behavior { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationFeature.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationFeature.cs new file mode 100644 index 00000000..43e5a13b --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationFeature.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Used to capture path info so redirects can be computed properly within an app.Map(). + /// + public interface IAuthenticationFeature + { + /// + /// The original path base. + /// + PathString OriginalPathBase { get; set; } + + /// + /// The original path. + /// + PathString OriginalPath { get; set; } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandler.cs new file mode 100644 index 00000000..7a805f7a --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandler.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Created per request to handle authentication for to a particular scheme. + /// + public interface IAuthenticationHandler + { + /// + /// The handler should initialize anything it needs from the request and scheme here. + /// + /// The scheme. + /// The context. + /// + Task InitializeAsync(AuthenticationScheme scheme, HttpContext context); + + /// + /// Authentication behavior. + /// + /// The context. + /// The result. + Task AuthenticateAsync(AuthenticateContext context); + + /// + /// Challenge behavior. + /// + /// The context. + /// A task. + Task ChallengeAsync(ChallengeContext context); + + /// + /// Handle sign in. + /// + /// The context. + /// A task. + Task SignInAsync(SignInContext context); + + /// + /// Signout behavior. + /// + /// The context. + /// A task. + Task SignOutAsync(SignOutContext context); + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandlerProvider.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandlerProvider.cs new file mode 100644 index 00000000..0507f51d --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandlerProvider.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Provides the appropriate IAuthenticationHandler instance for the authenticationScheme and request. + /// + public interface IAuthenticationHandlerProvider + { + /// + /// Returns the handler instance that will be used. + /// + /// The context. + /// The name of the authentication scheme being handled. + /// The handler instance. + Task GetHandlerAsync(HttpContext context, string authenticationScheme); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationRequestHandler.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationRequestHandler.cs new file mode 100644 index 00000000..fffe08f4 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationRequestHandler.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Used to determine if a handler wants to participate in request processing. + /// + public interface IAuthenticationRequestHandler : IAuthenticationHandler + { + + /// + /// Returns true if request processing should stop. + /// + /// + Task HandleRequestAsync(); + } + +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSchemeProvider.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSchemeProvider.cs new file mode 100644 index 00000000..4b36abda --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSchemeProvider.cs @@ -0,0 +1,70 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Responsible for managing what authenticationSchemes are supported. + /// + public interface IAuthenticationSchemeProvider + { + /// + /// Returns all currently registered s. + /// + /// All currently registered s. + Task> GetAllSchemesAsync(); + + /// + /// Returns the matching the name, or null. + /// + /// The name of the authenticationScheme. + /// The scheme or null if not found. + Task GetSchemeAsync(string name); + + /// + /// Returns the scheme that will be used by default for . + /// This is typically specified via . + /// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned. + /// + /// The scheme that will be used by default for . + Task GetDefaultAuthenticateSchemeAsync(); + + /// + /// Returns the scheme that will be used by default for . + /// This is typically specified via . + /// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned. + /// + /// The scheme that will be used by default for . + Task GetDefaultChallengeSchemeAsync(); + + /// + /// Returns the scheme that will be used by default for . + /// This is typically specified via . + /// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned. + /// + /// The scheme that will be used by default for . + Task GetDefaultSignInSchemeAsync(); + + /// + /// Registers a scheme for use by . + /// + /// The scheme. + void AddScheme(AuthenticationScheme scheme); + + /// + /// Removes a scheme, preventing it from being used by . + /// + /// The name of the authenticationScheme being removed. + void RemoveScheme(string name); + + /// + /// Returns the schemes in priority order for request handling. + /// + /// The schemes in priority order for request handling + Task> GetRequestHandlerSchemesAsync(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationService.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationService.cs new file mode 100644 index 00000000..ec54325e --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationService.cs @@ -0,0 +1,52 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Used to provide authentication. + /// + public interface IAuthenticationService + { + /// + /// Authenticate for the specified authentication scheme. + /// + /// The . + /// The name of the authentication scheme. + /// The result. + Task AuthenticateAsync(HttpContext context, string scheme); + + /// + /// Challenge the specified authentication scheme. + /// + /// The . + /// The name of the authentication scheme. + /// The . + /// The . + /// A task. + Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties, ChallengeBehavior behavior); + + /// + /// Sign a principal in for the specified authentication scheme. + /// + /// The . + /// The name of the authentication scheme. + /// The to sign in. + /// The . + /// A task. + Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties); + + /// + /// Sign out the specified authentication scheme. + /// + /// The . + /// The name of the authentication scheme. + /// The . + /// A task. + Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties); + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IClaimsTransformation.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IClaimsTransformation.cs new file mode 100644 index 00000000..3aed710a --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IClaimsTransformation.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Security.Claims; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Used by the for claims transformation. + /// + public interface IClaimsTransformation + { + /// + /// Provides a central transformation point to change the specified principal. + /// + /// The to transform. + /// The transformed principal. + Task TransformAsync(ClaimsPrincipal principal); + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/Microsoft.AspNetCore.Authentication.Abstractions.csproj b/src/Microsoft.AspNetCore.Authentication.Abstractions/Microsoft.AspNetCore.Authentication.Abstractions.csproj new file mode 100644 index 00000000..aa2fae7f --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/Microsoft.AspNetCore.Authentication.Abstractions.csproj @@ -0,0 +1,24 @@ + + + + ASP.NET Core common types used by the various authentication components. + netstandard1.3;net46 + $(NoWarn);CS1591 + true + aspnetcore;authentication;security + false + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/SignInContext.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/SignInContext.cs new file mode 100644 index 00000000..e89b663a --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/SignInContext.cs @@ -0,0 +1,37 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Security.Claims; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Context used for sign out. + /// + public class SignInContext : BaseAuthenticationContext + { + /// + /// Constructor. + /// + /// The context. + /// The name of the authentication scheme. + /// The user to sign in. + /// The properties. + public SignInContext(HttpContext context, string authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties properties) + : base(context, authenticationScheme, properties) + { + if (principal == null) + { + throw new ArgumentNullException(nameof(principal)); + } + Principal = principal; + } + + /// + /// The user to sign in. + /// + public ClaimsPrincipal Principal { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/SignOutContext.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/SignOutContext.cs new file mode 100644 index 00000000..307a3af8 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/SignOutContext.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Context used to sign out. + /// + public class SignOutContext : BaseAuthenticationContext + { + /// + /// Constructor. + /// + /// The context. + /// The name of the authentication scheme. + /// The properties. + public SignOutContext(HttpContext context, string authenticationScheme, AuthenticationProperties properties) + : base(context, authenticationScheme, properties) + { } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/TokenExtensions.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/TokenExtensions.cs new file mode 100644 index 00000000..24f66d90 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/TokenExtensions.cs @@ -0,0 +1,155 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Extension methods for storing authentication tokens in . + /// + public static class AuthenticationTokenExtensions + { + private static string TokenNamesKey = ".TokenNames"; + private static string TokenKeyPrefix = ".Token."; + + /// + /// Stores a set of authentication tokens, after removing any old tokens. + /// + /// The properties. + /// The tokens to store. + public static void StoreTokens(this AuthenticationProperties properties, IEnumerable tokens) + { + if (properties == null) + { + throw new ArgumentNullException(nameof(properties)); + } + if (tokens == null) + { + throw new ArgumentNullException(nameof(tokens)); + } + + // Clear old tokens first + var oldTokens = properties.GetTokens(); + foreach (var t in oldTokens) + { + properties.Items.Remove(TokenKeyPrefix + t.Name); + } + properties.Items.Remove(TokenNamesKey); + + var tokenNames = new List(); + foreach (var token in tokens) + { + // REVIEW: should probably check that there are no ; in the token name and throw or encode + tokenNames.Add(token.Name); + properties.Items[TokenKeyPrefix+token.Name] = token.Value; + } + if (tokenNames.Count > 0) + { + properties.Items[TokenNamesKey] = string.Join(";", tokenNames.ToArray()); + } + } + + /// + /// Returns the value of a token. + /// + /// The properties. + /// The token name. + /// The token value. + public static string GetTokenValue(this AuthenticationProperties properties, string tokenName) + { + if (properties == null) + { + throw new ArgumentNullException(nameof(properties)); + } + if (tokenName == null) + { + throw new ArgumentNullException(nameof(tokenName)); + } + + var tokenKey = TokenKeyPrefix + tokenName; + return properties.Items.ContainsKey(tokenKey) + ? properties.Items[tokenKey] + : null; + } + + public static bool UpdateTokenValue(this AuthenticationProperties properties, string tokenName, string tokenValue) + { + if (properties == null) + { + throw new ArgumentNullException(nameof(properties)); + } + if (tokenName == null) + { + throw new ArgumentNullException(nameof(tokenName)); + } + + var tokenKey = TokenKeyPrefix + tokenName; + if (!properties.Items.ContainsKey(tokenKey)) + { + return false; + } + properties.Items[tokenKey] = tokenValue; + return true; + } + + /// + /// Returns all of the AuthenticationTokens contained in the properties. + /// + /// The properties. + /// The authentication toekns. + public static IEnumerable GetTokens(this AuthenticationProperties properties) + { + if (properties == null) + { + throw new ArgumentNullException(nameof(properties)); + } + + var tokens = new List(); + if (properties.Items.ContainsKey(TokenNamesKey)) + { + var tokenNames = properties.Items[TokenNamesKey].Split(';'); + foreach (var name in tokenNames) + { + var token = properties.GetTokenValue(name); + if (token != null) + { + tokens.Add(new AuthenticationToken { Name = name, Value = token }); + } + } + } + + return tokens; + } + + /// + /// Extension method for getting the value of an authentication token. + /// + /// The . + /// The context. + /// The name of the authentication scheme. + /// The name of the token. + /// The value of the token. + public static async Task GetTokenAsync(this IAuthenticationService auth, HttpContext context, string scheme, string tokenName) + { + if (auth == null) + { + throw new ArgumentNullException(nameof(auth)); + } + if (scheme == null) + { + throw new ArgumentNullException(nameof(scheme)); + } + if (tokenName == null) + { + throw new ArgumentNullException(nameof(tokenName)); + } + + var result = await auth.AuthenticateAsync(context, scheme); + return result?.Properties?.GetTokenValue(tokenName); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationCoreServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationCoreServiceCollectionExtensions.cs new file mode 100644 index 00000000..fdf85a9b --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationCoreServiceCollectionExtensions.cs @@ -0,0 +1,56 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Extension methods for setting up authentication services in an . + /// + public static class AuthenticationCoreServiceCollectionExtensions + { + /// + /// Add core authentication services needed for . + /// + /// The . + /// The service collection. + public static IServiceCollection AddAuthenticationCore(this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + services.TryAddScoped(); + services.TryAddSingleton(); // Can be replaced with scoped ones that use DbContext + services.TryAddScoped(); + services.TryAddSingleton(); + return services; + } + + /// + /// Add core authentication services needed for . + /// + /// The . + /// Used to configure the . + /// The service collection. + public static IServiceCollection AddAuthenticationCore(this IServiceCollection services, Action configureOptions) { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (configureOptions == null) + { + throw new ArgumentNullException(nameof(configureOptions)); + } + + services.AddAuthenticationCore(); + services.Configure(configureOptions); + return services; + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationFeature.cs b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationFeature.cs new file mode 100644 index 00000000..3282cbf4 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationFeature.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Used to capture path info so redirects can be computed properly within an app.Map(). + /// + public class AuthenticationFeature : IAuthenticationFeature + { + /// + /// The original path base. + /// + public PathString OriginalPathBase { get; set; } + + /// + /// The original path. + /// + public PathString OriginalPath { get; set; } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationHandlerProvider.cs b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationHandlerProvider.cs new file mode 100644 index 00000000..c4921e53 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationHandlerProvider.cs @@ -0,0 +1,63 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Implementation of . + /// + public class AuthenticationHandlerProvider : IAuthenticationHandlerProvider + { + /// + /// Constructor. + /// + /// The . + public AuthenticationHandlerProvider(IAuthenticationSchemeProvider schemes) + { + Schemes = schemes; + } + + /// + /// The . + /// + public IAuthenticationSchemeProvider Schemes { get; } + + // handler instance cache, need to initialize once per request + private Dictionary _handlerMap = new Dictionary(StringComparer.Ordinal); + + /// + /// Returns the handler instance that will be used. + /// + /// The context. + /// The name of the authentication scheme being handled. + /// The handler instance. + public async Task GetHandlerAsync(HttpContext context, string authenticationScheme) + { + if (_handlerMap.ContainsKey(authenticationScheme)) + { + return _handlerMap[authenticationScheme]; + } + + var scheme = await Schemes.GetSchemeAsync(authenticationScheme); + if (scheme == null) + { + return null; + } + var handler = (context.RequestServices.GetService(scheme.HandlerType) ?? + ActivatorUtilities.CreateInstance(context.RequestServices, scheme.HandlerType)) + as IAuthenticationHandler; + if (handler != null) + { + await handler.InitializeAsync(scheme, context); + _handlerMap[authenticationScheme] = handler; + } + return handler; + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs new file mode 100644 index 00000000..fe347d6e --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs @@ -0,0 +1,170 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Implements . + /// + public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider + { + /// + /// Constructor. + /// + /// The options. + public AuthenticationSchemeProvider(IOptions options) + { + _options = options.Value; + + foreach (var builder in _options.Schemes) + { + var scheme = builder.Build(); + AddScheme(scheme); + } + } + + private readonly AuthenticationOptions _options; + private readonly object _lock = new object(); + + private IDictionary _map = new Dictionary(StringComparer.Ordinal); + private List _requestHandlers = new List(); + + /// + /// Returns the scheme that will be used by default for . + /// This is typically specified via . + /// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned. + /// + /// The scheme that will be used by default for . + public Task GetDefaultAuthenticateSchemeAsync() + { + if (_options.DefaultAuthenticationScheme != null) + { + return GetSchemeAsync(_options.DefaultAuthenticationScheme); + } + if (_map.Count == 1) + { + return Task.FromResult(_map.Values.First()); + } + return Task.FromResult(null); + } + + /// + /// Returns the scheme that will be used by default for . + /// This is typically specified via . + /// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned. + /// + /// The scheme that will be used by default for . + public Task GetDefaultChallengeSchemeAsync() + { + if (_options.DefaultChallengeScheme != null) + { + return GetSchemeAsync(_options.DefaultChallengeScheme); + } + if (_map.Count == 1) + { + return Task.FromResult(_map.Values.First()); + } + return Task.FromResult(null); + } + + /// + /// Returns the scheme that will be used by default for . + /// This is typically specified via . + /// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned. + /// + /// The scheme that will be used by default for . + public Task GetDefaultSignInSchemeAsync() + { + if (_options.DefaultSignInScheme != null) + { + return GetSchemeAsync(_options.DefaultSignInScheme); + } + if (_map.Count == 1) + { + return Task.FromResult(_map.Values.First()); + } + return Task.FromResult(null); + } + + /// + /// Returns the matching the name, or null. + /// + /// The name of the authenticationScheme. + /// The scheme or null if not found. + public Task GetSchemeAsync(string name) + { + if (_map.ContainsKey(name)) + { + return Task.FromResult(_map[name]); + } + return Task.FromResult(null); + } + + /// + /// Returns the schemes in priority order for request handling. + /// + /// The schemes in priority order for request handling + public Task> GetRequestHandlerSchemesAsync() + { + return Task.FromResult>(_requestHandlers); + } + + /// + /// Registers a scheme for use by . + /// + /// The scheme. + public void AddScheme(AuthenticationScheme scheme) + { + if (_map.ContainsKey(scheme.Name)) + { + throw new InvalidOperationException("Scheme already exists: " + scheme.Name); + } + lock (_lock) + { + if (_map.ContainsKey(scheme.Name)) + { + throw new InvalidOperationException("Scheme already exists: " + scheme.Name); + } + if (typeof(IAuthenticationRequestHandler).IsAssignableFrom(scheme.HandlerType)) + { + _requestHandlers.Add(scheme); + } + _map[scheme.Name] = scheme; + } + } + + /// + /// Removes a scheme, preventing it from being used by . + /// + /// The name of the authenticationScheme being removed. + public void RemoveScheme(string name) + { + if (!_map.ContainsKey(name)) + { + return; + } + lock (_lock) + { + if (_map.ContainsKey(name)) + { + var scheme = _map[name]; + _requestHandlers.Remove(_requestHandlers.Where(s => s.Name == name).FirstOrDefault()); + _map.Remove(name); + } + } + } + + public Task> GetAllSchemesAsync() + { + return Task.FromResult>(_map.Values); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationService.cs b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationService.cs new file mode 100644 index 00000000..9b8837f2 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationService.cs @@ -0,0 +1,167 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Implements . + /// + public class AuthenticationService : IAuthenticationService + { + /// + /// Constructor. + /// + /// The . + /// The . + /// The The . + public AuthenticationService(IAuthenticationSchemeProvider schemes, IAuthenticationHandlerProvider handlers, IClaimsTransformation transform) + { + Schemes = schemes; + Handlers = handlers; + Transform = transform; + } + + /// + /// Used to lookup AuthenticationSchemes. + /// + public IAuthenticationSchemeProvider Schemes { get; } + + /// + /// Used to resolve IAuthenticationHandler instances. + /// + public IAuthenticationHandlerProvider Handlers { get; } + + /// + /// Used for claims transformation. + /// + public IClaimsTransformation Transform { get; } + + /// + /// Authenticate for the specified authentication scheme. + /// + /// The . + /// The name of the authentication scheme. + /// The result. + public virtual async Task AuthenticateAsync(HttpContext context, string scheme) + { + if (scheme == null) + { + var defaultScheme = await Schemes.GetDefaultAuthenticateSchemeAsync(); + scheme = defaultScheme?.Name; + if (scheme == null) + { + throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultAuthenticateScheme found."); + } + } + + var handler = await Handlers.GetHandlerAsync(context, scheme); + if (handler == null) + { + throw new InvalidOperationException($"No authentication handler is configured to authenticate for the scheme: {scheme}"); + } + + var authContext = new AuthenticateContext(context, scheme); + var result = await handler.AuthenticateAsync(authContext); + if (result.Succeeded) + { + var transformed = await Transform.TransformAsync(result.Principal); + return AuthenticateResult.Success(new AuthenticationTicket(transformed, result.Properties, result.Ticket.AuthenticationScheme)); + } + return result; + } + + /// + /// Challenge the specified authentication scheme. + /// + /// The . + /// The name of the authentication scheme. + /// The . + /// The . + /// A task. + public virtual async Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties, ChallengeBehavior behavior) + { + if (scheme == null) + { + var defaultChallengeScheme = await Schemes.GetDefaultChallengeSchemeAsync(); + scheme = defaultChallengeScheme?.Name; + if (scheme == null) + { + throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultChallengeScheme found."); + } + } + + var handler = await Handlers.GetHandlerAsync(context, scheme); + if (handler == null) + { + throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}"); + } + + var challengeContext = new ChallengeContext(context, scheme, properties, behavior); + await handler.ChallengeAsync(challengeContext); + } + + /// + /// Sign a principal in for the specified authentication scheme. + /// + /// The . + /// The name of the authentication scheme. + /// The to sign in. + /// The . + /// A task. + public virtual async Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) + { + if (principal == null) + { + throw new ArgumentNullException(nameof(principal)); + } + + if (scheme == null) + { + var defaultScheme = await Schemes.GetDefaultSignInSchemeAsync(); + scheme = defaultScheme?.Name; + if (scheme == null) + { + throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultSignInScheme found."); + } + } + + var handler = await Handlers.GetHandlerAsync(context, scheme); + if (handler == null) + { + throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}"); + } + + var signInContext = new SignInContext(context, scheme, principal, properties); + await handler.SignInAsync(signInContext); + } + + /// + /// Sign out the specified authentication scheme. + /// + /// The . + /// The name of the authentication scheme. + /// The . + /// A task. + public virtual async Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties) + { + if (string.IsNullOrEmpty(scheme)) + { + throw new ArgumentException(nameof(scheme)); + } + + var handler = await Handlers.GetHandlerAsync(context, scheme); + if (handler == null) + { + throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}"); + } + + var signOutContext = new SignOutContext(context, scheme, properties); + await handler.SignOutAsync(signOutContext); + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Core/Microsoft.AspNetCore.Authentication.Core.csproj b/src/Microsoft.AspNetCore.Authentication.Core/Microsoft.AspNetCore.Authentication.Core.csproj new file mode 100644 index 00000000..61628336 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Core/Microsoft.AspNetCore.Authentication.Core.csproj @@ -0,0 +1,21 @@ + + + + + + ASP.NET Core common types used by the various authentication middleware components. + netstandard1.3;net46 + $(NoWarn);CS1591 + true + aspnetcore;authentication;security + false + + + + + + + + + + diff --git a/src/Microsoft.AspNetCore.Authentication.Core/NoopClaimsTransformation.cs b/src/Microsoft.AspNetCore.Authentication.Core/NoopClaimsTransformation.cs new file mode 100644 index 00000000..83c488fe --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Core/NoopClaimsTransformation.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Security.Claims; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Default claims transformation is a no-op. + /// + public class NoopClaimsTransformation : IClaimsTransformation + { + /// + /// Returns the principal unchanged. + /// + /// The user. + /// The principal unchanged. + public virtual Task TransformAsync(ClaimsPrincipal principal) + { + return Task.FromResult(principal); + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Core/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Authentication.Core/Properties/Resources.Designer.cs new file mode 100644 index 00000000..11e2e458 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Core/Properties/Resources.Designer.cs @@ -0,0 +1,94 @@ +// +namespace Microsoft.AspNetCore.Authentication +{ + using System.Globalization; + using System.Reflection; + using System.Resources; + + internal static class Resources + { + private static readonly ResourceManager _resourceManager + = new ResourceManager("Microsoft.AspNetCore.Authentication.Resources", typeof(Resources).GetTypeInfo().Assembly); + + /// + /// The default data protection provider may only be used when the IApplicationBuilder.Properties contains an appropriate 'host.AppName' key. + /// + internal static string Exception_DefaultDpapiRequiresAppNameKey + { + get { return GetString("Exception_DefaultDpapiRequiresAppNameKey"); } + } + + /// + /// The default data protection provider may only be used when the IApplicationBuilder.Properties contains an appropriate 'host.AppName' key. + /// + internal static string FormatException_DefaultDpapiRequiresAppNameKey() + { + return GetString("Exception_DefaultDpapiRequiresAppNameKey"); + } + + /// + /// The state passed to UnhookAuthentication may only be the return value from HookAuthentication. + /// + internal static string Exception_UnhookAuthenticationStateType + { + get { return GetString("Exception_UnhookAuthenticationStateType"); } + } + + /// + /// The state passed to UnhookAuthentication may only be the return value from HookAuthentication. + /// + internal static string FormatException_UnhookAuthenticationStateType() + { + return GetString("Exception_UnhookAuthenticationStateType"); + } + + /// + /// The AuthenticationTokenProvider's required synchronous events have not been registered. + /// + internal static string Exception_AuthenticationTokenDoesNotProvideSyncMethods + { + get { return GetString("Exception_AuthenticationTokenDoesNotProvideSyncMethods"); } + } + + /// + /// The AuthenticationTokenProvider's required synchronous events have not been registered. + /// + internal static string FormatException_AuthenticationTokenDoesNotProvideSyncMethods() + { + return GetString("Exception_AuthenticationTokenDoesNotProvideSyncMethods"); + } + + /// + /// The '{0}' option must be provided. + /// + internal static string Exception_OptionMustBeProvided + { + get { return GetString("Exception_OptionMustBeProvided"); } + } + + /// + /// The '{0}' option must be provided. + /// + internal static string FormatException_OptionMustBeProvided(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); + } + + private static string GetString(string name, params string[] formatterNames) + { + var value = _resourceManager.GetString(name); + + System.Diagnostics.Debug.Assert(value != null); + + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + + return value; + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Core/Resources.resx b/src/Microsoft.AspNetCore.Authentication.Core/Resources.resx new file mode 100644 index 00000000..54d22bcc --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Core/Resources.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The default data protection provider may only be used when the IApplicationBuilder.Properties contains an appropriate 'host.AppName' key. + + + The state passed to UnhookAuthentication may only be the return value from HookAuthentication. + + + The AuthenticationTokenProvider's required synchronous events have not been registered. + + + The '{0}' option must be provided. + + \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticateInfo.cs b/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticateInfo.cs index 9e8e3fd5..3c893dbb 100644 --- a/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticateInfo.cs +++ b/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticateInfo.cs @@ -9,21 +9,25 @@ namespace Microsoft.AspNetCore.Http.Authentication /// /// Used to store the results of an Authenticate call. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public class AuthenticateInfo { /// /// The . /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public ClaimsPrincipal Principal { get; set; } /// /// The . /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public AuthenticationProperties Properties { get; set; } /// /// The . /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public AuthenticationDescription Description { get; set; } } } diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticationDescription.cs b/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticationDescription.cs index fb0a073f..fb2d00c8 100644 --- a/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticationDescription.cs +++ b/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticationDescription.cs @@ -10,6 +10,7 @@ namespace Microsoft.AspNetCore.Http.Authentication /// /// Contains information describing an authentication provider. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public class AuthenticationDescription { private const string DisplayNamePropertyKey = "DisplayName"; diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticationManager.cs b/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticationManager.cs index 56d9dbad..164618dc 100644 --- a/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticationManager.cs +++ b/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticationManager.cs @@ -9,37 +9,47 @@ namespace Microsoft.AspNetCore.Http.Authentication { + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public abstract class AuthenticationManager { /// /// Constant used to represent the automatic scheme /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public const string AutomaticScheme = "Automatic"; + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public abstract HttpContext HttpContext { get; } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public abstract IEnumerable GetAuthenticationSchemes(); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public abstract Task GetAuthenticateInfoAsync(string authenticationScheme); // Will remove once callees have been updated + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public abstract Task AuthenticateAsync(AuthenticateContext context); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public virtual async Task AuthenticateAsync(string authenticationScheme) { return (await GetAuthenticateInfoAsync(authenticationScheme))?.Principal; } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public virtual Task ChallengeAsync() { return ChallengeAsync(properties: null); } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public virtual Task ChallengeAsync(AuthenticationProperties properties) { return ChallengeAsync(authenticationScheme: AutomaticScheme, properties: properties); } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public virtual Task ChallengeAsync(string authenticationScheme) { if (string.IsNullOrEmpty(authenticationScheme)) @@ -51,6 +61,7 @@ public virtual Task ChallengeAsync(string authenticationScheme) } // Leave it up to authentication handler to do the right thing for the challenge + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public virtual Task ChallengeAsync(string authenticationScheme, AuthenticationProperties properties) { if (string.IsNullOrEmpty(authenticationScheme)) @@ -61,6 +72,7 @@ public virtual Task ChallengeAsync(string authenticationScheme, AuthenticationPr return ChallengeAsync(authenticationScheme, properties, ChallengeBehavior.Automatic); } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public virtual Task SignInAsync(string authenticationScheme, ClaimsPrincipal principal) { if (string.IsNullOrEmpty(authenticationScheme)) @@ -80,9 +92,11 @@ public virtual Task SignInAsync(string authenticationScheme, ClaimsPrincipal pri /// Creates a challenge for the authentication manager with . /// /// A that represents the asynchronous challenge operation. + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public virtual Task ForbidAsync() => ForbidAsync(AutomaticScheme, properties: null); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public virtual Task ForbidAsync(string authenticationScheme) { if (authenticationScheme == null) @@ -94,6 +108,7 @@ public virtual Task ForbidAsync(string authenticationScheme) } // Deny access (typically a 403) + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public virtual Task ForbidAsync(string authenticationScheme, AuthenticationProperties properties) { if (authenticationScheme == null) @@ -109,13 +124,17 @@ public virtual Task ForbidAsync(string authenticationScheme, AuthenticationPrope /// /// Additional arbitrary values which may be used by particular authentication types. /// A that represents the asynchronous challenge operation. + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public virtual Task ForbidAsync(AuthenticationProperties properties) => ForbidAsync(AutomaticScheme, properties); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public abstract Task ChallengeAsync(string authenticationScheme, AuthenticationProperties properties, ChallengeBehavior behavior); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public abstract Task SignInAsync(string authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties properties); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public virtual Task SignOutAsync(string authenticationScheme) { if (authenticationScheme == null) @@ -126,6 +145,7 @@ public virtual Task SignOutAsync(string authenticationScheme) return SignOutAsync(authenticationScheme, properties: null); } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public abstract Task SignOutAsync(string authenticationScheme, AuthenticationProperties properties); } } diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticationProperties.cs b/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticationProperties.cs index 6e883efb..a01c5d4c 100644 --- a/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticationProperties.cs +++ b/src/Microsoft.AspNetCore.Http.Abstractions/Authentication/AuthenticationProperties.cs @@ -10,6 +10,7 @@ namespace Microsoft.AspNetCore.Http.Authentication /// /// Dictionary used to store state values about the authentication session. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public class AuthenticationProperties { internal const string IssuedUtcKey = ".issued"; @@ -39,11 +40,13 @@ public AuthenticationProperties(IDictionary items) /// /// State values about the authentication session. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public IDictionary Items { get; } /// /// Gets or sets whether the authentication session is persisted across multiple requests. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public bool IsPersistent { get { return Items.ContainsKey(IsPersistentKey); } @@ -69,6 +72,7 @@ public bool IsPersistent /// /// Gets or sets the full path or absolute URI to be used as an http redirect response value. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public string RedirectUri { get @@ -95,6 +99,7 @@ public string RedirectUri /// /// Gets or sets the time at which the authentication ticket was issued. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public DateTimeOffset? IssuedUtc { get @@ -129,6 +134,7 @@ public DateTimeOffset? IssuedUtc /// /// Gets or sets the time at which the authentication ticket expires. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public DateTimeOffset? ExpiresUtc { get @@ -163,6 +169,7 @@ public DateTimeOffset? ExpiresUtc /// /// Gets or sets if refreshing the authentication session should be allowed. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public bool? AllowRefresh { get diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/HttpContext.cs b/src/Microsoft.AspNetCore.Http.Abstractions/HttpContext.cs index 7f72dcd8..6b38ae43 100644 --- a/src/Microsoft.AspNetCore.Http.Abstractions/HttpContext.cs +++ b/src/Microsoft.AspNetCore.Http.Abstractions/HttpContext.cs @@ -43,6 +43,7 @@ public abstract class HttpContext /// /// Gets an object that facilitates authentication for this request. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public abstract AuthenticationManager Authentication { get; } /// diff --git a/src/Microsoft.AspNetCore.Http.Features/Authentication/AuthenticateContext.cs b/src/Microsoft.AspNetCore.Http.Features/Authentication/AuthenticateContext.cs index e7306166..67e89f18 100644 --- a/src/Microsoft.AspNetCore.Http.Features/Authentication/AuthenticateContext.cs +++ b/src/Microsoft.AspNetCore.Http.Features/Authentication/AuthenticateContext.cs @@ -7,6 +7,7 @@ namespace Microsoft.AspNetCore.Http.Features.Authentication { + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public class AuthenticateContext { public AuthenticateContext(string authenticationScheme) diff --git a/src/Microsoft.AspNetCore.Http.Features/Authentication/ChallengeBehavior.cs b/src/Microsoft.AspNetCore.Http.Features/Authentication/ChallengeBehavior.cs index 549d5113..9fdceb0a 100644 --- a/src/Microsoft.AspNetCore.Http.Features/Authentication/ChallengeBehavior.cs +++ b/src/Microsoft.AspNetCore.Http.Features/Authentication/ChallengeBehavior.cs @@ -1,8 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; + namespace Microsoft.AspNetCore.Http.Features.Authentication { + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public enum ChallengeBehavior { Automatic, diff --git a/src/Microsoft.AspNetCore.Http.Features/Authentication/ChallengeContext.cs b/src/Microsoft.AspNetCore.Http.Features/Authentication/ChallengeContext.cs index c0fe4708..d8e04c14 100644 --- a/src/Microsoft.AspNetCore.Http.Features/Authentication/ChallengeContext.cs +++ b/src/Microsoft.AspNetCore.Http.Features/Authentication/ChallengeContext.cs @@ -6,6 +6,7 @@ namespace Microsoft.AspNetCore.Http.Features.Authentication { + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public class ChallengeContext { public ChallengeContext(string authenticationScheme) diff --git a/src/Microsoft.AspNetCore.Http.Features/Authentication/DescribeSchemesContext.cs b/src/Microsoft.AspNetCore.Http.Features/Authentication/DescribeSchemesContext.cs index b25c2c97..e86f8475 100644 --- a/src/Microsoft.AspNetCore.Http.Features/Authentication/DescribeSchemesContext.cs +++ b/src/Microsoft.AspNetCore.Http.Features/Authentication/DescribeSchemesContext.cs @@ -1,10 +1,12 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; namespace Microsoft.AspNetCore.Http.Features.Authentication { + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public class DescribeSchemesContext { private List> _results; diff --git a/src/Microsoft.AspNetCore.Http.Features/Authentication/IAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Http.Features/Authentication/IAuthenticationHandler.cs index 3b723641..7d5b9c01 100644 --- a/src/Microsoft.AspNetCore.Http.Features/Authentication/IAuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Http.Features/Authentication/IAuthenticationHandler.cs @@ -1,20 +1,27 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Threading.Tasks; namespace Microsoft.AspNetCore.Http.Features.Authentication { + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public interface IAuthenticationHandler { + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] void GetDescriptions(DescribeSchemesContext context); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] Task AuthenticateAsync(AuthenticateContext context); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] Task ChallengeAsync(ChallengeContext context); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] Task SignInAsync(SignInContext context); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] Task SignOutAsync(SignOutContext context); } } diff --git a/src/Microsoft.AspNetCore.Http.Features/Authentication/IHttpAuthenticationFeature.cs b/src/Microsoft.AspNetCore.Http.Features/Authentication/IHttpAuthenticationFeature.cs index 080ce405..b018e51a 100644 --- a/src/Microsoft.AspNetCore.Http.Features/Authentication/IHttpAuthenticationFeature.cs +++ b/src/Microsoft.AspNetCore.Http.Features/Authentication/IHttpAuthenticationFeature.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Security.Claims; namespace Microsoft.AspNetCore.Http.Features.Authentication @@ -9,6 +10,7 @@ public interface IHttpAuthenticationFeature { ClaimsPrincipal User { get; set; } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] IAuthenticationHandler Handler { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Http.Features/Authentication/SignInContext.cs b/src/Microsoft.AspNetCore.Http.Features/Authentication/SignInContext.cs index f04dade5..982f4400 100644 --- a/src/Microsoft.AspNetCore.Http.Features/Authentication/SignInContext.cs +++ b/src/Microsoft.AspNetCore.Http.Features/Authentication/SignInContext.cs @@ -7,6 +7,7 @@ namespace Microsoft.AspNetCore.Http.Features.Authentication { + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public class SignInContext { public SignInContext(string authenticationScheme, ClaimsPrincipal principal, IDictionary properties) diff --git a/src/Microsoft.AspNetCore.Http.Features/Authentication/SignOutContext.cs b/src/Microsoft.AspNetCore.Http.Features/Authentication/SignOutContext.cs index c752f057..e99773e9 100644 --- a/src/Microsoft.AspNetCore.Http.Features/Authentication/SignOutContext.cs +++ b/src/Microsoft.AspNetCore.Http.Features/Authentication/SignOutContext.cs @@ -6,6 +6,7 @@ namespace Microsoft.AspNetCore.Http.Features.Authentication { + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public class SignOutContext { public SignOutContext(string authenticationScheme, IDictionary properties) diff --git a/src/Microsoft.AspNetCore.Http/Authentication/DefaultAuthenticationManager.cs b/src/Microsoft.AspNetCore.Http/Authentication/DefaultAuthenticationManager.cs index 666e2179..028555f4 100644 --- a/src/Microsoft.AspNetCore.Http/Authentication/DefaultAuthenticationManager.cs +++ b/src/Microsoft.AspNetCore.Http/Authentication/DefaultAuthenticationManager.cs @@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Http.Authentication.Internal { + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public class DefaultAuthenticationManager : AuthenticationManager { // Lambda hoisted to static readonly field to improve inlining https://github.com/dotnet/roslyn/issues/13624 @@ -35,11 +36,13 @@ public virtual void Uninitialize() _features = default(FeatureReferences); } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public override HttpContext HttpContext => _context; private IHttpAuthenticationFeature HttpAuthenticationFeature => _features.Fetch(ref _features.Cache, _newAuthenticationFeature); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public override IEnumerable GetAuthenticationSchemes() { var handler = HttpAuthenticationFeature.Handler; @@ -54,6 +57,7 @@ public override IEnumerable GetAuthenticationSchemes( } // Remove once callers have been switched to GetAuthenticateInfoAsync + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public override async Task AuthenticateAsync(AuthenticateContext context) { if (context == null) @@ -73,6 +77,7 @@ public override async Task AuthenticateAsync(AuthenticateContext context) } } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public override async Task GetAuthenticateInfoAsync(string authenticationScheme) { if (authenticationScheme == null) @@ -100,6 +105,7 @@ public override async Task GetAuthenticateInfoAsync(string aut }; } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public override async Task ChallengeAsync(string authenticationScheme, AuthenticationProperties properties, ChallengeBehavior behavior) { if (string.IsNullOrEmpty(authenticationScheme)) @@ -121,6 +127,7 @@ public override async Task ChallengeAsync(string authenticationScheme, Authentic } } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public override async Task SignInAsync(string authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties properties) { if (string.IsNullOrEmpty(authenticationScheme)) @@ -147,6 +154,7 @@ public override async Task SignInAsync(string authenticationScheme, ClaimsPrinci } } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public override async Task SignOutAsync(string authenticationScheme, AuthenticationProperties properties) { if (string.IsNullOrEmpty(authenticationScheme)) diff --git a/src/Microsoft.AspNetCore.Http/DefaultHttpContext.cs b/src/Microsoft.AspNetCore.Http/DefaultHttpContext.cs index d1e431c7..883cc33e 100644 --- a/src/Microsoft.AspNetCore.Http/DefaultHttpContext.cs +++ b/src/Microsoft.AspNetCore.Http/DefaultHttpContext.cs @@ -28,7 +28,9 @@ public class DefaultHttpContext : HttpContext private HttpRequest _request; private HttpResponse _response; +#pragma warning disable 618 private AuthenticationManager _authenticationManager; +#pragma warning restore 618 private ConnectionInfo _connection; private WebSocketManager _websockets; @@ -66,7 +68,9 @@ public virtual void Uninitialize() } if (_authenticationManager != null) { +#pragma warning disable 618 UninitializeAuthenticationManager(_authenticationManager); +#pragma warning restore 618 _authenticationManager = null; } if (_connection != null) @@ -111,6 +115,7 @@ public virtual void Uninitialize() public override ConnectionInfo Connection => _connection ?? (_connection = InitializeConnectionInfo()); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public override AuthenticationManager Authentication => _authenticationManager ?? (_authenticationManager = InitializeAuthenticationManager()); public override WebSocketManager WebSockets => _websockets ?? (_websockets = InitializeWebSocketManager()); @@ -190,7 +195,9 @@ protected virtual void UninitializeHttpResponse(HttpResponse instance) { } protected virtual ConnectionInfo InitializeConnectionInfo() => new DefaultConnectionInfo(Features); protected virtual void UninitializeConnectionInfo(ConnectionInfo instance) { } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] protected virtual AuthenticationManager InitializeAuthenticationManager() => new DefaultAuthenticationManager(this); + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] protected virtual void UninitializeAuthenticationManager(AuthenticationManager instance) { } protected virtual WebSocketManager InitializeWebSocketManager() => new DefaultWebSocketManager(Features); diff --git a/src/Microsoft.AspNetCore.Http/Features/Authentication/HttpAuthenticationFeature.cs b/src/Microsoft.AspNetCore.Http/Features/Authentication/HttpAuthenticationFeature.cs index 9a14b657..89f06e3b 100644 --- a/src/Microsoft.AspNetCore.Http/Features/Authentication/HttpAuthenticationFeature.cs +++ b/src/Microsoft.AspNetCore.Http/Features/Authentication/HttpAuthenticationFeature.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Security.Claims; namespace Microsoft.AspNetCore.Http.Features.Authentication @@ -13,6 +14,7 @@ public ClaimsPrincipal User set; } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] public IAuthenticationHandler Handler { get; diff --git a/src/Microsoft.AspNetCore.Owin/OwinFeatureCollection.cs b/src/Microsoft.AspNetCore.Owin/OwinFeatureCollection.cs index 4838b99f..4a71310c 100644 --- a/src/Microsoft.AspNetCore.Owin/OwinFeatureCollection.cs +++ b/src/Microsoft.AspNetCore.Owin/OwinFeatureCollection.cs @@ -279,6 +279,7 @@ ClaimsPrincipal IHttpAuthenticationFeature.User } } + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470")] IAuthenticationHandler IHttpAuthenticationFeature.Handler { get; set; } /// diff --git a/test/Microsoft.AspNetCore.Authentication.Core.Test/Microsoft.AspNetCore.Authentication.Core.Test.csproj b/test/Microsoft.AspNetCore.Authentication.Core.Test/Microsoft.AspNetCore.Authentication.Core.Test.csproj new file mode 100644 index 00000000..925f5aa0 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Core.Test/Microsoft.AspNetCore.Authentication.Core.Test.csproj @@ -0,0 +1,19 @@ + + + + + netcoreapp2.0;net46 + netcoreapp2.0 + + + + + + + + + + + + + diff --git a/test/Microsoft.AspNetCore.Authentication.Core.Test/TokenExtensionTests.cs b/test/Microsoft.AspNetCore.Authentication.Core.Test/TokenExtensionTests.cs new file mode 100644 index 00000000..3e3eb9c5 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Core.Test/TokenExtensionTests.cs @@ -0,0 +1,123 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Microsoft.AspNetCore.Authentication +{ + public class TokenExtensionTests + { + [Fact] + public void CanStoreMultipleTokens() + { + var props = new AuthenticationProperties(); + var tokens = new List(); + var tok1 = new AuthenticationToken { Name = "One", Value = "1" }; + var tok2 = new AuthenticationToken { Name = "Two", Value = "2" }; + var tok3 = new AuthenticationToken { Name = "Three", Value = "3" }; + tokens.Add(tok1); + tokens.Add(tok2); + tokens.Add(tok3); + props.StoreTokens(tokens); + + Assert.Equal("1", props.GetTokenValue("One")); + Assert.Equal("2", props.GetTokenValue("Two")); + Assert.Equal("3", props.GetTokenValue("Three")); + Assert.Equal(3, props.GetTokens().Count()); + } + + [Fact] + public void SubsequentStoreTokenDeletesPreviousTokens() + { + var props = new AuthenticationProperties(); + var tokens = new List(); + var tok1 = new AuthenticationToken { Name = "One", Value = "1" }; + var tok2 = new AuthenticationToken { Name = "Two", Value = "2" }; + var tok3 = new AuthenticationToken { Name = "Three", Value = "3" }; + tokens.Add(tok1); + tokens.Add(tok2); + tokens.Add(tok3); + + props.StoreTokens(tokens); + + props.StoreTokens(new[] { new AuthenticationToken { Name = "Zero", Value = "0" } }); + + Assert.Equal("0", props.GetTokenValue("Zero")); + Assert.Equal(null, props.GetTokenValue("One")); + Assert.Equal(null, props.GetTokenValue("Two")); + Assert.Equal(null, props.GetTokenValue("Three")); + Assert.Equal(1, props.GetTokens().Count()); + } + + [Fact] + public void CanUpdateTokens() + { + var props = new AuthenticationProperties(); + var tokens = new List(); + var tok1 = new AuthenticationToken { Name = "One", Value = "1" }; + var tok2 = new AuthenticationToken { Name = "Two", Value = "2" }; + var tok3 = new AuthenticationToken { Name = "Three", Value = "3" }; + tokens.Add(tok1); + tokens.Add(tok2); + tokens.Add(tok3); + props.StoreTokens(tokens); + + tok1.Value = ".1"; + tok2.Value = ".2"; + tok3.Value = ".3"; + props.StoreTokens(tokens); + + Assert.Equal(".1", props.GetTokenValue("One")); + Assert.Equal(".2", props.GetTokenValue("Two")); + Assert.Equal(".3", props.GetTokenValue("Three")); + Assert.Equal(3, props.GetTokens().Count()); + } + + [Fact] + public void CanUpdateTokenValues() + { + var props = new AuthenticationProperties(); + var tokens = new List(); + var tok1 = new AuthenticationToken { Name = "One", Value = "1" }; + var tok2 = new AuthenticationToken { Name = "Two", Value = "2" }; + var tok3 = new AuthenticationToken { Name = "Three", Value = "3" }; + tokens.Add(tok1); + tokens.Add(tok2); + tokens.Add(tok3); + props.StoreTokens(tokens); + + Assert.True(props.UpdateTokenValue("One", ".11")); + Assert.True(props.UpdateTokenValue("Two", ".22")); + Assert.True(props.UpdateTokenValue("Three", ".33")); + + Assert.Equal(".11", props.GetTokenValue("One")); + Assert.Equal(".22", props.GetTokenValue("Two")); + Assert.Equal(".33", props.GetTokenValue("Three")); + Assert.Equal(3, props.GetTokens().Count()); + } + + [Fact] + public void UpdateTokenValueReturnsFalseForUnknownToken() + { + var props = new AuthenticationProperties(); + var tokens = new List(); + var tok1 = new AuthenticationToken { Name = "One", Value = "1" }; + var tok2 = new AuthenticationToken { Name = "Two", Value = "2" }; + var tok3 = new AuthenticationToken { Name = "Three", Value = "3" }; + tokens.Add(tok1); + tokens.Add(tok2); + tokens.Add(tok3); + props.StoreTokens(tokens); + + Assert.False(props.UpdateTokenValue("ONE", ".11")); + Assert.False(props.UpdateTokenValue("Jigglypuff", ".11")); + + Assert.Null(props.GetTokenValue("ONE")); + Assert.Null(props.GetTokenValue("Jigglypuff")); + Assert.Equal(3, props.GetTokens().Count()); + + } + } +} diff --git a/test/Microsoft.AspNetCore.Http.Features.Tests/Authentication/AuthenticateContextTest.cs b/test/Microsoft.AspNetCore.Http.Features.Tests/Authentication/AuthenticateContextTest.cs index c4d90132..1fae5b0a 100644 --- a/test/Microsoft.AspNetCore.Http.Features.Tests/Authentication/AuthenticateContextTest.cs +++ b/test/Microsoft.AspNetCore.Http.Features.Tests/Authentication/AuthenticateContextTest.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Xunit; +#pragma warning disable 618 namespace Microsoft.AspNetCore.Http.Features.Authentication { public class AuthenticateContextTest @@ -160,3 +161,4 @@ public void AuthenticateContext_NotAuthenticated_SetsUnusedPropertiesToDefault_F } } } +#pragma warning restore 618 diff --git a/test/Microsoft.AspNetCore.Http.Tests/Authentication/DefaultAuthenticationManagerTests.cs b/test/Microsoft.AspNetCore.Http.Tests/Authentication/DefaultAuthenticationManagerTests.cs index 85968a94..73c0c931 100644 --- a/test/Microsoft.AspNetCore.Http.Tests/Authentication/DefaultAuthenticationManagerTests.cs +++ b/test/Microsoft.AspNetCore.Http.Tests/Authentication/DefaultAuthenticationManagerTests.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Http.Features.Authentication; using Xunit; +#pragma warning disable 618 namespace Microsoft.AspNetCore.Http.Authentication.Internal { public class DefaultAuthenticationManagerTests @@ -100,3 +101,4 @@ private HttpContext CreateContext() } } } +#pragma warning restore 618 diff --git a/test/Microsoft.AspNetCore.Http.Tests/DefaultHttpContextTests.cs b/test/Microsoft.AspNetCore.Http.Tests/DefaultHttpContextTests.cs index 13b9ee65..7766210e 100644 --- a/test/Microsoft.AspNetCore.Http.Tests/DefaultHttpContextTests.cs +++ b/test/Microsoft.AspNetCore.Http.Tests/DefaultHttpContextTests.cs @@ -191,7 +191,9 @@ void TestAllCachedFeaturesAreNull(HttpContext context, IFeatureCollection featur TestCachedFeaturesAreNull(context, features); TestCachedFeaturesAreNull(context.Request, features); TestCachedFeaturesAreNull(context.Response, features); +#pragma warning disable 618 TestCachedFeaturesAreNull(context.Authentication, features); +#pragma warning restore 618 TestCachedFeaturesAreNull(context.Connection, features); TestCachedFeaturesAreNull(context.WebSockets, features); } @@ -220,7 +222,9 @@ void TestAllCachedFeaturesAreSet(HttpContext context, IFeatureCollection feature TestCachedFeaturesAreSet(context, features); TestCachedFeaturesAreSet(context.Request, features); TestCachedFeaturesAreSet(context.Response, features); +#pragma warning disable 618 TestCachedFeaturesAreSet(context.Authentication, features); +#pragma warning restore 618 TestCachedFeaturesAreSet(context.Connection, features); TestCachedFeaturesAreSet(context.WebSockets, features); }