diff --git a/Extensibility/Security/TokenAuthenticator/Client/Client.csproj b/Extensibility/Security/TokenAuthenticator/Client/Client.csproj
new file mode 100644
index 0000000..a39ce70
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Client/Client.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Extensibility/Security/TokenAuthenticator/Client/Connected Services/CoreWcf.Samples.TokenAuthenticator/ConnectedService.json b/Extensibility/Security/TokenAuthenticator/Client/Connected Services/CoreWcf.Samples.TokenAuthenticator/ConnectedService.json
new file mode 100644
index 0000000..bcedf81
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Client/Connected Services/CoreWcf.Samples.TokenAuthenticator/ConnectedService.json
@@ -0,0 +1,17 @@
+{
+ "ExtendedData": {
+ "inputs": [
+ "https://localhost:5001/CalculatorService?wsdl"
+ ],
+ "collectionTypes": [
+ "System.Array",
+ "System.Collections.Generic.Dictionary`2"
+ ],
+ "namespaceMappings": [
+ "*, CoreWcf.Samples.TokenAuthenticator"
+ ],
+ "sync": true,
+ "targetFramework": "net6.0",
+ "typeReuseMode": "All"
+ }
+}
\ No newline at end of file
diff --git a/Extensibility/Security/TokenAuthenticator/Client/Connected Services/CoreWcf.Samples.TokenAuthenticator/Reference.cs b/Extensibility/Security/TokenAuthenticator/Client/Connected Services/CoreWcf.Samples.TokenAuthenticator/Reference.cs
new file mode 100644
index 0000000..02278ff
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Client/Connected Services/CoreWcf.Samples.TokenAuthenticator/Reference.cs
@@ -0,0 +1,180 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace CoreWcf.Samples.TokenAuthenticator
+{
+
+
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.1.0")]
+ [System.ServiceModel.ServiceContractAttribute(Namespace="http://CoreWcf.Samples.TokenAuthenticator", ConfigurationName="CoreWcf.Samples.TokenAuthenticator.ICalculatorService")]
+ public interface ICalculatorService
+ {
+
+ [System.ServiceModel.OperationContractAttribute(Action="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/Add", ReplyAction="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/AddResponse")]
+ double Add(double n1, double n2);
+
+ [System.ServiceModel.OperationContractAttribute(Action="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/Add", ReplyAction="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/AddResponse")]
+ System.Threading.Tasks.Task AddAsync(double n1, double n2);
+
+ [System.ServiceModel.OperationContractAttribute(Action="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/Subtract", ReplyAction="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/SubtractResponse")]
+ double Subtract(double n1, double n2);
+
+ [System.ServiceModel.OperationContractAttribute(Action="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/Subtract", ReplyAction="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/SubtractResponse")]
+ System.Threading.Tasks.Task SubtractAsync(double n1, double n2);
+
+ [System.ServiceModel.OperationContractAttribute(Action="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/Multiply", ReplyAction="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/MultiplyResponse")]
+ double Multiply(double n1, double n2);
+
+ [System.ServiceModel.OperationContractAttribute(Action="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/Multiply", ReplyAction="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/MultiplyResponse")]
+ System.Threading.Tasks.Task MultiplyAsync(double n1, double n2);
+
+ [System.ServiceModel.OperationContractAttribute(Action="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/Divide", ReplyAction="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/DivideResponse")]
+ double Divide(double n1, double n2);
+
+ [System.ServiceModel.OperationContractAttribute(Action="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/Divide", ReplyAction="http://CoreWcf.Samples.TokenAuthenticator/ICalculatorService/DivideResponse")]
+ System.Threading.Tasks.Task DivideAsync(double n1, double n2);
+ }
+
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.1.0")]
+ public interface ICalculatorServiceChannel : CoreWcf.Samples.TokenAuthenticator.ICalculatorService, System.ServiceModel.IClientChannel
+ {
+ }
+
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.1.0")]
+ public partial class CalculatorServiceClient : System.ServiceModel.ClientBase, CoreWcf.Samples.TokenAuthenticator.ICalculatorService
+ {
+
+ ///
+ /// Implement this partial method to configure the service endpoint.
+ ///
+ /// The endpoint to configure
+ /// The client credentials
+ static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);
+
+ public CalculatorServiceClient() :
+ base(CalculatorServiceClient.GetDefaultBinding(), CalculatorServiceClient.GetDefaultEndpointAddress())
+ {
+ this.Endpoint.Name = EndpointConfiguration.WSHttpBinding_ICalculatorService.ToString();
+ ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
+ }
+
+ public CalculatorServiceClient(EndpointConfiguration endpointConfiguration) :
+ base(CalculatorServiceClient.GetBindingForEndpoint(endpointConfiguration), CalculatorServiceClient.GetEndpointAddress(endpointConfiguration))
+ {
+ this.Endpoint.Name = endpointConfiguration.ToString();
+ ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
+ }
+
+ public CalculatorServiceClient(EndpointConfiguration endpointConfiguration, string remoteAddress) :
+ base(CalculatorServiceClient.GetBindingForEndpoint(endpointConfiguration), new System.ServiceModel.EndpointAddress(remoteAddress))
+ {
+ this.Endpoint.Name = endpointConfiguration.ToString();
+ ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
+ }
+
+ public CalculatorServiceClient(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress) :
+ base(CalculatorServiceClient.GetBindingForEndpoint(endpointConfiguration), remoteAddress)
+ {
+ this.Endpoint.Name = endpointConfiguration.ToString();
+ ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
+ }
+
+ public CalculatorServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
+ base(binding, remoteAddress)
+ {
+ }
+
+ public double Add(double n1, double n2)
+ {
+ return base.Channel.Add(n1, n2);
+ }
+
+ public System.Threading.Tasks.Task AddAsync(double n1, double n2)
+ {
+ return base.Channel.AddAsync(n1, n2);
+ }
+
+ public double Subtract(double n1, double n2)
+ {
+ return base.Channel.Subtract(n1, n2);
+ }
+
+ public System.Threading.Tasks.Task SubtractAsync(double n1, double n2)
+ {
+ return base.Channel.SubtractAsync(n1, n2);
+ }
+
+ public double Multiply(double n1, double n2)
+ {
+ return base.Channel.Multiply(n1, n2);
+ }
+
+ public System.Threading.Tasks.Task MultiplyAsync(double n1, double n2)
+ {
+ return base.Channel.MultiplyAsync(n1, n2);
+ }
+
+ public double Divide(double n1, double n2)
+ {
+ return base.Channel.Divide(n1, n2);
+ }
+
+ public System.Threading.Tasks.Task DivideAsync(double n1, double n2)
+ {
+ return base.Channel.DivideAsync(n1, n2);
+ }
+
+ public virtual System.Threading.Tasks.Task OpenAsync()
+ {
+ return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginOpen(null, null), new System.Action(((System.ServiceModel.ICommunicationObject)(this)).EndOpen));
+ }
+
+ private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
+ {
+ if ((endpointConfiguration == EndpointConfiguration.WSHttpBinding_ICalculatorService))
+ {
+ System.ServiceModel.WSHttpBinding result = new System.ServiceModel.WSHttpBinding();
+ result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
+ result.MaxReceivedMessageSize = int.MaxValue;
+ result.AllowCookies = true;
+ result.Security.Mode = System.ServiceModel.SecurityMode.TransportWithMessageCredential;
+ result.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.None;
+ result.Security.Message.ClientCredentialType = System.ServiceModel.MessageCredentialType.UserName;
+ return result;
+ }
+ throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
+ }
+
+ private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
+ {
+ if ((endpointConfiguration == EndpointConfiguration.WSHttpBinding_ICalculatorService))
+ {
+ return new System.ServiceModel.EndpointAddress("https://localhost:5001/CalculatorService");
+ }
+ throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
+ }
+
+ private static System.ServiceModel.Channels.Binding GetDefaultBinding()
+ {
+ return CalculatorServiceClient.GetBindingForEndpoint(EndpointConfiguration.WSHttpBinding_ICalculatorService);
+ }
+
+ private static System.ServiceModel.EndpointAddress GetDefaultEndpointAddress()
+ {
+ return CalculatorServiceClient.GetEndpointAddress(EndpointConfiguration.WSHttpBinding_ICalculatorService);
+ }
+
+ public enum EndpointConfiguration
+ {
+
+ WSHttpBinding_ICalculatorService,
+ }
+ }
+}
diff --git a/Extensibility/Security/TokenAuthenticator/Client/Program.cs b/Extensibility/Security/TokenAuthenticator/Client/Program.cs
new file mode 100644
index 0000000..ada09df
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Client/Program.cs
@@ -0,0 +1,78 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//The service contract is defined using Connected Service "WCF Web Service", generated from the service by the dotnet svcutil tool.
+
+Console.WriteLine("Username authentication required.");
+Console.WriteLine(" Please enter a valid domain email address:");
+string username = Console.ReadLine();
+Console.WriteLine(" Enter password:");
+string password = "";
+ConsoleKeyInfo info = Console.ReadKey(true);
+while (info.Key != ConsoleKey.Enter)
+{
+ if (info.Key != ConsoleKey.Backspace)
+ {
+ password += info.KeyChar;
+ info = Console.ReadKey(true);
+ }
+ else if (info.Key == ConsoleKey.Backspace)
+ {
+ if (password != "")
+ {
+ password = password.Substring(0, password.Length - 1);
+
+ }
+ info = Console.ReadKey(true);
+ }
+}
+
+for (int i = 0; i < password.Length; i++)
+ Console.Write("*");
+
+Console.WriteLine();
+
+// Create a client with given client endpoint configuration
+CalculatorServiceClient client = new CalculatorServiceClient();
+
+client.ClientCredentials.UserName.UserName = username;
+client.ClientCredentials.UserName.Password = password;
+
+/*
+Setting the CertificateValidationMode to PeerOrChainTrust means that if the certificate
+is in the Trusted People store, then it will be trusted without performing a
+validation of the certificate's issuer chain. This setting is used here for convenience so that the
+sample can be run without having to have certificates issued by a certificate authority (CA).
+This setting is less secure than the default, ChainTrust. The security implications of this
+setting should be carefully considered before using PeerOrChainTrust in production code.
+*/
+client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.PeerOrChainTrust;
+// Call the Add service operation.
+double value1 = 100.00D;
+double value2 = 15.99D;
+double result = client.Add(value1, value2);
+Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
+
+// Call the Subtract service operation.
+value1 = 145.00D;
+value2 = 76.54D;
+result = client.Subtract(value1, value2);
+Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);
+
+// Call the Multiply service operation.
+value1 = 9.00D;
+value2 = 81.25D;
+result = client.Multiply(value1, value2);
+Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);
+
+// Call the Divide service operation.
+value1 = 22.00D;
+value2 = 7.00D;
+result = client.Divide(value1, value2);
+Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);
+
+client.Close();
+
+Console.WriteLine();
+Console.WriteLine("Press to terminate client.");
+Console.ReadLine();
diff --git a/Extensibility/Security/TokenAuthenticator/Extensibility.Security.TokenAuthenticator.sln b/Extensibility/Security/TokenAuthenticator/Extensibility.Security.TokenAuthenticator.sln
new file mode 100644
index 0000000..16cf586
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Extensibility.Security.TokenAuthenticator.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32422.2
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Service", "Service\Service.csproj", "{BF126326-3393-407C-B24A-8FCCC388BE27}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csproj", "{B533CADA-93BB-40E1-8FBA-FE37100062C3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BF126326-3393-407C-B24A-8FCCC388BE27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BF126326-3393-407C-B24A-8FCCC388BE27}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BF126326-3393-407C-B24A-8FCCC388BE27}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BF126326-3393-407C-B24A-8FCCC388BE27}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B533CADA-93BB-40E1-8FBA-FE37100062C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B533CADA-93BB-40E1-8FBA-FE37100062C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B533CADA-93BB-40E1-8FBA-FE37100062C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B533CADA-93BB-40E1-8FBA-FE37100062C3}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {AD996EFD-70DC-4431-B411-5A2771DD02D3}
+ EndGlobalSection
+EndGlobal
diff --git a/Extensibility/Security/TokenAuthenticator/Service/CalculatorService.cs b/Extensibility/Security/TokenAuthenticator/Service/CalculatorService.cs
new file mode 100644
index 0000000..42d3d76
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Service/CalculatorService.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace CoreWcf.Samples.TokenAuthenticator
+{
+ // Service class which implements the service contract interface.
+ // Added code to write output to the console window
+ public class CalculatorService : ICalculatorService
+ {
+ private static void DisplayIdentityInformation()
+ {
+ Console.WriteLine("\t\tSecurity context identity : {0}", ServiceSecurityContext.Current.PrimaryIdentity.Name);
+ }
+
+ public double Add(double n1, double n2)
+ {
+ DisplayIdentityInformation();
+ double result = n1 + n2;
+ Console.WriteLine("Received Add({0},{1})", n1, n2);
+ Console.WriteLine("Return: {0}", result);
+ return result;
+ }
+
+ public double Subtract(double n1, double n2)
+ {
+ DisplayIdentityInformation();
+ double result = n1 - n2;
+ Console.WriteLine("Received Subtract({0},{1})", n1, n2);
+ Console.WriteLine("Return: {0}", result);
+ return result;
+ }
+
+ public double Multiply(double n1, double n2)
+ {
+ DisplayIdentityInformation();
+ double result = n1 * n2;
+ Console.WriteLine("Received Multiply({0},{1})", n1, n2);
+ Console.WriteLine("Return: {0}", result);
+ return result;
+ }
+
+ public double Divide(double n1, double n2)
+ {
+ DisplayIdentityInformation();
+ double result = n1 / n2;
+ Console.WriteLine("Received Divide({0},{1})", n1, n2);
+ Console.WriteLine("Return: {0}", result);
+ return result;
+ }
+ }
+}
diff --git a/Extensibility/Security/TokenAuthenticator/Service/ICalculatorService.cs b/Extensibility/Security/TokenAuthenticator/Service/ICalculatorService.cs
new file mode 100644
index 0000000..40db849
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Service/ICalculatorService.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace CoreWcf.Samples.TokenAuthenticator
+{
+ // Define a service contract.
+ [ServiceContract(Namespace = "http://CoreWcf.Samples.TokenAuthenticator")]
+ public interface ICalculatorService
+ {
+ [OperationContract]
+ double Add(double n1, double n2);
+ [OperationContract]
+ double Subtract(double n1, double n2);
+ [OperationContract]
+ double Multiply(double n1, double n2);
+ [OperationContract]
+ double Divide(double n1, double n2);
+ }
+}
diff --git a/Extensibility/Security/TokenAuthenticator/Service/MySecurityTokenManager.cs b/Extensibility/Security/TokenAuthenticator/Service/MySecurityTokenManager.cs
new file mode 100644
index 0000000..4780a91
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Service/MySecurityTokenManager.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using CoreWCF.IdentityModel.Selectors;
+
+namespace CoreWcf.Samples.TokenAuthenticator
+{
+ public class MySecurityTokenManager : ServiceCredentialsSecurityTokenManager
+ {
+ private MyUserNameCredential myUserNameCredential;
+ private readonly string _userNameTokenType = "http://schemas.microsoft.com/ws/2006/05/identitymodel/tokens/UserName";
+
+ public MySecurityTokenManager(MyUserNameCredential myUserNameCredential)
+ : base(myUserNameCredential)
+ {
+ this.myUserNameCredential = myUserNameCredential;
+ }
+
+ public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
+ {
+ if (tokenRequirement.TokenType == _userNameTokenType)
+ {
+ outOfBandTokenResolver = null;
+ return new MyTokenAuthenticator();
+ }
+ else
+ {
+ return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
+ }
+ }
+ }
+}
+
diff --git a/Extensibility/Security/TokenAuthenticator/Service/MyTokenAuthenticator.cs b/Extensibility/Security/TokenAuthenticator/Service/MyTokenAuthenticator.cs
new file mode 100644
index 0000000..cc3e026
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Service/MyTokenAuthenticator.cs
@@ -0,0 +1,114 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.ObjectModel;
+using System.Security.Principal;
+using System.Text.RegularExpressions;
+using CoreWCF.IdentityModel.Claims;
+using CoreWCF.IdentityModel.Policy;
+using CoreWCF.IdentityModel.Selectors;
+using CoreWCF.IdentityModel.Tokens;
+
+namespace CoreWcf.Samples.TokenAuthenticator
+{
+ internal class MyTokenAuthenticator : UserNameSecurityTokenAuthenticator
+ {
+ private static bool IsRogueDomain(string domain)
+ {
+ return false;
+ }
+
+ private static bool IsEmail(string inputEmail)
+ {
+
+ string strRegex = @"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}" +
+ @"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
+ @".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
+ Regex re = new Regex(strRegex);
+ if (re.IsMatch(inputEmail))
+ return (true);
+ else
+ return (false);
+ }
+
+ private static bool ValidateUserNameFormat(string UserName)
+ {
+ if (!IsEmail(UserName))
+ {
+ Console.WriteLine("Not a valid email");
+ return false;
+ }
+ string[] emailAddress = UserName.Split('@');
+ string user = emailAddress[0];
+ string domain = emailAddress[1];
+ if (IsRogueDomain(domain))
+ return false;
+ return true;
+ }
+ protected override ValueTask> ValidateUserNamePasswordCoreAsync(string userName, string password)
+ {
+ if (!ValidateUserNameFormat(userName))
+ throw new SecurityTokenValidationException("Incorrect UserName format");
+
+ ClaimSet claimSet = new DefaultClaimSet(ClaimSet.System, new Claim(ClaimTypes.Name, userName, Rights.PossessProperty));
+ List identities = new List(1);
+ identities.Add(new GenericIdentity(userName));
+ List policies = new List(1);
+ policies.Add(new UnconditionalPolicy(ClaimSet.System, claimSet, DateTime.MaxValue.ToUniversalTime(), identities));
+ return ValueTask.FromResult(policies.AsReadOnly());
+ }
+ }
+
+ internal class UnconditionalPolicy : IAuthorizationPolicy
+ {
+ private readonly ClaimSet _issuance;
+ private readonly IList _identities;
+
+ public UnconditionalPolicy(ClaimSet issuer, ClaimSet issuance, DateTime expirationTime, IList identities)
+ {
+ if (issuer == null)
+ throw new ArgumentNullException(nameof(issuer));
+
+ if (issuance == null)
+ throw new ArgumentNullException(nameof(issuance));
+
+ Issuer = issuer;
+ _issuance = issuance;
+ _identities = identities;
+ ExpirationTime = expirationTime;
+ }
+
+ public string Id { get; } = Guid.NewGuid().ToString();
+
+ public ClaimSet Issuer { get; }
+
+ public DateTime ExpirationTime { get; }
+
+ public bool Evaluate(EvaluationContext evaluationContext, ref object state)
+ {
+ evaluationContext.AddClaimSet(this, _issuance);
+
+ if (_identities != null)
+ {
+ object value;
+ IList contextIdentities;
+ if (!evaluationContext.Properties.TryGetValue("Identities", out value))
+ {
+ contextIdentities = new List(_identities.Count);
+ evaluationContext.Properties.Add("Identities", contextIdentities);
+ }
+ else
+ {
+ contextIdentities = value as IList;
+ }
+ foreach (IIdentity identity in _identities)
+ {
+ contextIdentities.Add(identity);
+ }
+ }
+
+ evaluationContext.RecordExpirationTime(ExpirationTime);
+ return true;
+ }
+ }
+}
diff --git a/Extensibility/Security/TokenAuthenticator/Service/MyUserNameCredential.cs b/Extensibility/Security/TokenAuthenticator/Service/MyUserNameCredential.cs
new file mode 100644
index 0000000..f46ffee
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Service/MyUserNameCredential.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using CoreWCF.IdentityModel.Selectors;
+
+namespace CoreWcf.Samples.TokenAuthenticator
+{
+ public class MyUserNameCredential : ServiceCredentials
+ {
+ public MyUserNameCredential()
+ : base()
+ {
+ }
+
+ protected override ServiceCredentials CloneCore()
+ {
+ return new MyUserNameCredential();
+ }
+
+ public override SecurityTokenManager CreateSecurityTokenManager()
+ {
+ return new MySecurityTokenManager(this);
+ }
+ }
+}
+
+
diff --git a/Extensibility/Security/TokenAuthenticator/Service/Program.cs b/Extensibility/Security/TokenAuthenticator/Service/Program.cs
new file mode 100644
index 0000000..42c1ae9
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Service/Program.cs
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Security.Cryptography.X509Certificates;
+
+var builder = WebApplication.CreateBuilder();
+
+//Enable CoreWCF Services, with metadata (WSDL) support
+builder.Services.AddServiceModelServices()
+ .AddServiceModelMetadata()
+ .AddSingleton();
+
+var app = builder.Build();
+
+app.UseServiceModel(builder =>
+{
+ // Add the Calculator Service
+ builder.AddService(serviceOptions => { })
+ // Add WSHttpBinding endpoint
+ .AddServiceEndpoint(ServiceWSHttpBinding(), "CalculatorService");
+
+ Action serviceHost = host => ChangeHostBehavior(host);
+ builder.ConfigureServiceHostBase(serviceHost);
+
+ // Configure WSDL to be available
+ var serviceMetadataBehavior = app.Services.GetRequiredService();
+ serviceMetadataBehavior.HttpsGetEnabled = true;
+});
+
+app.Run();
+
+static Binding ServiceWSHttpBinding()
+{
+ WSHttpBinding binding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential);
+ binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
+ return binding;
+}
+
+void ChangeHostBehavior(ServiceHostBase host)
+{
+ var srvCredentials = new ServiceCredentials();
+ srvCredentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "localhost");
+ MyUserNameCredential serviceCredential = new MyUserNameCredential();
+ host.Description.Behaviors.Remove((typeof(ServiceCredentials)));
+ host.Description.Behaviors.Add(serviceCredential);
+}
diff --git a/Extensibility/Security/TokenAuthenticator/Service/Properties/launchSettings.json b/Extensibility/Security/TokenAuthenticator/Service/Properties/launchSettings.json
new file mode 100644
index 0000000..941fac5
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Service/Properties/launchSettings.json
@@ -0,0 +1,13 @@
+{
+ "profiles": {
+ "Service": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "https://localhost:5001/CalculatorService",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/Extensibility/Security/TokenAuthenticator/Service/Service.csproj b/Extensibility/Security/TokenAuthenticator/Service/Service.csproj
new file mode 100644
index 0000000..d11cc26
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Service/Service.csproj
@@ -0,0 +1,23 @@
+
+
+
+ net6.0
+ enable
+ true
+ InProcess
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Extensibility/Security/TokenAuthenticator/Service/appsettings.Development.json b/Extensibility/Security/TokenAuthenticator/Service/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Service/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/Extensibility/Security/TokenAuthenticator/Service/appsettings.json b/Extensibility/Security/TokenAuthenticator/Service/appsettings.json
new file mode 100644
index 0000000..10f68b8
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/Service/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/Extensibility/Security/TokenAuthenticator/cleanup.bat b/Extensibility/Security/TokenAuthenticator/cleanup.bat
new file mode 100644
index 0000000..0417074
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/cleanup.bat
@@ -0,0 +1,9 @@
+echo off
+set SERVER_NAME=localhost
+echo ---------------------------------------------------------------------
+echo cleaning up the certificates from previous run
+certmgr.exe -del -r CurrentUser -s TrustedPeople -c -n %SERVER_NAME%
+certmgr.exe -del -r LocalMachine -s My -c -n %SERVER_NAME%
+echo cleanup completed
+echo ---------------------------------------------------------------------
+
diff --git a/Extensibility/Security/TokenAuthenticator/setup.bat b/Extensibility/Security/TokenAuthenticator/setup.bat
new file mode 100644
index 0000000..f477aa0
--- /dev/null
+++ b/Extensibility/Security/TokenAuthenticator/setup.bat
@@ -0,0 +1,15 @@
+echo off
+set SERVER_NAME=localhost
+echo ---------------------------------------------------------------------
+echo cleaning up the certificates from previous run
+certmgr.exe -del -r CurrentUser -s TrustedPeople -c -n %SERVER_NAME%
+certmgr.exe -del -r LocalMachine -s My -c -n %SERVER_NAME%
+
+echo ---------------------------------------------------------------------
+echo Server cert setup starting
+echo for server: %SERVER_NAME%
+echo making server cert
+makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=%SERVER_NAME% -sky exchange -pe
+echo copying server cert to client's CurrentUser store
+certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
+echo ---------------------------------------------------------------------