I have a ServiceHost
that implements many Contacts on various endpoints and bindings ((http & https) x (rest & soap) with Anonymous Access, Windows, or Custom authentication, as bound depending on the Contract) & nettcp with Windows authentication. Custom authentication is embedded in the http or soap headers. Each endpoint and contract has its purpose. It should all be manageable on a single ServiceHost
- I don't want to split it up.
I've gotten it to the point where all the endpoints work and serve their content correctly, but the authentication/authorization isn't integrated into the WCF stack. I have been using a MessageInspector
to handle validation of the authentication header and allowing it if the token was valid.
Now I want to integrate the authentication/authorization into the WCF stack. I want to be able to access the identity and check the claims in each Operation's implementation. And perhaps basic claims can be authorized centrally, like "are these claims authorizing anything at all in this contract?" (by contract type).
In the case of custom authentication, I have a signed/secure token that includes a custom implementation of identity and property claims which I can properly extract and convert into WCF claims upon receipt (even if I don't know exactly where to put them once I've got them). For Windows authentication, I just need access to the default Windows identity stuff.
I can set the ServiceAuthenticationManager
and ServiceAuthorizationManager
each to custom values, but it's not doing anything I want it to and I'm getting totally lost.
For example, the ReadOnlyCollection<IAuthorizationPolicy> authPolicy
coming into Authenticate()
seems to be inconsistent - sometimes it's empty, sometimes it has one UnconditionalPolicy
and sometimes it has 2 or more (4?) of my custom IAuthorizationPolicy
already there. Meanwhile IAuthorizationPolicy.Evaluate()
gets executed anywhere from 0 to ~9 times per request. Sometimes, within IAuthorizationPolicy.Evaluate()
, OperationContext.Current
is null
! And sometimes the evaluationContext.ClaimSets
already has my claimset. Yet state
is always null even when I give it a value in a previous enactment.
Rather than tackling these problems individually, I think it'd be better to step back and ask for some high-level explanation of what I should do/expect to see.
Authentication: When a request comes into the ServiceHost, it needs to get Authenticated. At this point in the pipeline, I don't need to know what they can do, just who they are. If a client submits both Windows credentials and a custom authentication ticket to a Contract/Binding on which I require just a custom authentication ticket, the service shouldn't be tricked into evaluating the wrong one. But perhaps at this stage in the WCF pipeline, the Contract hasn't been resolved so perhaps during this stage, all found identities/claims/tokens should be captured and selected among later. Is this right? How is this done?
Message Inspection: I have a custom MessageInspector which I'll still need for CORS support and OPTIONS request processing on some of the endpoints. I believe this happens after authentication and before authorization. In the case of OPTIONS, I set ref message
to null
and skip the Operation entirely, jumping straight to the BeforeSendReply
. Clients don't send the auth-token on a CORS OPTIONS preflight request and so I allow these requests unconditionally.
Authorization: Depending on the Contract, I want to require certain authentication mechanisms and ignore others. I believe some setup needs to prepare the Thread Principal and Operation Context principal to the correct value. It seems that multiple Authorization policies can be in play at once. How do they interact?
Operation: I want to be able to implement each operation assuming that the identity of the caller is authenticated using a supported authentication (by Contract, which I'm OK maintaining hard-coded somewhere ONCE) and get the validated caller identity and assert simple permissions checks againsts the verified Claims.
How do I achieve this (code preferred over config xml)?
Thanks!