I have a web site hosted in IIS that uses Windows Authentication and exposes WCF web services.
I configure this service with an endpoint behavior:
<serviceAuthorization principalPermissionMode ="UseAspNetRoles"
roleProviderName="MyRoleProvider"/>
and a binding:
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" />
</security>
When the service is called, Thread.CurrentPrincipal
is set to a RolePrincipal
with the client's Windows identity and roles provided by by configured provider.
All is well with the world.
Now I've added some additional WCF services that are consumed by REST-ful Ajax calls: Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"
in the svc file, WebGet
attribute in the service contract, and the AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)
attribute on the service implementation.
I also add the following incantation to web.config as recommended in MSDN:
<system.serviceModel>
...
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
...
</system.serviceModel>
My Ajax service almost works the way I want it to. When it's called, HttpContext.Current.User
is set to a RolePrincipal
with the roles I expect. But Thread.CurrentPrincipal
remains set to an unauthenticated GenericPrincipal
.
So I need to add a line of code to each of my service methods:
Thread.CurrentPrincipal = HttpContext.Current.User
Is there any incantation in the configuration file I can use to get Thread.CurrentPrincipal
to be set automagically, like it is for a normal SOAP service?
UPDATE Here's a blog from someone who had the same problem, and solved it by implementing custom behaviors. Surely there's a way to do this out of the box?
UPDATE 2 Coming back to add a bounty to this as it's bugging me again in a new project, using a WCF WebGet-enabled service on .NET 3.5.
I've experimented with a number of options, including setting principalPermissionMode="None", but nothing works. Here's what happens:
I navigate to a WebGet URL that calls my service: http://myserver/MyService.svc/...
I've put a breakpoint in Global.asax "Application_AuthorizeRequest". When this breakpoint is hit, both "HttpContext.Current.User" and "Thread.CurrentPrincipal" have been set to a "RolePrincipal" that uses my configured ASP.NET RoleProvider. This is the behavior I want.
I have a second breakpoint when my service's OperationContract method is called. When this breakpoint is hit, HttpContext.Current.User still references my RolePrincipal, but Thread.CurrentPrincipal has been changed to a GenericPrincipal. Aaargh.
I've seen suggestions to implement a custom IAuthorizationPolicy, and will look into that if I don't find a better solution, but why should I need to implement a custom policy to make use of existing ASP.NET authorization functionality? If I have principalPermissionMode = "UseAspNetRoles", surely WCF should know what I want?
This is an interesting question. I don't have the same setup as you, so its difficult to test whether my recommendations will apply exactly to your use case, but I can share what has worked for us with similar projects.
Thread.CurrentPrincipal
and HttpContext.Current.User
in SyncWe wrote an HttpModule called "AuthenticationModule" which inherits from IHtppModule
.
We then attached to the HttpApplication.AuthenticateRequest
event which happens very early in request lifecycle.
In our AuthenticateRequest event handler, we implement our application specific requirements including setting Thread.CurrentPrincipal
and if necessary also the current context user. In this way you only implement this code once for your entire application and if it changes (like if you implement a custom Principal IIDentity) you have only one place to change it. (Don't duplicate this code in every service method.)
public class AuthenticationModule : IHttpModule
{
public void Dispose() { return; }
public void Init(HttpApplication app)
{
app.AuthenticateRequest += new EventHandler(app_AuthenticateRequest);
}
void app_AuthenticateRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
// This is what you were asking for, but hey you
// could change this behavior easily.
Thread.CurrentPrincipal = app.Context.User;
}
}
Ours is actually a bit more complex as we implement a custom IIdentity
, create an instance of GenericPrincipal
and then assign it to both app.Context.User
and Thread.CurrentPrincipal
; but, the above is what you were asking for.
Don't forget to register your new HttpModule in your web.config
!
For integrated app pools:
<system.webServer>
<modules>
<add name="AuthenticationModule" type="YourNameSpace.AuthenticationModule" preCondition="integratedMode" />
</modules>
</system.webServer>
For old classic app pools you'd have to put it in <system.web><httpModules></httpModules></system.web>
You might need to play with what goes inside that AuthenticationRequest event handler and/or the order with which you register the handler. Because ours is totally custom it might be different than what you need. We actually grab the Forms Authentication cookie, decrypt it, etc... you might need to ping some built in methods for WindowsAuthentication.
I believe this is a more generic way to handle your application authentication stuff as it applies to all HttpRequests
whether that be a page request, an IHttpHandler
, some 3rd party component, etc... That will keep it consistent throughout your app.
I'm not sure. Maybe this will help
<configuration>
<system.web>
<identity impersonate="true" />
</system.web>
</configuration>
http://msdn.microsoft.com/en-us/library/134ec8tc(v=vs.80).aspx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With