Using WIF for securing REST Service
OAuth WRAP and SWT (Simple Web Token) have emerged as a standard way for employing claim-based-security to the REST services. Conceptually this model is very similar to the one used in SOAP world. A client goes to an issuer, authenticates itself using some token and gets back a different token (containing claims) in SWT format.
WIF provides a rich API and object model to claim-enable your .Net applications but currently it doesn’t natively support OAuth or SWT. In this post, I’ll show how you can extend WIF to bring claim-based-security to REST services. My goal is to implement this functionality in such a way that all the WIF goodness around claims-transformation (ClaimsAuthenticationManager), claim-based-authorization (ClaimsAuthorizationManager) etc can be used exactly the same way in the REST world.
I have achieved this by introducing an OAuth WRAP channel which sits in the WCF channel stack and perform an almost identical job to its SOAP counterpart WS-Security channel. I have implemented the OAuth WRAP channel using the interceptor API from the REST Starter Kit.
WebServiceHost2 host = new WebServiceHost2(typeof(TestService), new Uri(http://localhost:9090));
host.Interceptors.Add(new OAuthWrapSecurityChannel());
host.Open();
The OAuth channel takes care of extracting the token from the incoming message, running it through the validation pipeline (based on WIF SecurityTokenHandler framework), calling ClaimsAuthenticationManager, setting the WCF authorization context so that ClaimsAuthorizationManager can be called, and finally presenting the claims to the service method in the standard WIF way: Thread.CurrentPrincipal :)
Now with OAuth channel plugged in, if I call the service with a token issues by the Windows AppFabric ACS:
I get following output on the service side.
And from the service code, you would notice that standard WIF API is used to access incoming claims.
[WebGet]
string Hello()
{
Console.WriteLine("Hello called...");
Console.WriteLine("------------------");
var cp = Thread.CurrentPrincipal as IClaimsPrincipal;
if (cp != null)
{
foreach (var id in cp.Identities)
{
Console.WriteLine("Authentication Type: " + id.AuthenticationType);
Console.WriteLine("Is Authenticated: " + id.IsAuthenticated);
Console.WriteLine("Name: " + id.Name);
Console.WriteLine();
Console.WriteLine("Claims...");
foreach (var c in id.Claims)
{
Console.WriteLine(c.ClaimType + ": " + c.Value);
}
}
}
return "Hello, World";
}
WIF would be configured using exactly same configuration settings required for a SOAP service.
- <configuration>
- <configSections>
- <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
- </configSections>
- <system.serviceModel>
- <behaviors>
- <serviceBehaviors>
- <behavior>
- <federatedServiceHostConfiguration />
- <serviceDebug includeExceptionDetailInFaults="true" />
- </behavior>
- </serviceBehaviors>
- </behaviors>
- <extensions>
- <behaviorExtensions>
- <add name="federatedServiceHostConfiguration" type="Microsoft.IdentityModel.Configuration.ConfigureServiceHostBehaviorExtensionElement, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
- </behaviorExtensions>
- </extensions>
- </system.serviceModel>
- <!--WIF configuration-->
- <microsoft.identityModel>
- <service>
- <securityTokenHandlers>
- <add type="Microsoft.IdentityModel.OAuth.SWTSecurityTokenHandler, Microsoft.IdentityModel.OAuth, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
- </securityTokenHandlers>
- <issuerNameRegistry type="SampleService.MySimpleRegistry, SampleService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
- <claimsAuthorizationManager type="SampleService.MySimpleClaimsAuthorizationManager, SampleService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
- <audienceUris>
- <add value="http://localhost/" />
- </audienceUris>
- <issuerTokenResolver type="SampleService.WrapIssuerTokenResolver, SampleService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
- </service>
- </microsoft.identityModel>
- <startup>
- <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
- </startup>
- </configuration>
In summary, these extensions implement OAuth WRAP protocol head for Windows Identity Foundation and re-uses all the neat WIF APIs for deriving the service behaviour. In the next post, I’ll discuss how to create a local OAuth WRAP issuer using the SecurityTokenService WIF API.
Feel free to download the source and experiment.