1
votes

I'm struggling on a setup mentioned in the subject line and am wondering if someone can help me.

Essentially, what I have is a WCF service and I want to achieve that the user can authenticate against the ACS using a custom login page (using the javascript with required information from ACS).

After doing that the user should get redirected to the WCF service using the provided SWT token. I am using the SimpleWebTokenHandler as a basis for the SWT token handling, but I'm not sure it's playing any role in this.

Here's the Web.config I'm running

<configuration>
    <configSections>
        <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
        <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral" />
    </configSections>
    ...
    <system.serviceModel>
        <diagnostics>
        </diagnostics>
        <services>
            <service name="WcfWifSwtAcs.Service1">
                <endpoint address="xmlService" binding="webHttpBinding" bindingConfiguration="" behaviorConfiguration="restPoxBehaviour" name="xmlServiceEndpoint" contract="WcfWifSwtAcs.IService1" />
            </service>
        </services>
        <behaviors>
            <endpointBehaviors>
                <behavior name="restPoxBehaviour">
                    <webHttp helpEnabled="true" />
                </behavior>
            </endpointBehaviors>
            <serviceBehaviors>
                <behavior>
                    <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="true" />
                    <serviceCredentials useIdentityConfiguration="true">
                        ...
                    </serviceCredentials>
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <protocolMapping>
            <add scheme="http" binding="ws2007FederationHttpBinding" />
        </protocolMapping>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
        <bindings>
            <ws2007FederationHttpBinding>
                <binding name="">
                    <security mode="Message">
                        <message 
                            issuedTokenType="http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0">
                            <issuerMetadata address="https://xxxx.accesscontrol.windows.net/v2/wstrust/13/certificate/mex" />
                        </message>
                    </security>
                </binding>
            </ws2007FederationHttpBinding>
        </bindings>
    </system.serviceModel>
    <system.webServer>
    ...
    </system.webServer>
    <system.identityModel>
        <identityConfiguration>
            <audienceUris>
                <add value="http://localhost:56782/Service1.svc" />
            </audienceUris>
            <issuerNameRegistry type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
                <trustedIssuers>
                    <add thumbprint="XXX" name="xxx.accesscontrol.windows.net" />
                </trustedIssuers>
            </issuerNameRegistry>
        <issuerTokenResolver type="SimpleWebToken.CustomIssuerTokenResolver, WcfWifSwtAcs" />
            <securityTokenHandlers>
                <clear/>
                <add type="SimpleWebToken.SimpleWebTokenHandler, WcfWifSwtAcs"/>
            </securityTokenHandlers>
        </identityConfiguration>
    </system.identityModel>
</configuration>

Now I can see, that the authentication happens and that the browser is redirected with the body to the service. I can also see that the SimpleWebToken handler get's instantiated and the token type URI is being requested. But that's almost all that happens. No actual token handling verification and whatsoever is happnening.

This is the token that get's sent to the service (after parsing).

wa=wsignin1.0&
wresult=
<t:RequestSecurityTokenResponse 
xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:Lifetime>
    <wsu:Created 
        xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2013-02-13T23:14:30.159Z</wsu:Created>
    <wsu:Expires
        xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2013-02-13T23:24:30.159Z</wsu:Expires>
</t:Lifetime>
<wsp:AppliesTo 
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
    <EndpointReference
        xmlns="http://www.w3.org/2005/08/addressing">
        <Address>http://localhost:56782/Service1.svc</Address>
    </EndpointReference>
</wsp:AppliesTo>
<t:RequestedSecurityToken>
    <wsse:BinarySecurityToken 
        wsu:Id="uuid:58e2fb15-dd1a-40bd-8ff0-ae24e22e6efe" 
        ValueType="http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0"
        EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
        xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"          xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        BASE64 DATA==
    </wsse:BinarySecurityToken>
</t:RequestedSecurityToken>
<t:TokenType>http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0</t:TokenType>
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
</t:RequestSecurityTokenResponse>

Service itself is braindead simple, with following signature.

[OperationContract]
[WebInvoke(UriTemplate = "/GetData/{id}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
string GetData(string id);

Any ideas? I've been verifying that the uri's, hostnames, thumbprints etc. are all valid. Also the service tracing doesn't really show up anything that is related either to token handling or exceptions in the token verification.

Somehow it almost seems that the token doesn't get even passed to the handler. At least all the claims and other authentication information is missing (null).

I would appreciate if someone points me to a direction of either where can I debug or if I'm missing something really obvious (which might also always be the case).

P.S. I know I could achieve it with custom authentication modules and whatsoever, I'd rather get it running with WIF (it's becoming fundamental as I've spend more time on this as I really wanted and I'm very stubborn :p).

2
Where did you get the SimpleWebTokenHandler that you're using? Keep in mind that when ACS issues a SWT over an XML protocol like WS-Trust it wraps it in a <BinarySecurityToken> element, which the token handler you're using has to understand. If I had to guess, the handler you're using doesn't recognize this as a SWT.Oren Melzer
Hi, I've been using the sample from here code.msdn.microsoft.com/vstudio/Custom-Token-ddce2f55 Now the interesting part then would be that what is the URI that is actually used to compare the token content to the one that the token handler returns. Both the t:TokenType and ValueType would make me think that it is schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0 , which I'm using. I've been unable to get any token related traces or debugs neither :(Laazik

2 Answers

1
votes

Soo, dedication will bring one to a solution. Although I initially thought that this can't be done, it's apparent that it actually can. I'll put the solution here, as maybe there are other people who find it useful.

First of all, WCF REST services are using webHttpBinding, which according to MS documentation does not support the Windows Identity Foundation and claims handling in the pipeline. Actually it does. Not in the WCF pipeline, but as the IIS module in web authentication flow.

First, you need to add the following modules to Web.config file.

<system.webServer>
    <modules runManagedModulesForAllRequests="true">
        <add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" preCondition="managedHandler" />
        <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" preCondition="managedHandler" />
    </modules>
 </system.webServer>

There's a caveat tho. You need still the <configSections> from my original posting. The problem is that you need, in VisualStudio, to mark the System.IdentyModel* assemblies as CopyLocal items (in the properties window). Otherwise you'll get some cryptic exception that assembly cannot be loaded for the configuration section. NB! It only happens if you are loading these two modules and doesn't happen when those modules are not getting loaded. Didn't have any will to investigate that thing further, perhaps someone knows better what's the cause there.

Next if for any reason you plan to use the SWT token handling sample from MS WIF code, there are a couple of bugs that need to be fixed, otherwise the token parsing just won't happen or you will get invalid signatures out of the token verification.

SimpleWebToken.cs you need to fix the SwtBaseTime as it is initialized incorrectly and the security token creation fails afterwards:

From public static DateTime SwtBaseTime = new DateTime( 1970, 1, 1, 0, 0, 0, 0 ); // per SWT psec To

public static DateTime SwtBaseTime = new DateTime( 1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc ); // per SWT psec

SimpleWebTokenHandler.cs you need to fix the casing of the following values:

From

const string BinarySecurityToken = "binarySecurityToken";
const string ValueType = "valueType";

To

const string BinarySecurityToken = "BinarySecurityToken";
const string ValueType = "ValueType";

CustomIssuerTokenResolver.cs you need to fix the key that is created as it's initalized with a UTF8 bytes, but it should actually get initialized with decoded Base64 bytes:

From

key = new InMemorySymmetricSecurityKey(UTF8Encoding.UTF8.FromBase64String(base64Key));

To

key = new InMemorySymmetricSecurityKey(System.Convert.FromBase64String(base64Key));

After you've fixed all this, everything sits in place. The authenticators and authorizators are getting called and voilà, suddenly you have a WCF Service exposed as REST endpoint and all the claims etc. are also working.

0
votes

I think your issue may be with the SWTTokenHandler in this sample: http://code.msdn.microsoft.com/vstudio/Custom-Token-ddce2f55

In CanReadToken(), it checks to see if the token is a BinarySecurityToken of type SWT:

if ( reader.IsStartElement( BinarySecurityToken )
&& ( reader.GetAttribute( ValueType ) == SimpleWebTokenConstants.ValueTypeUri ) )

But the constant BinarySecurityToken is defined as:

const string BinarySecurityToken = "binarySecurityToken";

Note the lower-case "b". XML elements are case sensitive, and the actual element is "BinarySecurityToken" with a capital B. This will cause the handler to return false in CanReadToken(), causing WIF to believe it doesn't have a handler registered for this token type.