I have written a custom MessageEncoder for a WCF pipeline, which is then applied using a BindingElementExtensionElement within my app.config.
On the server, when a message arrives, my message encoder needs to pull some information from the stream, and hold it for the duration of the Operation Context, so that it may be returned as a header in the response. The header manipulation screams of Behavior functionality, so...
I would like to know, how do I get my custom encoder to also apply a custom Behavior to the pipeline (which is where I will take care of storing this special 'information' across an operation context) - the encoder is essentially useless without the behavior, so I dont want to rely on the user remembering to add the behavior config when they add the encoder config - it should just happen automatically under the covers.
Additionally, where is a good resource for reading about the lifecycle of these pipeline elements?
Thanks
I wrote custom headers and message inspectors for WCF and found this article and some of the links in it useful along my way:
Handling custom SOAP headers via WCF Behaviors
HTH
You have basically two options:
1) either you expose your behavior via an attribute, so that it can be added to the server's config file - something like this:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="Default">
<YourCustomMessageEncoderBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
2) or you can create your own custom ServiceHost which makes sure to add this behavior to the list of dispatch behaviors if it's not already there.
class YourCustomServiceHost : ServiceHost
{
public YourCustomServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses) { }
protected override void ApplyConfiguration()
{
base.ApplyConfiguration();
YourCustomMessageEncodingBehavior behavior =
this.Description.Behaviors.Find<YourCustomMessageEncodingBehavior>();
if (behavior == null)
{
behavior = new YourCustomMessageEncodingBehavior();
this.Description.Behaviors.Add(behavior);
}
else
{
return;
}
}
}
Marc
Related
I've had quite the challenge in handling the following scenario.
I want to use the Unity DI Framework to create a new channel for my service when needed.
The service is secured with Federated security.
The service is not called from within a service hosted within IIS, but called from within a self-hosted WCF service.
My current issue is occurring in step 3 above - how do I bootstrap the token and yet then satisfy the above 2 requirements also?? I'll outline the solutions to each step though as they're non-trivial and will hopefully assist someone else with this issue.
Solving issue 1:
The following code-snippet will create a new instance of the channel anytime one is required.
container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(),
new InjectionFactory(
x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
.CreateChannel()));
Here's some examples of people doing this better than I:
http://unity.codeplex.com/discussions/211736
https://gist.github.com/tracker1/5675161
You can also use alternative lifetime managers as well e.g. the TransientLifetimeManager would work well here.
Solving Issue 2:
Now the real difficulty begins - how do I include a security token with the InjectionFactory?
Clearly I'm going to want to use CreatChannelWithIssuedToken, but I'm going to need to grab the bootstrap token to do so. This is fairly well documented around the net, e.g. here:
http://www.cloudidentity.com/blog/2012/11/30/using-the-bootstrapcontext-property-in-net-4-5-2/
Some important things to note: make sure that you have a serviceBehaviors section in the config that specifies useIdentityConfiguration="true" otherwise your system.identityModel section will be ignored e.g.
<serviceBehaviors>
<behavior name="">
<serviceCredentials useIdentityConfiguration="true" />
</behavior>
</serviceBehaviors>
And then your system.identityModel section should also feature:
<system.identityModel>
<identityConfiguration>
<securityTokenHandlers>
<securityTokenHandlerConfiguration saveBootstrapContext="true" />
</securityTokenHandlers>
</identityConfiguration>
</system.identityModel>
Then, provided that you've made a request to set this correctly on the session (see my other question: AJAX call against REST endpoint secured with Thinktecture's IdentityServer STS it will be available in the session whenever you access the bootstrap context security token like so:
var bootstrapContext = ((ClaimsIdentity) Thread.CurrentPrincipal.Identity).BootstrapContext
as BootstrapContext;
SecurityToken securityToken = bootstrapContext.SecurityToken;
You can then either change your InjectionFactory to look like this:
container.RegisterType<IControllerConfigurationService>(new PerResolveLifetimeManager(),
new InjectionFactory(
x =>
{
var bootstrapContext = ((ClaimsIdentity)
Thread.CurrentPrincipal.Identity).BootstrapContext as BootstrapContext;
var securityToken = bootstrapContext.SecurityToken;
return new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
.CreateChannelWithIssuedToken(securityToken);
}));
Or perhaps better yet, create a class that inherits from ChannelFactory and add a method that pulls that combines the above logic into a single CreateChannelWithIssuedTokenUsingBootstrapContext method:
public class ChannelFactoryWithChannelFactoryOperations<T> : ChannelFactory<T>
{
protected ChannelFactoryWithChannelFactoryOperations(Type channelType) : base(channelType)
{
}
public ChannelFactoryWithChannelFactoryOperations()
{
}
public ChannelFactoryWithChannelFactoryOperations(string endpointConfigurationName)
: base(endpointConfigurationName)
{
}
public ChannelFactoryWithChannelFactoryOperations(string endpointConfigurationName,
EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress)
{
}
public ChannelFactoryWithChannelFactoryOperations(Binding binding) : base(binding)
{
}
public ChannelFactoryWithChannelFactoryOperations(Binding binding, string remoteAddress)
: base(binding, remoteAddress)
{
}
public ChannelFactoryWithChannelFactoryOperations(Binding binding, EndpointAddress remoteAddress)
: base(binding, remoteAddress)
{
}
public ChannelFactoryWithChannelFactoryOperations(ServiceEndpoint endpoint) : base(endpoint)
{
}
public T CreateChannelWithIssuedTokenUsingBootstrapContext()
{
var bootstrapContext =
((ClaimsIdentity) Thread.CurrentPrincipal.Identity).BootstrapContext as BootstrapContext;
SecurityToken securityToken = bootstrapContext.SecurityToken;
return CreateChannelWithIssuedToken(securityToken);
}
}
This allows you to then just call this:
container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(),
new InjectionFactory(
x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
.CreateChannelWithIssuedTokenUsingBootstrapContext()));
Solving issue 3:
Adding upon the complexity of the above two issues, I'm now trying the same thing outside of IIS in my own Self-Hosted WCF service. This same service is completely stateless, so here's where my next dilemma occurs:
Bootstrapping of the security token still occurs, but it isn't occurring on the correct Thread. Unity seems to run its InjectionFactory in a separate Thread to the actual service call execution.
i.e. when the InjectionFactory delegate above executes, the CurrentPrincipal is an unauthorized GenericPrincipal. This is different from what we had in issue 2 above - where it is an authorized ClaimsPrincipal - I believe this is all set up by the IIS session (please do feel free to correct if I'm incorrect).
Funnily enough, if we then replace the above with
container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(),
new InjectionFactory(
x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
.CreateChannel()));
i.e. now just inject an unsecured Channel object, we can then observe that in our self-hosted WCF service, where we actually try to interact with the channel, the Thread.CurrentPrincipal is the authenticated ClaimsPrincipal with the SecurityToken correctly Bootstrapped on the principal.
So the problem can be summarized as the following:
because the InjectionFactory delegate executes on a thread on which no Authentication/Authorization has yet taken place, the SecurityToken is not actually available to pass to the creation of the channel.
Does anybody have any suggestions on how I might solve this issue? Have I already painted myself into a corner with this particular combination of self-hosted WCF and unity?
Thanks,
Clint
I've found a way to do this, though I'd love a better suggestion. The SessionSecurityTokenHandler ValidateToken method always executes on the same Thread as the InjectionFactory delegate, so we can set that Thread's CurrentPrincipal in the ValidateToken method of a CustomSessionSecurityTokenHandler like so:
public class CustomSessionSecurityTokenHandler: SessionSecurityTokenHandler
{
public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
{
var claimsIdentities = base.ValidateToken(token);
Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentities);
return claimsIdentities;
}
}
And then the system.identityModel configuration needs to be modified to include the custom securityTokenHandler:
<securityTokenHandlers>
<remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<add type="MyAssembly.CustomSessionSecurityTokenHandler, MyAssembly" />
</securityTokenHandlers>
Having done that, attempts to access the security token from the Bootstrap context then succeed:
container.RegisterType<IControllerConfigurationService>(new PerResolveLifetimeManager(),
new InjectionFactory(
x =>
{
var bootstrapContext = ((ClaimsIdentity)
Thread.CurrentPrincipal.Identity).BootstrapContext as BootstrapContext;
var securityToken = bootstrapContext.SecurityToken;
return new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
.CreateChannelWithIssuedToken(securityToken);
}));
Please feel free to add any alternative suggestions! :)
Thanks,
Clint
I have a WCF REST based service that I would like to add Swagger to. I have installed Swagger.NET package, my project is using .NET 4.0. I have also enabled XML documentation, etc... but now I am not sure the best route from here.
When i launch http://localhost/SwaggerUI/index.html I get the default page with the http://YOUR-URL-HERE:PORT/api/swagger. I am not sure what url should go here. I did enable help of the service via: <standardEndpoint name="" helpEnabled="true" /> which does give me the ugly page here: http://localhost/api/help -- when i plug that into Swagger i just get the result:
200 : OK http://localhost/api/help
What is the best way to proceed, i am not using WebApi but have many of the features available if that helps.
As of December 2015, there is an implementation of Swagger for Wcf now. You should check this out - Swagger for WCF - SwaggerWcf
Currently there are no implementations of Swagger for WCF. You will either need to implement your own by following the spec from https://github.com/wordnik/swagger-core/wiki, or wait patiently for someone to implement one for you.
I currently have an implementation in progress, but it's not quite ready for primetime. The basic spec is deceptively simple, but fitting it to WCF is a definite challenge.
I tried to implement swagger for WCF in my application as the implementation is different as we implanted for web API(simpler). I will tell you how its different and how it works step by step:-
**1. Why Swagger4Wcf**
----------------------
•Manually writing yaml description for swagger and maintain it especially WCF services are boring.
•There is a nuget package called Swagger4WCF that automatically generates yaml description for swagger 2.0 for each interface matching attributes used by WCF (ServiceContract/OperationContract/WebGet/WebInvoke).
2. How Swagger Works in the Background
--------------------------------------
Swagger4WCF uses NuPack post build pattern to trigger at build time.
https://www.codeproject.com/Tips/1190360/How-to-setup-a-managed-postbuild-without-scripting
3.At build time, it will detect assemblies present in output directory, open them with mono.cecil (to reflect assemblies) to generate expected yaml description for swagger 2.0.
Swagger4WCF detects **WebGet/WebInvoke** to provide Verb/Method in serialization style in yaml.
Steps to implement Swagger in your application:-
1. Install SwaggerWcf package
2. Configure WCF routes
We have to add the route in the Application_Start method inside Global.asax
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("v1/rest", new WebServiceHostFactory(), typeof(BookStore)));
RouteTable.Routes.Add(new ServiceRoute("api-docs", new WebServiceHostFactory(), typeof(SwaggerWcfEndpoint)));
}
Note: Edit Web.config and add the following (if it doesn't exist yet) inside the system.serviceModel block
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
3. Configure WCF response auto types (optional)
We have to add the following to Web.config. This will allow the WCF service to accept requests and send replies based on the Content-Type headers.
<behavior name="webHttpBehavior">
<webHttp defaultOutgoingResponseFormat="Json" automaticFormatSelectionEnabled="true"/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
4. Decorate WCF services interfaces
For each method, we have to configure the WebInvoke or WebGet attribute, and add a SwaggerWcfPath attribute.
[SwaggerWcfPath("Get book", "Retrieve a book from the store using its id")]
[WebGet(UriTemplate = "/books/{id}", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
Book ReadBook(string id);
5. Decorate WCF services class
• Add the SwaggerWcf and AspNetCompatibilityRequirements attributes to the class providing the base path for the service.
• For each method, add the SwaggerWcfTag to categorize the method and theSwaggerWcfResponse for each possible response from the service.
[SwaggerWcfTag("Books")]
[SwaggerWcfResponse(HttpStatusCode.OK, "Book found, value in the response body")]
[SwaggerWcfResponse(HttpStatusCode.NoContent, "No books", true)]
public Book[] ReadBooks()
{
}
6. Decorate data types used in WCF services
[DataContract]
[Description("Book with title, first publish date, author and language")]
[SwaggerWcfDefinition(ExternalDocsUrl = "http://en.wikipedia.org/wiki/Book", ExternalDocsDescription = "Description of a book")]
public class Book
{
[DataMember]
[Description("Book ID")]
public string Id { get; set; }
[DataMember]
[Description("Book Title")]
public string Title { get; set; }
[DataMember]
[Description("Book First Publish Date")]
public int FirstPublished { get; set; }
[DataMember]
[Description("Book Author")]
public Author Author { get; set; }
[DataMember]
[Description("Book Language")]
public Language Language { get; set; }
}
That's it wcf for Swagger implemented.
Please free if you face any issue.
Thanks,
Abhi
I have created three assemblies. A web site, a WCF service and a contracts assembly that holds the interfaces that the services implement. I would like to use Castle Windsor to create the services for me on the client (website) so that I do not have to have an endpoint in the web.config of the web site for each service that I wish to use.
I would like to look at the contract assembly and get all the service interfaces in a namespace. Right now for every service I have something like the following when registering the components with the container.
container.Register(Component.For<ChannelFactory<IMyService>>().DependsOn(new { endpointConfigurationName = "MyServiceEndpoint" }).LifeStyle.Singleton);
container.Register(Component.For<IMyService>().UsingFactoryMethod((kernel, creationContext) => kernel.Resolve<ChannelFactory<IMyService>>().CreateChannel()).LifeStyle.PerWebRequest);
and in my web.config I have the setup code.
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="AuthToken" type="MyNamespace.Infrastructure.AuthTokenBehavior, MyNamespace.Contracts" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior>
<AuthToken />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00">
<readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647"></readerQuotas>
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint name="MyServiceEndpoint" address="http://someurl/MyService.svc" binding="wsHttpBinding" contract="MyNamespace.Contracts.IMyService"></endpoint>
</client>
</system.serviceModel>
I end up with multiple service endpoints that all look almost exactly the same and when we deploy onto clients machines they have to set the address of every endpoint even though the base url is the same for every one.
I would like to have a base url in my web.config that is grabbed through code and then have the services registered with the container using reflection on the contracts assembly. I do need the specialised endpoint behaviour that is in the above config file.
Where so I start? the WcfFacility looks great but the doco is a bit lacking...
I agree the docs for the wcf facility are lacking and that is sad because it is a really great tool and it would be a real shame if people didn't use it because they could not get started, so let me see if I can help you out a little bit if I can...
Let's create a three project application that has:
A class library for shared contracts
A console application that acts as a server
A console application that acts as a client
The idea is that we want to be able to use the service names when we register the services and to share a base URL (I think that is what you were asking and if not, hopefully you can extrapolate from here). So, firstly, the shared contracts simply has this in it (nothing special, normal WCF fare):
[ServiceContract]
public interface IMyService1
{
[OperationContract]
void DoSomething();
}
[ServiceContract]
public interface IMyService2
{
[OperationContract]
void DoSomethingToo();
}
Now the server console application looks like this, we firstly implement the service contracts (again nothing special there, just classes implementing interfaces) and then just register them all as services (notice no need for any configuration file here and you can change the way you decide what are services etc using all the options that Windsor gives you - my scheme is a bit limited but it gives you an idea):
namespace Services
{
public class MyService1 : IMyService1
{
public void DoSomething()
{
}
}
public class MyService2 : IMyService2
{
public void DoSomethingToo()
{
}
}
}
//... In some other namespace...
class Program
{
// Console application main
static void Main()
{
// Construct the container, add the facility and then register all
// the types in the same namespace as the MyService1 implementation
// as WCF services using the name as the URL (so for example
// MyService1 would be http://localhost/MyServices/MyService1) and
// with the default interface as teh service contract
var container = new WindsorContainer();
container.AddFacility<WcfFacility>(
f => f.CloseTimeout = TimeSpan.Zero);
container
.Register(
AllTypes
.FromThisAssembly()
.InSameNamespaceAs<MyService1>()
.WithServiceDefaultInterfaces()
.Configure(c =>
c.Named(c.Implementation.Name)
.AsWcfService(
new DefaultServiceModel()
.AddEndpoints(WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format(
"http://localhost/MyServices/{0}",
c.Implementation.Name)
)))));
// Now just wait for a Q before shutting down
while (Console.ReadKey().Key != ConsoleKey.Q)
{
}
}
}
And that is the server, now how to consume these services? Well, actually that is quite easy, here is a client console application (it references just the contracts class library):
class Program
{
static void Main()
{
// Create the container, add the facilty and then use all the
// interfaces in the same namespace as IMyService1 in the assembly
// that contains the aforementioned namesapce as WCF client proxies
IWindsorContainer container = new WindsorContainer();
container.AddFacility<WcfFacility>(
f => f.CloseTimeout = TimeSpan.Zero);
container
.Register(
Types
.FromAssemblyContaining<IMyService1>()
.InSameNamespaceAs<IMyService1>()
.Configure(
c => c.Named(c.Implementation.Name)
.AsWcfClient(new DefaultClientModel
{
Endpoint = WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format(
"http://localhost/MyServices/{0}",
c.Name.Substring(1)))
})));
// Now we just resolve them from the container and call an operation
// to test it - of course, now they are in the container you can get
// hold of them just like any other Castle registered component
var service1 = container.Resolve<IMyService1>();
service1.DoSomething();
var service2 = container.Resolve<IMyService2>();
service2.DoSomethingToo();
}
}
That's it - hopefully this will get you started (I find that experimenting and using the intellisense usually gets me where I need to go). I showed you both the service and client sides but you can just use one or the other if you prefer.
You should be able to see where the binding is configured and how I have gone about constructing the URLs so in your case you could easily just pluck your base URL from a configuration file or whatever you want to do.
One last thing to mention is that you can add your custom endpoint behaviour by adding it as an extension to the endpoint, so in the client example you would have something like this:
Endpoint = WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format("http://localhost/MyServices/{0}", c.Name.Substring(1)))
.AddExtensions(new AuthTokenBehavior())
Downloaded the WCF REST Template from this location.
The default response format is XML, which works great. However, when I try to get a JSON response, I still get XML.
This is my modified code -
[WebGet(UriTemplate = "",ResponseFormat = WebMessageFormat.Json)]
public List<SampleItem> GetCollection()
{
// TODO: Replace the current implementation to return a collection of SampleItem instances
return new List<SampleItem>() { new SampleItem() { Id = 1, StringValue = "Hello" } };
}
Note the ResponseFormat=WebMessageFormat.Json. That is the only change I did to that template.
What am I missing?
Thanks!
Figured out. automaticFormatSelectionEnabled property for standardendpoint should be set to false and defaultOutgoingReponseFormat should be set to Json.
<standardEndpoint name="" helpEnabled="true"
automaticFormatSelectionEnabled="false"
defaultOutgoingResponseFormat ="Json" />
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="false" defaultOutgoingResponseFormat="Json"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
Changes to 2 attributes within the web.config will fix it:
automaticFormatSelectionEnabled=false
defaultOutgoingResponseFormat=Json (edited: from "true")
For me, setting the response format to JSON in the WebGet attribute doesn't work. Setting it in the body of the method does;
// This works
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
return jsonData;
// This doesn't work
`[WebGet(UriTemplate = "/conditions?term={term}", ResponseFormat = WebMessageFormat.Json)]`
Click -> reference links
"When automatic format selection is enabled, the infrastructure parses the Accept header of the request message and determines the most appropriate response format. If the Accept header does not specify a suitable response format, the infrastructure uses the Content-Type of the request message or the default response format of the operation."
EDIT: this link might get you moving ahead
http://blogs.msdn.com/b/endpoint/archive/2010/11/01/wcf-webhttp-service-returns-http-415-unsupported-media-type.aspx
I was hitting issues like this every time I tried to create a JSON web service.
Now, I just follow the steps shown here.
http://mikesknowledgebase.com/pages/Services/WebServices-Page1.htm
It shows how to create a JSON web service, step-by-step, with screenshots and examples.
Hope this helps.
I have a per-call WCF service that's being hosted in IIS (.svc). In the service's constructor, I set Thread.CurrentPrincipal = HttpContext.Current.User as per this article. In this case HttpContext.Current.User is of type Microsoft.IdentityModel.Claims.ClaimsPrincipal and has the claims that were sent back from my custom passive STS.
However, as soon as I step into my service operation and examine Thread.CurrentPrincipal, while this object is still of type Microsoft.IdentityModel.Claims.ClaimsIdentity, the object itself is no longer the same as HttpContext.Current.User (IsAuthenticated = false, AuthenticationType = "" and Name is null on Thread.CurrentPrincipal.Identity), whereas these values are all still filled in correctly on HttpContext.Current.User. This tells me that something is intercepting the call to the operation and incorrectly changing the current principal to some generic, empty, unauthenticated claims principal.
I checked the thread ID in the constructor as well as in the operation and it's the same in both places, and evaluating Thread.CurrentPrincipal in the immediate window after assigning from HttpContext.Current.User shows that the thread identity is being set correctly in the constructor, so something is definitely executing in between the constructor and the method, and that something is changing my Thread.CurrentPrincipal.
Does anybody have any idea what is doing this, and how I can go about preventing / fixing this behaviour?
I just ran into a similar problem. I set my custom principal in the constructor of my WCF service. When I left the constructor, and entered the method I called, the thread.currentprincipal was overridden by an empty one. I solved this by adding the following behavior:
<serviceAuthorization principalPermissionMode="None"></serviceAuthorization>
This worked fine for me.
When configuring a service for WIF federation, you call
FederatedServiceCredentials.ConfigureServiceHost(this);
Part of what this call does is to set up a custom ServiceAuthorizationManager of type IdentityModelServiceAuthorizationManager on the service host. This authorization manager appears to get called in between activation (construction) of the instance and execution of the operation, and it overwrites Thread.CurrentPrincipal with an instance of IClaimsPrincipal, but it doesn't seem to realize that it's running in ASP.NET Compatibility mode, so it doesn't pull the principal from HttpContext.Current.User.
I was able to bypass this behaviour by deriving from IdentityModelServiceAuthorizationManager and overriding the CheckAccess method as follows:
public class CustomAuthorizationManager : IdentityModelServiceAuthorizationManager
{
public override bool CheckAccess(System.ServiceModel.OperationContext operationContext, ref System.ServiceModel.Channels.Message message)
{
var result = base.CheckAccess(operationContext, ref message);
var properties = operationContext.ServiceSecurityContext.AuthorizationContext.Properties;
properties["Principal"] = System.Web.HttpContext.Current.User;
return result;
}
}
This then gets applied to the service host as follows:
protected override void InitializeRuntime()
{
FederatedServiceCredentials.ConfigureServiceHost(this);
this.Authorization.ServiceAuthorizationManager = new CustomAuthorizationManager();
base.InitializeRuntime();
}
And now when I enter my service operation, I have the correct IClaimsPrincipal on Thread.CurrentPrincipal, so declarative PrincipalPermission now works as expected.
confguration settings for call FederatedServiceCredentials.ConfigureServiceHost(this);
is as below
in system.serviceModel add following
<extensions>
<behaviorExtensions>
<add name="federatedServiceHostConfiguration" type="Microsoft.IdentityModel.Configuration.ConfigureServiceHostBehaviorExtensionElement, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</behaviorExtensions>
</extensions>
under inside add folowing line
<behavior name="serviceBehavior">
<federatedServiceHostConfiguration name="MyService" />
My guess is that nothing is intercepting the call. Either the CurrentPrincipal is reset by the time you're examining it or you're in a different thread.
Check the CurrentPrincipal immediately after assigning to it and you should see the correct value.