Initial Auth 2.0 checkin

This commit is contained in:
Hao Kung 2017-03-31 11:56:20 -07:00
parent a0bb843dd5
commit 13925be91e
34 changed files with 2070 additions and 0 deletions

View File

@ -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

View File

@ -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
{
/// <summary>
/// Base class used by <see cref="IAuthenticationHandler"/> methods.
/// </summary>
public class AuthenticateContext : BaseAuthenticationContext
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="authenticationScheme">The name of the authentication scheme.</param>
public AuthenticateContext(HttpContext context, string authenticationScheme) : base(context, authenticationScheme, properties: null)
{ }
}
}

View File

@ -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
{
/// <summary>
/// Contains the result of an Authenticate call
/// </summary>
public class AuthenticateResult
{
private AuthenticateResult() { }
/// <summary>
/// If a ticket was produced, authenticate was successful.
/// </summary>
public bool Succeeded => Ticket != null;
/// <summary>
/// The authentication ticket.
/// </summary>
public AuthenticationTicket Ticket { get; private set; }
/// <summary>
/// Gets the claims-principal with authenticated user identities.
/// </summary>
public ClaimsPrincipal Principal => Ticket?.Principal;
/// <summary>
/// Additional state values for the authentication session.
/// </summary>
public AuthenticationProperties Properties => Ticket?.Properties;
/// <summary>
/// Holds failure information from the authentication.
/// </summary>
public Exception Failure { get; private set; }
/// <summary>
/// Indicates that stage of authentication was directly handled by user intervention and no
/// further processing should be attempted.
/// </summary>
public bool Handled { get; private set; }
/// <summary>
/// Indicates that there was no information returned for this authentication scheme.
/// </summary>
public bool Nothing { get; private set; }
/// <summary>
/// Indicates that authentication was successful.
/// </summary>
/// <param name="ticket">The ticket representing the authentication result.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Success(AuthenticationTicket ticket)
{
if (ticket == null)
{
throw new ArgumentNullException(nameof(ticket));
}
return new AuthenticateResult() { Ticket = ticket };
}
/// <summary>
/// Indicates that stage of authentication was directly handled by user intervention and no
/// further processing should be attempted.
/// </summary>
/// <returns>The result.</returns>
public static AuthenticateResult Handle()
{
return new AuthenticateResult() { Handled = true };
}
/// <summary>
/// Indicates that there was no information returned for this authentication scheme.
/// </summary>
/// <returns>The result.</returns>
public static AuthenticateResult None()
{
return new AuthenticateResult() { Nothing = true };
}
/// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failure">The failure exception.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Fail(Exception failure)
{
return new AuthenticateResult() { Failure = failure };
}
/// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failureMessage">The failure message.</param>
/// <returns>The result.</returns>
public static AuthenticateResult Fail(string failureMessage)
{
return new AuthenticateResult() { Failure = new Exception(failureMessage) };
}
}
}

View File

@ -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
{
/// <summary>
/// Extension methods to expose Authentication on HttpContext.
/// </summary>
public static class AuthenticationHttpContextExtensions
{
/// <summary>
/// Extension method for authenticate using the <see cref="AuthenticationOptions.DefaultAuthenticationScheme"/> scheme.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <returns>The <see cref="AuthenticateResult"/>.</returns>
public static Task<AuthenticateResult> AuthenticateAsync(this HttpContext context) =>
context.AuthenticateAsync(scheme: null);
/// <summary>
/// Extension method for authenticate.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <returns>The <see cref="AuthenticateResult"/>.</returns>
public static Task<AuthenticateResult> AuthenticateAsync(this HttpContext context, string scheme) =>
context.RequestServices.GetRequiredService<IAuthenticationService>().AuthenticateAsync(context, scheme);
/// <summary>
/// Extension method for Challenge.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <returns>The result.</returns>
public static Task ChallengeAsync(this HttpContext context, string scheme) =>
context.ChallengeAsync(scheme, properties: null);
/// <summary>
/// Extension method for authenticate using the <see cref="AuthenticationOptions.DefaultChallengeScheme"/> scheme.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <returns>The task.</returns>
public static Task ChallengeAsync(this HttpContext context) =>
context.ChallengeAsync(scheme: null, properties: null);
/// <summary>
/// Extension method for Challenge.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param>
/// <returns>The task.</returns>
public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties) =>
context.ChallengeAsync(scheme, properties: properties, behavior: ChallengeBehavior.Automatic);
/// <summary>
/// Extension method for Challenge.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param>
/// <param name="behavior">The <see cref="ChallengeBehavior"/> behavior.</param>
/// <returns>The task.</returns>
public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties, ChallengeBehavior behavior) =>
context.RequestServices.GetRequiredService<IAuthenticationService>().ChallengeAsync(context, scheme, properties, behavior);
/// <summary>
/// Extension method for Forbid.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <returns>The task.</returns>
public static Task ForbidAsync(this HttpContext context, string scheme) =>
context.ForbidAsync(scheme, properties: null);
/// <summary>
/// Extension method for Forbid.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param>
/// <returns>The task.</returns>
public static Task ForbidAsync(this HttpContext context, string scheme, AuthenticationProperties properties) =>
context.RequestServices.GetRequiredService<IAuthenticationService>().ChallengeAsync(context, scheme, properties, ChallengeBehavior.Forbidden);
/// <summary>
/// Extension method for SignIn.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="principal">The user.</param>
/// <returns>The task.</returns>
public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal) =>
context.SignInAsync(scheme, principal, properties: null);
/// <summary>
/// Extension method for SignIn using the <see cref="AuthenticationOptions.DefaultSignInScheme"/>.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="principal">The user.</param>
/// <returns>The task.</returns>
public static Task SignInAsync(this HttpContext context, ClaimsPrincipal principal) =>
context.SignInAsync(scheme: null, principal: principal, properties: null);
/// <summary>
/// Extension method for SignIn using the <see cref="AuthenticationOptions.DefaultSignInScheme"/>.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="principal">The user.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param>
/// <returns>The task.</returns>
public static Task SignInAsync(this HttpContext context, ClaimsPrincipal principal, AuthenticationProperties properties) =>
context.SignInAsync(scheme: null, principal: principal, properties: properties);
/// <summary>
/// Extension method for SignIn.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="principal">The user.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param>
/// <returns>The task.</returns>
public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) =>
context.RequestServices.GetRequiredService<IAuthenticationService>().SignInAsync(context, scheme, principal, properties);
/// <summary>
/// Extension method for SignOut.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <returns>The task.</returns>
public static Task SignOutAsync(this HttpContext context, string scheme) => context.SignOutAsync(scheme, properties: null);
/// <summary>
/// Extension method for SignOut.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param>
/// <returns></returns>
public static Task SignOutAsync(this HttpContext context, string scheme, AuthenticationProperties properties) =>
context.RequestServices.GetRequiredService<IAuthenticationService>().SignOutAsync(context, scheme, properties);
/// <summary>
/// Extension method for getting the value of an authentication token.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="tokenName">The name of the token.</param>
/// <returns>The value of the token.</returns>
public static Task<string> GetTokenAsync(this HttpContext context, string scheme, string tokenName) =>
context.RequestServices.GetRequiredService<IAuthenticationService>().GetTokenAsync(context, scheme, tokenName);
}
}

View File

@ -0,0 +1,65 @@
// 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 Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication
{
public class AuthenticationOptions
{
private readonly IList<AuthenticationSchemeBuilder> _schemes = new List<AuthenticationSchemeBuilder>();
/// <summary>
/// Returns the schemes in the order they were added (important for request handling priority)
/// </summary>
public IEnumerable<AuthenticationSchemeBuilder> Schemes => _schemes;
/// <summary>
/// Maps schemes by name.
/// </summary>
public IDictionary<string, AuthenticationSchemeBuilder> SchemeMap { get; } = new Dictionary<string, AuthenticationSchemeBuilder>(StringComparer.Ordinal);
/// <summary>
/// Adds an <see cref="AuthenticationScheme"/>.
/// </summary>
/// <param name="name">The name of the scheme being added.</param>
/// <param name="configureBuilder">Configures the scheme.</param>
public void AddScheme(string name, Action<AuthenticationSchemeBuilder> 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;
}
/// <summary>
/// Used by as the default scheme by <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>.
/// </summary>
public string DefaultAuthenticationScheme { get; set; }
/// <summary>
/// Used by as the default scheme by <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>.
/// </summary>
public string DefaultSignInScheme { get; set; }
/// <summary>
/// Used by as the default scheme by <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties, ChallengeBehavior)"/>.
/// </summary>
public string DefaultChallengeScheme { get; set; }
}
}

View File

@ -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
{
/// <summary>
/// Dictionary used to store state values about the authentication session.
/// </summary>
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";
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationProperties"/> class
/// </summary>
public AuthenticationProperties()
: this(items: null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationProperties"/> class
/// </summary>
/// <param name="items"></param>
public AuthenticationProperties(IDictionary<string, string> items)
{
Items = items ?? new Dictionary<string, string>(StringComparer.Ordinal);
}
/// <summary>
/// State values about the authentication session.
/// </summary>
public IDictionary<string, string> Items { get; }
/// <summary>
/// Gets or sets whether the authentication session is persisted across multiple requests.
/// </summary>
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);
}
}
}
}
/// <summary>
/// Gets or sets the full path or absolute URI to be used as an http redirect response value.
/// </summary>
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);
}
}
}
}
/// <summary>
/// Gets or sets the time at which the authentication ticket was issued.
/// </summary>
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);
}
}
}
}
/// <summary>
/// Gets or sets the time at which the authentication ticket expires.
/// </summary>
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);
}
}
}
}
/// <summary>
/// Gets or sets if refreshing the authentication session should be allowed.
/// </summary>
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);
}
}
}
}
}
}

View File

@ -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 System.Reflection;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// AuthenticationSchemes assign a name to a specific <see cref="IAuthenticationHandler"/>
/// handlerType.
/// </summary>
public class AuthenticationScheme
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="name">The name for the authentication scheme.</param>
/// <param name="handlerType">The <see cref="IAuthenticationHandler"/> type that handles this scheme.</param>
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;
}
/// <summary>
/// The name of the authentication scheme.
/// </summary>
public string Name { get; }
/// <summary>
/// The <see cref="IAuthenticationHandler"/> type that handles this scheme.
/// </summary>
public Type HandlerType { get; }
}
}

View File

@ -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
{
/// <summary>
/// Used to build <see cref="AuthenticationScheme"/>s.
/// </summary>
public class AuthenticationSchemeBuilder
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="name">The name of the scheme being built.</param>
public AuthenticationSchemeBuilder(string name)
{
Name = name;
}
/// <summary>
/// The name of the scheme being built.
/// </summary>
public string Name { get; }
/// <summary>
/// The <see cref="IAuthenticationHandler"/> type responsible for this scheme.
/// </summary>
public Type HandlerType { get; set; }
/// <summary>
/// Builds the <see cref="AuthenticationScheme"/> instance.
/// </summary>
/// <returns></returns>
public AuthenticationScheme Build() => new AuthenticationScheme(Name, HandlerType);
}
}

View File

@ -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
{
/// <summary>
/// Contains user identity information as well as additional authentication state.
/// </summary>
public class AuthenticationTicket
{
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationTicket"/> class
/// </summary>
/// <param name="principal">the <see cref="ClaimsPrincipal"/> that represents the authenticated user.</param>
/// <param name="properties">additional properties that can be consumed by the user or runtime.</param>
/// <param name="authenticationScheme">the authentication middleware that was responsible for this ticket.</param>
public AuthenticationTicket(ClaimsPrincipal principal, AuthenticationProperties properties, string authenticationScheme)
{
if (principal == null)
{
throw new ArgumentNullException(nameof(principal));
}
AuthenticationScheme = authenticationScheme;
Principal = principal;
Properties = properties ?? new AuthenticationProperties();
}
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationTicket"/> class
/// </summary>
/// <param name="principal">the <see cref="ClaimsPrincipal"/> that represents the authenticated user.</param>
/// <param name="authenticationScheme">the authentication middleware that was responsible for this ticket.</param>
public AuthenticationTicket(ClaimsPrincipal principal, string authenticationScheme)
: this(principal, properties: null, authenticationScheme: authenticationScheme)
{ }
/// <summary>
/// Gets the authentication type.
/// </summary>
public string AuthenticationScheme { get; private set; }
/// <summary>
/// Gets the claims-principal with authenticated user identities.
/// </summary>
public ClaimsPrincipal Principal { get; private set; }
/// <summary>
/// Additional state values for the authentication session.
/// </summary>
public AuthenticationProperties Properties { get; private set; }
}
}

View File

@ -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
{
/// <summary>
/// Name/Value representing an token.
/// </summary>
public class AuthenticationToken
{
/// <summary>
/// Name.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Value.
/// </summary>
public string Value { get; set; }
}
}

View File

@ -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
{
/// <summary>
/// Base context for authentication.
/// </summary>
public abstract class BaseAuthenticationContext : BaseContext
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="authenticationScheme">The name of the scheme.</param>
/// <param name="properties">The properties.</param>
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();
}
/// <summary>
/// The name of the scheme.
/// </summary>
public string AuthenticationScheme { get; }
/// <summary>
/// Contains the extra meta-data arriving with the authentication. May be altered.
/// </summary>
public AuthenticationProperties Properties { get; protected set; }
}
}

View File

@ -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
{
/// <summary>
/// Base class used by other context classes.
/// </summary>
public abstract class BaseContext
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The request context.</param>
protected BaseContext(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
HttpContext = context;
}
/// <summary>
/// The context.
/// </summary>
public HttpContext HttpContext { get; }
/// <summary>
/// The request.
/// </summary>
public HttpRequest Request
{
get { return HttpContext.Request; }
}
/// <summary>
/// The response.
/// </summary>
public HttpResponse Response
{
get { return HttpContext.Response; }
}
}
}

View File

@ -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
{
/// <summary>
/// Controls how challenge will behave (i.e. 401 vs 403).
/// </summary>
public enum ChallengeBehavior
{
Automatic,
Unauthorized,
Forbidden
}
}

View File

@ -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
{
/// <summary>
/// Context used for challenges.
/// </summary>
public class ChallengeContext : BaseAuthenticationContext
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="httpContext">The context.</param>
/// <param name="authenticationScheme">The name of the scheme.</param>
public ChallengeContext(HttpContext httpContext, string authenticationScheme)
: this(httpContext, authenticationScheme, properties: null, behavior: ChallengeBehavior.Automatic)
{ }
/// <summary>
/// Constructor
/// </summary>
/// <param name="httpContext">The context.</param>
/// <param name="authenticationScheme">The name of the scheme.</param>
/// <param name="properties">The properties.</param>
/// <param name="behavior">The challenge behavior.</param>
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;
}
/// <summary>
/// The challenge behavior.
/// </summary>
public ChallengeBehavior Behavior { get; }
}
}

View File

@ -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
{
/// <summary>
/// Used to capture path info so redirects can be computed properly within an app.Map().
/// </summary>
public interface IAuthenticationFeature
{
/// <summary>
/// The original path base.
/// </summary>
PathString OriginalPathBase { get; set; }
/// <summary>
/// The original path.
/// </summary>
PathString OriginalPath { get; set; }
}
}

View File

@ -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
{
/// <summary>
/// Created per request to handle authentication for to a particular scheme.
/// </summary>
public interface IAuthenticationHandler
{
/// <summary>
/// The handler should initialize anything it needs from the request and scheme here.
/// </summary>
/// <param name="scheme">The <see cref="AuthenticationScheme"/> scheme.</param>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <returns></returns>
Task InitializeAsync(AuthenticationScheme scheme, HttpContext context);
/// <summary>
/// Authentication behavior.
/// </summary>
/// <param name="context">The <see cref="AuthenticateContext"/> context.</param>
/// <returns>The <see cref="AuthenticateResult"/> result.</returns>
Task<AuthenticateResult> AuthenticateAsync(AuthenticateContext context);
/// <summary>
/// Challenge behavior.
/// </summary>
/// <param name="context">The <see cref="ChallengeContext"/> context.</param>
/// <returns>A task.</returns>
Task ChallengeAsync(ChallengeContext context);
/// <summary>
/// Handle sign in.
/// </summary>
/// <param name="context">The <see cref="SignInContext"/> context.</param>
/// <returns>A task.</returns>
Task SignInAsync(SignInContext context);
/// <summary>
/// Signout behavior.
/// </summary>
/// <param name="context">The <see cref="SignOutContext"/> context.</param>
/// <returns>A task.</returns>
Task SignOutAsync(SignOutContext context);
}
}

View File

@ -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
{
/// <summary>
/// Provides the appropriate IAuthenticationHandler instance for the authenticationScheme and request.
/// </summary>
public interface IAuthenticationHandlerProvider
{
/// <summary>
/// Returns the handler instance that will be used.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="authenticationScheme">The name of the authentication scheme being handled.</param>
/// <returns>The handler instance.</returns>
Task<IAuthenticationHandler> GetHandlerAsync(HttpContext context, string authenticationScheme);
}
}

View File

@ -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
{
/// <summary>
/// Used to determine if a handler wants to participate in request processing.
/// </summary>
public interface IAuthenticationRequestHandler : IAuthenticationHandler
{
/// <summary>
/// Returns true if request processing should stop.
/// </summary>
/// <returns></returns>
Task<bool> HandleRequestAsync();
}
}

View File

@ -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
{
/// <summary>
/// Responsible for managing what authenticationSchemes are supported.
/// </summary>
public interface IAuthenticationSchemeProvider
{
/// <summary>
/// Returns all currently registered <see cref="AuthenticationScheme"/>s.
/// </summary>
/// <returns>All currently registered <see cref="AuthenticationScheme"/>s.</returns>
Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync();
/// <summary>
/// Returns the <see cref="AuthenticationScheme"/> matching the name, or null.
/// </summary>
/// <param name="name">The name of the authenticationScheme.</param>
/// <returns>The scheme or null if not found.</returns>
Task<AuthenticationScheme> GetSchemeAsync(string name);
/// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultAuthenticationScheme"/>.
/// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned.
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>.</returns>
Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync();
/// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties, ChallengeBehavior)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultChallengeScheme"/>.
/// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned.
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties, ChallengeBehavior)"/>.</returns>
Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync();
/// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultSignInScheme"/>.
/// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned.
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>.</returns>
Task<AuthenticationScheme> GetDefaultSignInSchemeAsync();
/// <summary>
/// Registers a scheme for use by <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="scheme">The scheme.</param>
void AddScheme(AuthenticationScheme scheme);
/// <summary>
/// Removes a scheme, preventing it from being used by <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="name">The name of the authenticationScheme being removed.</param>
void RemoveScheme(string name);
/// <summary>
/// Returns the schemes in priority order for request handling.
/// </summary>
/// <returns>The schemes in priority order for request handling</returns>
Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync();
}
}

View File

@ -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
{
/// <summary>
/// Used to provide authentication.
/// </summary>
public interface IAuthenticationService
{
/// <summary>
/// Authenticate for the specified authentication scheme.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <returns>The result.</returns>
Task<AuthenticateResult> AuthenticateAsync(HttpContext context, string scheme);
/// <summary>
/// Challenge the specified authentication scheme.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <param name="behavior">The <see cref="ChallengeBehavior"/>.</param>
/// <returns>A task.</returns>
Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties, ChallengeBehavior behavior);
/// <summary>
/// Sign a principal in for the specified authentication scheme.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="principal">The <see cref="ClaimsPrincipal"/> to sign in.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <returns>A task.</returns>
Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties);
/// <summary>
/// Sign out the specified authentication scheme.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <returns>A task.</returns>
Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties);
}
}

View File

@ -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
{
/// <summary>
/// Used by the <see cref="IAuthenticationService"/> for claims transformation.
/// </summary>
public interface IClaimsTransformation
{
/// <summary>
/// Provides a central transformation point to change the specified principal.
/// </summary>
/// <param name="principal">The <see cref="ClaimsPrincipal"/> to transform.</param>
/// <returns>The transformed principal.</returns>
Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal);
}
}

View File

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<Description>ASP.NET Core common types used by the various authentication components.</Description>
<TargetFrameworks>netstandard1.3;net46</TargetFrameworks>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;authentication;security</PackageTags>
<EnableApiCheck>false</EnableApiCheck>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Http.Abstractions\Microsoft.AspNetCore.Http.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Options" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.TaskCache.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View File

@ -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
{
/// <summary>
/// Context used for sign out.
/// </summary>
public class SignInContext : BaseAuthenticationContext
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="authenticationScheme">The name of the authentication scheme.</param>
/// <param name="principal">The user to sign in.</param>
/// <param name="properties">The properties.</param>
public SignInContext(HttpContext context, string authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties properties)
: base(context, authenticationScheme, properties)
{
if (principal == null)
{
throw new ArgumentNullException(nameof(principal));
}
Principal = principal;
}
/// <summary>
/// The user to sign in.
/// </summary>
public ClaimsPrincipal Principal { get; }
}
}

View File

@ -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
{
/// <summary>
/// Context used to sign out.
/// </summary>
public class SignOutContext : BaseAuthenticationContext
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="authenticationScheme">The name of the authentication scheme.</param>
/// <param name="properties">The properties.</param>
public SignOutContext(HttpContext context, string authenticationScheme, AuthenticationProperties properties)
: base(context, authenticationScheme, properties)
{ }
}
}

View File

@ -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
{
/// <summary>
/// Extension methods for storing authentication tokens in <see cref="AuthenticationProperties"/>.
/// </summary>
public static class AuthenticationTokenExtensions
{
private static string TokenNamesKey = ".TokenNames";
private static string TokenKeyPrefix = ".Token.";
/// <summary>
/// Stores a set of authentication tokens, after removing any old tokens.
/// </summary>
/// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param>
/// <param name="tokens">The tokens to store.</param>
public static void StoreTokens(this AuthenticationProperties properties, IEnumerable<AuthenticationToken> 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<string>();
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());
}
}
/// <summary>
/// Returns the value of a token.
/// </summary>
/// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param>
/// <param name="tokenName">The token name.</param>
/// <returns>The token value.</returns>
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;
}
/// <summary>
/// Returns all of the AuthenticationTokens contained in the properties.
/// </summary>
/// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param>
/// <returns>The authentication toekns.</returns>
public static IEnumerable<AuthenticationToken> GetTokens(this AuthenticationProperties properties)
{
if (properties == null)
{
throw new ArgumentNullException(nameof(properties));
}
var tokens = new List<AuthenticationToken>();
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;
}
/// <summary>
/// Extension method for getting the value of an authentication token.
/// </summary>
/// <param name="auth">The <see cref="IAuthenticationService"/>.</param>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="tokenName">The name of the token.</param>
/// <returns>The value of the token.</returns>
public static async Task<string> 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);
}
}
}

View File

@ -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
{
/// <summary>
/// Extension methods for setting up authentication services in an <see cref="IServiceCollection" />.
/// </summary>
public static class AuthenticationCoreServiceCollectionExtensions
{
/// <summary>
/// Add core authentication services needed for <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddAuthenticationCore(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.TryAddScoped<IAuthenticationService, AuthenticationService>();
services.TryAddSingleton<IClaimsTransformation, NoopClaimsTransformation>(); // Can be replaced with scoped ones that use DbContext
services.TryAddScoped<IAuthenticationHandlerProvider, AuthenticationHandlerProvider>();
services.TryAddSingleton<IAuthenticationSchemeProvider, AuthenticationSchemeProvider>();
return services;
}
/// <summary>
/// Add core authentication services needed for <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
/// <param name="configureOptions">Used to configure the <see cref="AuthenticationOptions"/>.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddAuthenticationCore(this IServiceCollection services, Action<AuthenticationOptions> configureOptions) {
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (configureOptions == null)
{
throw new ArgumentNullException(nameof(configureOptions));
}
services.AddAuthenticationCore();
services.Configure(configureOptions);
return services;
}
}
}

View File

@ -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
{
/// <summary>
/// Used to capture path info so redirects can be computed properly within an app.Map().
/// </summary>
public class AuthenticationFeature : IAuthenticationFeature
{
/// <summary>
/// The original path base.
/// </summary>
public PathString OriginalPathBase { get; set; }
/// <summary>
/// The original path.
/// </summary>
public PathString OriginalPath { get; set; }
}
}

View File

@ -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
{
/// <summary>
/// Implementation of <see cref="IAuthenticationHandlerProvider"/>.
/// </summary>
public class AuthenticationHandlerProvider : IAuthenticationHandlerProvider
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="schemes">The <see cref="IAuthenticationHandlerProvider"/>.</param>
public AuthenticationHandlerProvider(IAuthenticationSchemeProvider schemes)
{
Schemes = schemes;
}
/// <summary>
/// The <see cref="IAuthenticationHandlerProvider"/>.
/// </summary>
public IAuthenticationSchemeProvider Schemes { get; }
// handler instance cache, need to initialize once per request
private Dictionary<string, IAuthenticationHandler> _handlerMap = new Dictionary<string, IAuthenticationHandler>(StringComparer.Ordinal);
/// <summary>
/// Returns the handler instance that will be used.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="authenticationScheme">The name of the authentication scheme being handled.</param>
/// <returns>The handler instance.</returns>
public async Task<IAuthenticationHandler> 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;
}
}
}

View File

@ -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
{
/// <summary>
/// Implements <see cref="IAuthenticationSchemeProvider"/>.
/// </summary>
public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="options">The <see cref="AuthenticationOptions"/> options.</param>
public AuthenticationSchemeProvider(IOptions<AuthenticationOptions> 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<string, AuthenticationScheme> _map = new Dictionary<string, AuthenticationScheme>(StringComparer.Ordinal);
private List<AuthenticationScheme> _requestHandlers = new List<AuthenticationScheme>();
/// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultAuthenticationScheme"/>.
/// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned.
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>.</returns>
public Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync()
{
if (_options.DefaultAuthenticationScheme != null)
{
return GetSchemeAsync(_options.DefaultAuthenticationScheme);
}
if (_map.Count == 1)
{
return Task.FromResult(_map.Values.First());
}
return Task.FromResult<AuthenticationScheme>(null);
}
/// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties, ChallengeBehavior)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultChallengeScheme"/>.
/// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned.
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties, ChallengeBehavior)"/>.</returns>
public Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync()
{
if (_options.DefaultChallengeScheme != null)
{
return GetSchemeAsync(_options.DefaultChallengeScheme);
}
if (_map.Count == 1)
{
return Task.FromResult(_map.Values.First());
}
return Task.FromResult<AuthenticationScheme>(null);
}
/// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultSignInScheme"/>.
/// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned.
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>.</returns>
public Task<AuthenticationScheme> GetDefaultSignInSchemeAsync()
{
if (_options.DefaultSignInScheme != null)
{
return GetSchemeAsync(_options.DefaultSignInScheme);
}
if (_map.Count == 1)
{
return Task.FromResult(_map.Values.First());
}
return Task.FromResult<AuthenticationScheme>(null);
}
/// <summary>
/// Returns the <see cref="AuthenticationScheme"/> matching the name, or null.
/// </summary>
/// <param name="name">The name of the authenticationScheme.</param>
/// <returns>The scheme or null if not found.</returns>
public Task<AuthenticationScheme> GetSchemeAsync(string name)
{
if (_map.ContainsKey(name))
{
return Task.FromResult(_map[name]);
}
return Task.FromResult<AuthenticationScheme>(null);
}
/// <summary>
/// Returns the schemes in priority order for request handling.
/// </summary>
/// <returns>The schemes in priority order for request handling</returns>
public Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync()
{
return Task.FromResult<IEnumerable<AuthenticationScheme>>(_requestHandlers);
}
/// <summary>
/// Registers a scheme for use by <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="scheme">The scheme.</param>
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;
}
}
/// <summary>
/// Removes a scheme, preventing it from being used by <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="name">The name of the authenticationScheme being removed.</param>
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<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync()
{
return Task.FromResult<IEnumerable<AuthenticationScheme>>(_map.Values);
}
}
}

View File

@ -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
{
/// <summary>
/// Implements <see cref="IAuthenticationService"/>.
/// </summary>
public class AuthenticationService : IAuthenticationService
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="schemes">The <see cref="IAuthenticationSchemeProvider"/>.</param>
/// <param name="handlers">The <see cref="IAuthenticationRequestHandler"/>.</param>
/// <param name="transform">The The <see cref="IClaimsTransformation"/>.</param>
public AuthenticationService(IAuthenticationSchemeProvider schemes, IAuthenticationHandlerProvider handlers, IClaimsTransformation transform)
{
Schemes = schemes;
Handlers = handlers;
Transform = transform;
}
/// <summary>
/// Used to lookup AuthenticationSchemes.
/// </summary>
public IAuthenticationSchemeProvider Schemes { get; }
/// <summary>
/// Used to resolve IAuthenticationHandler instances.
/// </summary>
public IAuthenticationHandlerProvider Handlers { get; }
/// <summary>
/// Used for claims transformation.
/// </summary>
public IClaimsTransformation Transform { get; }
/// <summary>
/// Authenticate for the specified authentication scheme.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <returns>The result.</returns>
public virtual async Task<AuthenticateResult> 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;
}
/// <summary>
/// Challenge the specified authentication scheme.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <param name="behavior">The <see cref="ChallengeBehavior"/>.</param>
/// <returns>A task.</returns>
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);
}
/// <summary>
/// Sign a principal in for the specified authentication scheme.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="principal">The <see cref="ClaimsPrincipal"/> to sign in.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <returns>A task.</returns>
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);
}
/// <summary>
/// Sign out the specified authentication scheme.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <returns>A task.</returns>
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);
}
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<Description>ASP.NET Core common types used by the various authentication middleware components.</Description>
<TargetFrameworks>netstandard1.3;net46</TargetFrameworks>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;authentication;security</PackageTags>
<EnableApiCheck>false</EnableApiCheck>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Authentication.Abstractions\Microsoft.AspNetCore.Authentication.Abstractions.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.Http\Microsoft.AspNetCore.Http.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.Http.Extensions\Microsoft.AspNetCore.Http.Extensions.csproj" />
<PackageReference Include="Microsoft.Extensions.TaskCache.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
</ItemGroup>
</Project>

View File

@ -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
{
/// <summary>
/// Default claims transformation is a no-op.
/// </summary>
public class NoopClaimsTransformation : IClaimsTransformation
{
/// <summary>
/// Returns the principal unchanged.
/// </summary>
/// <param name="principal">The user.</param>
/// <returns>The principal unchanged.</returns>
public virtual Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
return Task.FromResult(principal);
}
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Authentication.Core\Microsoft.AspNetCore.Authentication.Core.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
<PackageReference Include="xunit" Version="$(XunitVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>

View File

@ -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<AuthenticationToken>();
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<AuthenticationToken>();
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<AuthenticationToken>();
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<AuthenticationToken>();
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<AuthenticationToken>();
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());
}
}
}