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