My current task is to secure a WCF service. The service is hosted using the configuration framework (5.5, released with the StockTraider sample) and the caller uses the configuration framework as well.
I managed to secure the connection using ws2007FederationHttpBinding.
For the "IsOnline()"-Check my STS issues a service token and this works already but for the actual service calls, I want to have ActAs-Tokens to still know the real user inside the called service.
My STS is capable of issuing the correct ActAs-Tokens.
The problem is the loadbalancing client, which always opens the factory and I cannot call the WIF-methods (ConfigureChannelFactory() and CreateChannelActingAs()) anymore, because they require the factory to be in the created state.
My best try is this, but it looses the ActAs-Subject somewhere and feels like a hack:
IPSServiceClient = new Client(serviceName, settingsInstance, createNewChannelInstance: true);
var token = ((IClaimsIdentity)Thread.CurrentPrincipal.Identity).BootstrapToken;
var factoryObject = IPSServiceClient.createANewChannelFactoryByAddress(IPSServiceClient.getANodeAddress());
var factory = factoryObject as ChannelFactory<IIWBPortalServiceV1>;
factory.ConfigureChannelFactory(); //factory must not be state=open here
factory.Credentials.SupportInteractive = false; //no cardspace
_channel = factory.CreateChannelActingAs(token);
Do I miss an extensibility point in the config framework? What is the best way I should go?
If I make a new console app, add service reference and add the two calls (ConfigureChannelFactory() and CreateChannelActingAs()) it just works!
The posted code inside my questions works. The problem was the web.config of the STS which was missing AudienceUris inside the ActAs-securityTokenHandlers section.
Still: The posted code feels like a hack to me.
Related
I am experimenting with a gRPC service and client using proto files. The advice is to use gRPC client factory integration in .NET Core (https://learn.microsoft.com/en-us/aspnet/core/grpc/clientfactory?view=aspnetcore-3.1). To do this you register the client derived from Grpc.Core.ClientBase that is generated by the Grpc.Tools package, like this:
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddGrpcClient<MyGrpcClientType>(o =>
{
o.Address = new Uri("https://localhost:5001");
});
})
My understanding is that MyGrpcClientType is registered with DI as a transient client, meaning a new one is created each time it is injected, but that the client is integrated with the HttpClientFactory, allowing the channel to be reused rather than be created each time.
Now, I would like to use protobuf-net.grpc to generate the client from an interface, which appears to be done like this:
GrpcClientFactory.AllowUnencryptedHttp2 = true;
using var http = GrpcChannel.ForAddress("http://localhost:10042");
var calculator = http.CreateGrpcService<ICalculator>();
If I am correct in thinking that channels are expensive to create, but clients are cheap, how do I achieve integration with the HttpClientFactory (and so reuse of the underlying channel) using protobuf-net.grpc? The above appears to create a GrpcChannel each time I want a client, so what is the correct approach to reusing channels?
Similarly, is it possible to register the protobuf-net.grpc generated service class with the below code in ASP.Net Core?
endpoints.MapGrpcService<MyGrpcServiceType>();
(Please correct any misunderstandings in the above)
Note that you don't need the AllowUnencryptedHttp2 - that's just if you aren't using https, but: you seem to be using https.
On the "similarly"; that should already work - the only bit you might be missing is the call to services.AddCodeFirstGrpc() (usually in Startup.cs, via ConfigureServices).
As for the AddGrpcClient; I would have to investigate. That isn't something that I've explored in the integrations so far. It might be a new piece is needed.
The Client Factory support not exists, and works exactly like documented here except you register with the method
services.AddCodeFirstGrpcClient<IMyService>(o =>
{
o.Address = new Uri("...etc...");
});
Through custom auth logic I already have an instance of WindowsIdentity.
Now I want to call a WCF service and pass the identity:
WindowsIdentity id = ... // get from session etc.
using (id.Impersonate())
{
var proxy = ... // create or access
proxy.DoSomething();
}
The above code works fine. But it has the drawback that you need to wrap every call in an impersonation scope (it's easy to forget, thus error-prone and for an existing app a lot of code needs to be changed).
I have also found an alternative to pass the credential:
var client = new SomeClient(new InstanceContext(callback), GetEndpointName());
client.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential(cached.User, cached.Password);
This has the advantage that it can be set in a central factory method and minimates the change to the existing code base. My application has the credentials cached and could do this, however since I already have a WindowsIdentity instance I thought it wouldn't be needed. Also am not sure if the performance of option two is slower, since username/pwd needs an additional resolving.
Is option two a good one or are there any drawbacks?
Or is there a third option that allows to pass the WindowsIdentity when creating the WCF proxy?
I'm using a really simple WCF project which is being hosted by a WPF application. It basically operates as a REST server for dishing up data on my PC. No IIS, and it runs as SingleInstance. I want to see what IP's are accessing MyService, and what WebMethod they're attempting to invoke.
Ok so I can have an event as part of my Service, declared in the service class itself. Here's some code that gets it going, it all works exactly as expected (no flames about m_ please ;)):
MyService ds = new MyService(); // It's not really called this :)
ds.Request += new EventHandler(ds_Request); // I want to avoid this
ds.SomePropertySetFromMyRehostingClient = "something"; // SingleInstance now required
m_Service = new ServiceHost(ds, new Uri(GetServerHostUri()));
m_Service.Description.Behaviors.Find<ServiceBehaviorAttribute>().InstanceContextMode = InstanceContextMode.Single;
m_Service.BeginOpen(new TimeSpan(0, 0, 5), new AsyncCallback(EndStartService), null);
Then in each service method, I can raise this event so my app knows that someone has tried to use it. Brilliant, but let's face it, this is awful.
I have to write something along the lines of:
var who = OperationContext.Current.IncomingMessageProperties.Via;
var what = OperationContext.Current.IncomingMessageProperties["UriTemplateMatchResults"];
for each service call.
Is there a more generic catch-all-event that can detect a call to my service? There's probably one fired by one of the many Behaviour/ChannelDispatcher types which I admittedly don't fully understand.
Thanks for your help, Tom
Using IParameterInspector you can hook to any method calls and inspect the method and/or parameters.
There is no other way to get the extra information (IP address, etc) of the incoming message other than the one you have used. This is just a bad design by Microsoft IMHO (see my rant here).
You can find an example here.
What is the best way to pass an existing SAML token from a website already authenticated via a passive STS?
We have built an Identity Provider which is issuing passive claims to the website for authentication. We have this working. Now we would like to add some WCF services into the mix - calling them from the context of the already authenticated web application. Ideally we would just like to pass the SAML token on without doing anything to it (i.e. adding new claims / re-signing). All of the examples I have seen require the ActAs sts implementation - but is this really necessary? This seems a bit bloated for what we want to achieve.
I would have thought a simple implementation passing the bootstrap token into the channel - using the CreateChannelActingAs or CreateChannelWithIssuedToken mechanism (and setting ChannelFactory.Credentials.SupportInteractive = false) to call the WCF service with the correct binding (what would that be?) would have been enough.
We are using the Fabrikam example code as reference, but as I say, think the ActAs functionality here is overkill for what we are trying to achieve.
What you need in this case is to insert the contents of your token into each outgoing message. If you look at the WIF Identity Training Toolkit they have an IssuedTokenHeader class that will facilitate this (along with the ClaimsIdentitySessionManager). These classes were built for Silverlight but, it doesn't change the solution they offer.
Here is an excerpt from the ClaimsIdentitySessionManager class.
using (OperationContextScope scope = new OperationContextScope(contextChannel))
{
IssuedTokenHeader header = new IssuedTokenHeader(this.TokenCache.GetTokenFromCache(serviceAppliesTo));
OperationContext.Current.OutgoingMessageHeaders.Add(header);
asyncOperation();
}
I have a client app and a server app.
The client calls a wcf service and passes machine information
to server , based on the machine name the server calls back a wcf service on client side.
So to achieve this , I am just changing the EndPointAddress but then it's throwing
NoEndPointFoundException , how can i fix it , below is the code :
public void RegisterTasks(MachineConfig machineInfo)
{
string add = exeProxy.Endpoint.Address.Uri.Scheme + "://" + machineInfo.MachineName.Trim()+"/"
+ exeProxy.Endpoint.Address.Uri.Segments[1];
Uri uri = new Uri(add);
EndpointAddress eadd = new EndpointAddress(add);
WSHttpBinding whttpBinding = new WSHttpBinding(SecurityMode.None);
//ServiceReference1.ExecuteTaskClient newProxy = new ExecuteTaskClient(whttpBinding , eadd);
//EndpointAddress endPointAddress = ;
exeProxy.Endpoint.Address = eadd;
//exeProxy.Endpoint.Binding = new System.ServiceModel.BasicHttpBinding("httpBinding");
// we just execute the task by
// calling the wcf service on client side
foreach (Task task in machineInfo.Tasks)
{
exeProxy.ExecuteTask(task.TaskID);
// newProxy.ExecuteTask(task.TaskID);
}
}
I am assuming that you are getting EndpointNotFoundException not NoEndPointFoundException. I could not find a reference to NoEndPointFoundException.
http://msdn.microsoft.com/en-us/library/system.servicemodel.endpointnotfoundexception.aspx
What this is saying is that the client cannot find the server. In your case the server is trying to call back to the client, so the roles are reversed.
There are two things that could be wrong:
the url that you have in the variable "add" is incorrect (try logging the value)
the wcf service on the client side is not listening on the correct url (try putting the url in a browser)
Hope this helps
Shiraz
My first impression is that your design should be reconsidered. Any time I see an ingenious (read: bizarre) solution I see a whole heap of head banging in the making.
So firstly, write out what it is that you are trying to acheive in baby speak: "I want client to be contacted when server has new data." and then think about how it can be acheived using more conventional techniques.
Check out the duplex bindings - and polling a stateful singleton is not always a bad idea if you're not scaling into thousands of clients -- in fact, I bet it'd scale better than your current design.
But, to solve your current design issue, I'd setup the client (which will become the server) with the MEX endpoint (mexHttpBinding) and then set it off so its listening, then use VS (an empty project or from the server project) to try and connect to the client(server) by way of Add Service Reference and supplying the local machine name etc. This alone might turn up your problem. Then once added, you can use the autogen app.config to know what settings you need.
Also, have you considered how the server will be able to call the client if the server doesn't have the proxy classes setup?
It does sound like you're making a rod for your own back.