WCF - Custom Credentials & Security Tokens - wcf

I'm fairly new to WCF development and have run into a couple problems whilst learning the framework. I have a service api which must support both REST & SOAP. So far this has been easy to implement especially with WCF4 and routing.
I am currently working on authorization and have managed to extend AuthorizationManager by creating two new manager classes: "ApiKeyAuthorizationManager" & "ApiKeyAndTokenAuthorizationManager"
Most of my services will require an ApiKey and Token (GUIDS) to be present; when initially authenticating, you simply need a valid ApiKey and password to receive a Token.
So far REST is working perfectly as the Authorization managers look to the query string to get ApiKey and/or Token.
So for example a service uri would look like:
*http://api.domain.com/Service/Operation/{someVariableValue}?ApiKey=GUID&Token=GUID
My problem now comes with authorizing SOAP service calls. I've done a little bit of research and have come to a few conclusions I wanted to verify are correct before implementing.
In order to authorize SOAP with custom credentials I should:
Create custom service token (MSDN)
Extend WCF by creating a custom SecurityTokenProvider, SecurityTokenAuthenticator, and SecurityTokenSerializer (MSDN)
Extend WCF by creating custom AuthorizationPolicies (MSDN)
Am I on the right track for this? Are all these steps needed to fit my scenario? Seems like so much customization for just verifying a credential made up of two GUIDs.
Thanks!
[EDIT #1]
This has been a very difficult task. Custom credentials and security tokens is nearly undocumented. Finding quality blog posts itself has proven near impossible. I've kept plugging away and am so close to have a working solution. I've even hit the same road blocks as described in this post.
When I try to access my service to uncover the wsdl or mex I receive this error:
The service encountered an error.
An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is:
System.InvalidOperationException: An exception was thrown in a call to a policy export extension.
Extension: System.ServiceModel.Channels.SymmetricSecurityBindingElement
Error: Specified argument was out of the range of valid values.
Parameter name: parameters ----> System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: parameters
at System.ServiceModel.Security.WSSecurityPolicy.CreateTokenAssertion(MetadataExporter exporter, SecurityTokenParameters parameters, Boolean isOptional)
at System.ServiceModel.Security.WSSecurityPolicy.CreateWsspSignedSupportingTokensAssertion(MetadataExporter exporter, Collection`1 signed, Collection`1 signedEncrypted, Collection`1 optionalSigned, Collection`1 optionalSignedEncrypted)
at System.ServiceModel.Security.WSSecurityPolicy.CreateWsspSupportingTokensAssertion(MetadataExporter exporter, Collection`1 signed, Collection`1 signedEncrypted, Collection`1 endorsing, Collection`1 signedEndorsing, Collection`1 optionalSigned, Collection`1 optionalSignedEncrypted, Collection`1 optionalEndorsing, Collection`1 optionalSignedEndorsing, AddressingVersion addressingVersion)
at System.ServiceModel.Security.WSSecurityPolicy.CreateWsspSupportingTokensAssertion(MetadataExporter exporter, Collection`1 signed, Collection`1 signedEncrypted, Collection`1 endorsing, Collection`1 signedEndorsing, Collection`1 optionalSigned, Collection`1 optionalSignedEncrypted, Collection`1 optionalEndorsing, Collection`1 optionalSignedEndorsing)
at System.ServiceModel.Channels.SecurityBindingElement.ExportSymmetricSecurityBindingElement(SymmetricSecurityBindingElement binding, MetadataExporter exporter, PolicyConversionContext policyContext)
at System.ServiceModel.Channels.SecurityBindingElement.ExportPolicy(MetadataExporter exporter, PolicyConversionContext context)
at System.ServiceModel.Description.MetadataExporter.ExportPolicy(ServiceEndpoint endpoint)
--- End of inner ExceptionDetail stack trace ---
at System.ServiceModel.Description.ServiceMetadataBehavior.MetadataExtensionInitializer.GenerateMetadata()
at System.ServiceModel.Description.ServiceMetadataExtension.EnsureInitialized()
at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.InitializationData.InitializeFrom(ServiceMetadataExtension extension)
at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.GetInitData()
at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.TryHandleDocumentationRequest(Message httpGetRequest, String[] queries, Message& replyMessage)
at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.ProcessHttpRequest(Message httpGetRequest)
at SyncInvokeGet(Object , Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
If you have any idea what might be causing this I'd love some help.
[EDIT #2]
It seems Microsoft doesn't want to update their samples to show how to allow wsdl support for custom credentials / tokens. See here.
Anyone have an idea how to get this working? What's the point of making a framework extensible if there's no documentation how to extend it?!?
[EDIT #3]
As stated in my comment below...
I have TransportWithMessageCredential working just fine using UserNameSecurityToken. Unfortunately, my services will end up requiring custom tokens when it's time to implement some more advanced features that I have planned.
What I'm looking for as an answer would be:
How to support custom service credentials and tokens with WSDL support?
Currently, following Microsoft's examples, you can only use custom credentials by using ChannelFactory and creating a custom binding on the client. I would rather not deal with that.
If this question remains unanswered, I'll keep upping the bounty. As soon as I can get this all working I'll write up a blog tutorial on all the steps required to creating a custom security solution.

I would advise you look to Windows Identity Foundation, and move in the direction of claim based security/federation. This model supports custom credentials much better as they are just a different set of claims.

Related

Accessing AdditionalContext of RequestSecurityToken from inside UserNameSecurityTokenHandler

I am creating a custom STS using a custom UserNameSecurityTokenHandler derived class. In the client, I am adding some additional information to the AdditionalContext property of the RequestSecurityToken that the Token Handler needs to fully authenticate (in addition to User Name & Password).
I assume that the RequestSecurityToken must be available to me somewhere in my Token Handler, but I cannot locate it. I've prowled through the code with Reflector, but that has not helped either.
How can I get at this information?
Thanks in advance.
David Mullin
It turns out that I was confusing two unrelated things. The UserNameSecurityTokenHandler class is for authenticating the UserName/Password Client Credentials, which then flows into the STS as the "Current Principal".
So, never mind.

Why WCF SoapFault responses are encrypted in some situations?

I am creating a WCF webservice whose requests/responses are supposed to be signed only.
For this, on ServiceContract attribute I have set
ProtectionLevel = ProtectionLevel.Sign
That works ok.
Due to requirements some SoapFaults are supposed to be thrown from service; two types of SoapFaults:
related to application
related to WS-Addressing (e.g. MessageID is missing)
For this I am using the normal of approach of dealing with SoafFaults: create an IErrorHandler in which a Message instance is created with MessageFault.CreateFault.
Almost all the returned SoapFaults are not encrypted (which is ok for me),
my question is why the ones with action="http://www.w3.org/2005/08/addressing/fault" or "http://www.w3.org/2005/08/addressing/soap/fault" are encrypted?
Check out http://msdn.microsoft.com/en-us/library/aa347791.aspx and http://msdn.microsoft.com/en-us/library/system.servicemodel.faultcontractattribute.aspx.
It states that
If you select a binding that enables security and you do not set the
ProtectionLevel property anywhere on the contract, all application
data will be encrypted and signed.
I guess that the build in types by default use this behaviour. You can verify this by looking at which exception is actually thrown.

WCF 4.0's analog to WCF REST Starter Kit's RequestInterceptor?

Does WCF 4.0 have an analog class/module/whatever to WCF REST Starter Kit's RequestInterceptor?
I'm back with an update.
I happen to value simplicity in code and after successfully solving this issue, I can't say I prefer it any more than the Query String method. Dropping a single call into each service endpoint that calls an AuthN method alongside the AuthZ method seems easier than some might believe.
Anyway, enough of the opinions...on to the solution. The solution is right under our eyes on Stackoverflow at this link but not well described in our context...so I will give credit to "user634119" for the sample code found here:
Headers in OperationContext
First, we need to add a serviceBehavior to our web.config file:
<behaviors>
<serviceBehaviors>
<behavior>
<serviceAuthenticationManager serviceAuthenticationManagerType="WCF.BasicAuthorization, WCF"></serviceAuthenticationManager>
<serviceAuthorization impersonateCallerForAllOperations="false" principalPermissionMode="Custom" serviceAuthorizationManagerType="WCF.BasicAuthentication, WCF">
</serviceAuthorization>
</behavior>
</serviceBehaviors>
</behaviors>
Next make a class (called BasicAuthorization as referenced in the serviceBehaviors block above):
//Authorize the call against the URI resource being requested...
public class BasicAuthorization : ServiceAuthorizationManager
{
public override bool CheckAccess(OperationContext operationContext,
ref Message message)
{
//some code
}
}
Next make an Authentication class:
// Authenticate the header signature as described in my previous post
public class BasicAuthentication : ServiceAuthenticationManager
{
public override ReadOnlyCollection<IAuthorizationPolicy> Authenticate(
ReadOnlyCollection<IAuthorizationPolicy> authPolicy, Uri listenUri,
ref Message message)
{
//some code
}
}
In the Authenticate method, use HttpRequestMessageProperty to pull the request header details out and perform the same 3 steps described in my first reply.
Eduardo, you asked: #carlosfigueira: Can I use it to implement an authentication subsystem?
I am working on that same issue and have at least one solution (described below) for you and an upcoming Authorization Header-based one (which I beleive is the one you are thinking of "intercepting").
The simplest way to secure a WCF 4 REST WebHttp programming model-based endpoint is this:
Issue a Shared Secret Key and an API Key to each client to use as credentials. The API key is really the same as a user name.
Run all endspoints over SSL to ensure you always have channel/message/data security
Require the clients to use the Shared Secret to generate a HMAC-SHA1 (or equiv) hash signature string that includes a timestamp and their API Key.
Require the client to pass all 3 of these as query strings parameters in every request:
Signature
Timestamp
API Key
Example: https://127.0.0.1/RestEndpoint?Sig={sigString}&ApiKey={apiKey}&TimeStamp={timeStamp}&All your other parameters here...
On your service side, implement an Authentication method that takes all 3 strings and then:
Looks up the API key and returns the client's shared secret that you have in a DB or somewhere else.
Compare the timestamp against DateTime.Now to make sure the request isn't more than 15 mins old to fend off replay attacks.
Using those 3 strings, recreate the signature string and compare yours to the one passed in by the client.
If they match, the requestor is authentic.
Now, the better way to do this is by using the HTTP Authorization Request Header to store those 3 strings and to have a global interceptor-ish process watch all requests. This would prevent the potential for an exposed endpoint without an authentication block (well, at least its less likely perhaps).
Problem with using the query string to carry all this info is the query string has a 2k max length (which varies by client/browser) and the query string gets really hard to read when debugging...but just get used to it.
Some think a more sophisticated way to do this is a STS model where you require the client to pass these 3 authentication strings to an Security Token Service endpoint. The response message would pass back a session token which the client would pass in on each call in lieu of the 3 strings. It is true that for the client there is no need to generate an HMAC hash signature on each call, but the server side must still authenticate the token and the session concept fouls up the clean RESTful stateless behavior.
I will do my best to post code blocks that implement both the query string and the auth header methodologies.
There isn't anything which maps 1-1 to it, but you can use an IDispatchMessageInspector from WCF core to implement most of the scenarios for which the RequestInspector would do. The post at http://blogs.msdn.com/b/carlosfigueira/archive/2011/04/19/wcf-extensibility-message-inspectors.aspx has some detailed information about the message inspectors.

How do I do username/password authentication in WCF, with session affinity?

It seems like I'm barking up the wrong tree when asking this question, this question and this question.
I need to authenticate users against a custom API (in COM), and I need to keep that custom API (the COM object) alive (for that user) for future WCF calls. During authentication against that custom API, I can get back a list of custom-defined roles. I'd also like to use these for authorization of the service methods.
Moreover, I need to be able to revoke the user's session remotely. This is triggered by an event raised by the COM API.
I've got a custom UserNamePasswordValidator, but it appears that this has no mechanism for correctly setting a custom principal, so it looks like I'm heading in the wrong direction.
How do I do these three things?
You can handle authentication completely in your service. Create service contract similar to:
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IService
{
// All your operations marked with [OperationContract(IsInitiating=false, IsTerminating=false)]
// Two additional operations
[OperationContract(IsInitiating=true, IsTerminating=false)]
void Login(string user, string password);
[OperationContract(IsInitiating=false, IsTerminating=true)]
void Logout();
}
Service implementing this contract has to have PerSession instancing. Implement authentication in Login method and store COM object in local field. When new client want to use such service he has to first call the Login method. So all your instances will be properly authenticated and they will store their instance of COM object.
You can also register InstanceContext and COM object to some global class which will deal with forcibly killing service instance. This will probably require some research to make it work.
Make sure that you use some secure binding (encryption) because you will send user name and password as a plain text.

WCF service consuming passively issued SAML token

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();
}