{"id":2348,"date":"2013-07-30T13:59:15","date_gmt":"2013-07-30T20:59:15","guid":{"rendered":"http:\/\/www.cloudidentity.com\/blog\/?p=2348"},"modified":"2013-07-31T08:07:07","modified_gmt":"2013-07-31T15:07:07","slug":"securing-a-web-api-with-windows-server-2012-r2-adfs-and-katana","status":"publish","type":"post","link":"https:\/\/www.cloudidentity.com\/blog\/2013\/07\/30\/securing-a-web-api-with-windows-server-2012-r2-adfs-and-katana\/","title":{"rendered":"Securing a Web API with Windows Server 2012 R2 ADFS and Katana"},"content":{"rendered":"<p>Last week I wrote a <a href=\"https:\/\/www.cloudidentity.com\/blog\/2013\/07\/23\/securing-a-web-api-with-windows-azure-ad-and-katana\/\">post<\/a> about how to use <a href=\"http:\/\/katanaproject.codeplex.com\/\">Katana<\/a> and Windows Azure AD to secure an MVC4 Web API, and showed how to use AAL to build a Windows Store client in just few lines of code.<br \/>This week I\u2019d like to show you how you can apply the exact same approach when <strong><u>using the new OAuth2 &amp; JWT support in Windows Server 2012 R2 ADFS<\/u><\/strong>; once again, this was one of the most frequent requests after my <a href=\"http:\/\/channel9.msdn.com\/Events\/Build\/2013\/3-518\">\/\/BUILD\/ session<\/a> last month hence I hope this will make you guys happy <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" style=\"border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none\" alt=\"Smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/wlEmoticon-smile2.png\">. <\/p>\n<p>Note: I am writing this post mostly for unblocking some people who asked for help right now, and for demonstrating the use of Katana in Web API scenarios a where the directory is kept on-premises. The ADFS guys will publish lots of official documentation in the coming weeks\/months, and it goes without saying that you should always refer to the official documentation. <\/p>\n<h2>Walkthrough<\/h2>\n<p>The scenario we want to implement is pretty simple: we want to restrict access to an MVC4 Web API to the users of a given on-premises AD instance, which happens to be using Windows Server 2012 R2 ADFS (just \u201cADFS\u201d from now on). Furthermore, we want to expose the Web API to the user via a Windows Store application.<\/p>\n<p>This is the exact same <a href=\"https:\/\/www.cloudidentity.com\/blog\/2013\/07\/23\/securing-a-web-api-with-windows-azure-ad-and-katana\/\">scenario we discussed last week<\/a>, and the implementation is nearly the same; hence instead of writing the same things I am going to ask you to read <a href=\"https:\/\/www.cloudidentity.com\/blog\/2013\/07\/23\/securing-a-web-api-with-windows-azure-ad-and-katana\/\">last weeks\u2019 post<\/a> first. Once you have done that, and you have working bits in Visual Studio, you can come back here and I\u2019ll show you how to modify things to make the ADFS scenario work. <br \/>Another prerequisite is that you must have a Windows Server 2012 R2 ADFS instance available: you can download the Preview <a href=\"http:\/\/technet.microsoft.com\/en-us\/evalcenter\/dn205286.aspx\">here<\/a>.<\/p>\n<h3>Setting Up the Web API Project<\/h3>\n<p>Alrighty, do you have the Windows Azure AD based scenario working? Fantastic. Let\u2019s start to modify things to work with ADFS starting form the Web API project.<\/p>\n<p>The good news is that in a short you will not have to change any code at all in order to switch from Windows Azure AD to ADFS and vice versa: Katana will include middleware that will work with both. The bad news is that today the middleware we used for validating incoming tokens is hardwired to Windows Azure AD. However do not despair, as working around it is pretty straightforward.<\/p>\n<p>Modify your Startup.cs class as following:<\/p>\n<pre class=\"csharpcode\"><span class=\"kwrd\">public<\/span> <span class=\"kwrd\">class<\/span> Startup\r\n{\r\n    <span class=\"kwrd\">public<\/span> <span class=\"kwrd\">void<\/span> Configuration(IAppBuilder app)\r\n    {\r\n        app.UseWindowsAzureBearerToken(<span class=\"kwrd\">new<\/span> WindowsAzureJwtBearerAuthenticationOptions()\r\n        {\r\n            Audience = <span class=\"str\">\"https:\/\/contoso100.com\/API\/KatanaADFS_WebAPISample\"<\/span>,\r\n            Tenant = <span class=\"str\">\"sts.contoso100.com\"<\/span>,\r\n            MetadataResolver = <span class=\"kwrd\">new<\/span> ADFSMetadataResolver()\r\n        });\r\n    }\r\n}\r\n<\/pre>\n<style type=\"text\/css\">.csharpcode, .csharpcode pre\n{\n\tfont-size: small;\n\tcolor: black;\n\tfont-family: consolas, \"Courier New\", courier, monospace;\n\tbackground-color: #ffffff;\n\t\/*white-space: pre;*\/\n}\n.csharpcode pre { margin: 0em; }\n.csharpcode .rem { color: #008000; }\n.csharpcode .kwrd { color: #0000ff; }\n.csharpcode .str { color: #006080; }\n.csharpcode .op { color: #0000c0; }\n.csharpcode .preproc { color: #cc6633; }\n.csharpcode .asp { background-color: #ffff00; }\n.csharpcode .html { color: #800000; }\n.csharpcode .attr { color: #ff0000; }\n.csharpcode .alt \n{\n\tbackground-color: #f4f4f4;\n\twidth: 100%;\n\tmargin: 0em;\n}\n.csharpcode .lnum { color: #606060; }\n<\/style>\n<p>What did I do here? I changed the Audience and Tenant values to refer to my ADFS scenario, and I added an assignment for the MetadataResolver property. What is the MetadataResolver and how is it used in our scenario? Well, the MetadataResolver is the class used by the middleware to reach out to the authority issuing tokens for your service, and acquire the coordinates that must be used for validating incoming tokens: signing key, issuer, and so on. In the Windows Azure AD scenario we did not have to specify it, as the WindowsAzureBearerToken middleware uses a class available out of the box which is hardwired to use the metadata path of Windows Azure AD tenants. Hence, if we want to work around the current limitation all we need to do is providing a MetadataResolver which goes to ADFS instead. Once again, soon this workaround will no longer be necessary. <\/p>\n<p>Basically all you need to do is creating a new class in the file ADFSMetadataResolver.cs and paste the following:<\/p>\n<pre class=\"csharpcode\"><span class=\"kwrd\">using<\/span> Microsoft.Owin.Security.WindowsAzure;\r\n<span class=\"kwrd\">using<\/span> System;\r\n<span class=\"kwrd\">using<\/span> System.Collections.Concurrent;\r\n<span class=\"kwrd\">using<\/span> System.Collections.Generic;\r\n<span class=\"kwrd\">using<\/span> System.Globalization;\r\n<span class=\"kwrd\">using<\/span> System.IdentityModel.Metadata;\r\n<span class=\"kwrd\">using<\/span> System.IdentityModel.Tokens;\r\n<span class=\"kwrd\">using<\/span> System.Linq;\r\n<span class=\"kwrd\">using<\/span> System.Security.Cryptography.X509Certificates;\r\n<span class=\"kwrd\">using<\/span> System.ServiceModel.Security;\r\n<span class=\"kwrd\">using<\/span> System.Xml;\r\n\r\n\r\n<span class=\"kwrd\">namespace<\/span> KatanaADFS_WebAPISample\r\n{\r\n    <span class=\"kwrd\">public<\/span> <span class=\"kwrd\">class<\/span> ADFSMetadataResolver : IMetadataResolver\r\n    {\r\n        <span class=\"kwrd\">internal<\/span> <span class=\"kwrd\">class<\/span> EndpointMetadata\r\n        {\r\n            <span class=\"kwrd\">public<\/span> DateTime ExpiresOn { get; set; }\r\n            <span class=\"kwrd\">public<\/span> <span class=\"kwrd\">string<\/span> Issuer { get; set; }\r\n            <span class=\"kwrd\">public<\/span> IList&lt;SecurityToken&gt; SigningTokens { get; set; }\r\n        }\r\n\r\n        <span class=\"kwrd\">private<\/span> <span class=\"kwrd\">const<\/span> <span class=\"kwrd\">string<\/span> SecurityTokenServiceAddressFormat = <span class=\"str\">\"https:\/\/sts.contoso100.com\/federationmetadata\/2007-06\/federationmetadata.xml\"<\/span>;\r\n\r\n        <span class=\"kwrd\">private<\/span> <span class=\"kwrd\">static<\/span> <span class=\"kwrd\">readonly<\/span> XmlReaderSettings _SafeSettings = <span class=\"kwrd\">new<\/span> XmlReaderSettings { XmlResolver = <span class=\"kwrd\">null<\/span>, DtdProcessing = DtdProcessing.Prohibit, ValidationType = ValidationType.None };\r\n\r\n        <span class=\"kwrd\">private<\/span> ConcurrentDictionary&lt;<span class=\"kwrd\">string<\/span>, EndpointMetadata&gt; _metadata = <span class=\"kwrd\">new<\/span> ConcurrentDictionary&lt;<span class=\"kwrd\">string<\/span>, EndpointMetadata&gt;();\r\n\r\n        <span class=\"kwrd\">public<\/span> ADFSMetadataResolver()\r\n        {\r\n            CacheLength = <span class=\"kwrd\">new<\/span> TimeSpan(1, 0, 0, 0);\r\n        }\r\n\r\n        <span class=\"kwrd\">public<\/span> TimeSpan CacheLength\r\n        {\r\n            get;\r\n            set;\r\n        }\r\n\r\n        <span class=\"kwrd\">public<\/span> <span class=\"kwrd\">string<\/span> GetIssuer(<span class=\"kwrd\">string<\/span> tenant)\r\n        {\r\n            <span class=\"kwrd\">return<\/span> GetMetadata(tenant).Issuer;\r\n        }\r\n\r\n        <span class=\"kwrd\">public<\/span> IList&lt;SecurityToken&gt; GetSigningTokens(<span class=\"kwrd\">string<\/span> tenant)\r\n        {\r\n            <span class=\"kwrd\">return<\/span> GetMetadata(tenant).SigningTokens;\r\n        }\r\n\r\n        <span class=\"kwrd\">private<\/span> EndpointMetadata GetMetadata(<span class=\"kwrd\">string<\/span> tenant)\r\n        {\r\n            <span class=\"kwrd\">if<\/span> (!_metadata.ContainsKey(tenant) ||\r\n                _metadata[tenant].ExpiresOn &lt; DateTime.Now)\r\n            {\r\n                <span class=\"kwrd\">using<\/span> (var metaDataReader = XmlReader.Create(<span class=\"kwrd\">string<\/span>.Format(CultureInfo.InvariantCulture, SecurityTokenServiceAddressFormat, tenant), _SafeSettings))\r\n                {\r\n                    var endpointMetadata = <span class=\"kwrd\">new<\/span> EndpointMetadata();\r\n                    var serializer = <span class=\"kwrd\">new<\/span> MetadataSerializer()\r\n                    {\r\n                        CertificateValidationMode = X509CertificateValidationMode.None\r\n                    };\r\n\r\n                    MetadataBase metadata = serializer.ReadMetadata(metaDataReader);\r\n                    var entityDescriptor = (EntityDescriptor)metadata;\r\n\r\n                    <span class=\"kwrd\">if<\/span> (!<span class=\"kwrd\">string<\/span>.IsNullOrWhiteSpace(entityDescriptor.EntityId.Id))\r\n                    {\r\n                        endpointMetadata.Issuer = entityDescriptor.EntityId.Id;\r\n                    }\r\n\r\n                    var tokens = <span class=\"kwrd\">new<\/span> List&lt;SecurityToken&gt;();\r\n                    var stsd = entityDescriptor.RoleDescriptors.OfType&lt;SecurityTokenServiceDescriptor&gt;().First();\r\n                    <span class=\"kwrd\">if<\/span> (stsd == <span class=\"kwrd\">null<\/span>)\r\n                    {\r\n                        <span class=\"kwrd\">throw<\/span> <span class=\"kwrd\">new<\/span> InvalidOperationException(<span class=\"str\">\"No SecurityTokenServiceType descriptor in metadata.\"<\/span>);\r\n                    }\r\n\r\n                    IEnumerable&lt;X509RawDataKeyIdentifierClause&gt; x509DataClauses = stsd.Keys.Where(key =&gt; key.KeyInfo != <span class=\"kwrd\">null<\/span> &amp;&amp; (key.Use == KeyType.Signing || key.Use == KeyType.Unspecified)).Select(key =&gt; key.KeyInfo.OfType&lt;X509RawDataKeyIdentifierClause&gt;().First());\r\n                    tokens.AddRange(x509DataClauses.Select(token =&gt; <span class=\"kwrd\">new<\/span> X509SecurityToken(<span class=\"kwrd\">new<\/span> X509Certificate2(token.GetX509RawData()))));\r\n\r\n                    endpointMetadata.SigningTokens = tokens.AsReadOnly();\r\n                    endpointMetadata.ExpiresOn = DateTime.Now.Add(CacheLength);\r\n\r\n                    <span class=\"kwrd\">lock<\/span> (_metadata)\r\n                    {\r\n                        _metadata[tenant] = endpointMetadata;\r\n                    }\r\n                }\r\n            }\r\n\r\n            <span class=\"kwrd\">return<\/span> _metadata[tenant];\r\n        }<br>    }\r\n}<\/pre>\n<style type=\"text\/css\">.csharpcode, .csharpcode pre\n{\n\tfont-size: small;\n\tcolor: black;\n\tfont-family: consolas, \"Courier New\", courier, monospace;\n\tbackground-color: #ffffff;\n\t\/*white-space: pre;*\/\n}\n.csharpcode pre { margin: 0em; }\n.csharpcode .rem { color: #008000; }\n.csharpcode .kwrd { color: #0000ff; }\n.csharpcode .str { color: #006080; }\n.csharpcode .op { color: #0000c0; }\n.csharpcode .preproc { color: #cc6633; }\n.csharpcode .asp { background-color: #ffff00; }\n.csharpcode .html { color: #800000; }\n.csharpcode .attr { color: #ff0000; }\n.csharpcode .alt \n{\n\tbackground-color: #f4f4f4;\n\twidth: 100%;\n\tmargin: 0em;\n}\n.csharpcode .lnum { color: #606060; }\n<\/style>\n<p>\u2026an that\u2019s all you need to modify the Web API to accept tokens from ADFS.<\/p>\n<h3>Setting Up the Web API in ADFS<\/h3>\n<p>Next, we are going to tell ADFS about our Web API.<\/p>\n<p>As mentioned earlier, you\u2019ll need to set up your own instance: I was lucky to have the ADFS guys themselves to set one for me! (Once again, you can get the Windows Server 2012 R2 Preview bits <a href=\"http:\/\/technet.microsoft.com\/en-us\/evalcenter\/dn205286.aspx\">here<\/a>. ADFS is one of the features on board)<\/p>\n<p>You manage the new ADFS just like the old one, via MMC console. You can start it form the Server Manager as shown below.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image20.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb20.png\" width=\"640\" height=\"308\"><\/a><\/p>\n<p>The UI offers the familiar experience you learned to use with 2.0; and just like in 2.0, registering a resource in ADFS is done by creating a \u201crelying party trust\u201d. To do that, you can hit \u201cadd relying party trust\u201d on the action pane at any time.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image21.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb21.png\" width=\"640\" height=\"300\"><\/a><\/p>\n<p>You\u2019ll be presented with the usual RP trust wizard, with some small changes here and there.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image22.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb22.png\" width=\"591\" height=\"480\"><\/a><\/p>\n<p>Hit Start.<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image23.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb23.png\" width=\"598\" height=\"480\"><\/a><\/p>\n<p>We are going to create the resource entry manually, there\u2019s no metadata describing the Web API. Choose \u201cEnter data about the relying party manually\u201d.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image24.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb24.png\" width=\"595\" height=\"480\"><\/a><\/p>\n<p>The first step gathers the name you want to assign to the rp trust entry. Needless to say, pick something that you\u2019ll recognize later! <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" style=\"border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none\" alt=\"Smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/wlEmoticon-smile2.png\"><\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image25.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb25.png\" width=\"595\" height=\"480\"><\/a><\/p>\n<p>The subsequent step gives you the chance of producing an ADFS1.x compatible RP entry; that\u2019s definitely not the case here. Pick \u201cAD FS profile\u201d and move forward.<\/p>\n<p>Skip through the next screen and leave all of the certificate related features as is. You\u2019ll land on the Configure URL screen.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image26.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb26.png\" width=\"594\" height=\"480\"><\/a><\/p>\n<p>A relying party trust entry can be used to engage with the RP via multiple protocols. The screen offers WS-Federation and SAML P: we don\u2019t need either of those, and the OAuth2 endpoints are automatically enabled. Leave everything as is and click Next.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image27.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb27.png\" width=\"602\" height=\"480\"><\/a><\/p>\n<p>Now, this part is very important: you need to provide the identifier that will be used in the OAuth2 flows to indicate that the resource being accessed is your Web API, so that ADFS will know what settings should be apply.<br \/>Choose a URI that is representative of your service: here I am picking one analogous to the one I used for Windows Azure AD, modified to indicate that this resource is protected by ADFS, but I could have used exactly the same identifier if I would have chosen to. Add your identifier, click add and click next.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image28.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb28.png\" width=\"597\" height=\"480\"><\/a><\/p>\n<p>The next screen is new with Windows Server 2012 R2: it allows you to configure multi-factor authentication settings for your RP. It is an *awesome* feature, that I know many of you have been eagerly waiting for, but for the sake of simplicity I\u2019ll ignore it here. Leave everything as is and click Next.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image29.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb29.png\" width=\"592\" height=\"480\"><\/a><\/p>\n<p>The next screen allows you to establish the baseline policy for the resource: are all users allowed by default to get a token for this resource, or is the opposite true? Keep the \u201cPermit\u201d policy and move forward.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image30.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb30.png\" width=\"596\" height=\"480\"><\/a><\/p>\n<p>That\u2019s it, your RP trust entry is now in! <\/p>\n<p>As usual, a brand new RP trust does not issue any claims of its own: you need to tell ADFS which claims should be issued for your Web API. The wizard offers to open the claim rules dialog: hit close and you\u2019ll be presented with the UI you need to add rules to your RP trust.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image31.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb31.png\" width=\"438\" height=\"480\"><\/a><\/p>\n<p>ADFS supports various rule types, a topic worth of multiple posts in itself. Here we\u2019ll stick to the bare minimum by adding one issuance rule. Click \u201cAdd Rule..\u201d.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image32.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb32.png\" width=\"598\" height=\"480\"><\/a><\/p>\n<p>You\u2019ll land on another wizard, designed to help you to easily define claim rules. We are going to do something really straightforward, issue an email claim sourced form the user attributes kept in AD. select the \u201cSend LDAP Attributes as Claims\u201d option and click next.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image33.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb33.png\" width=\"594\" height=\"480\"><\/a><\/p>\n<p>Give a meaningful name to the rule, pick Active Directory as the attribute store, and pick the email entry on both the LDAP attribute and outgoing claim type dropdowns.<\/p>\n<p>Hit finish and then hit OK until all dialogs are gone. Congratulations: your Web API is now configured in ADFS.<\/p>\n<h3>Setting Up a Test Client Project in ADFS and in Visual Studio<\/h3>\n<p>Now that we took care of the Web API, all that\u2019s left is modifying &amp; registering the client with ADFS.<\/p>\n<p>Now, here there\u2019s the kicker: if you\u2019d want, you could leave the client project *exactly* as we developed it in the Windows Azure AD scenario: AAL offers you the exact same methods signature whether you connect to Windows Azure AD or ADFS. However there are things that you can do on the client that will prepare your app to take advantage of some extra ADFS features, such as workplace join related access control. I won\u2019t be talking about those in this walkthrough, however it is worth it to do all the due diligence at this point so that you won\u2019t have to revisit the client setup once you\u2019ll want to learn more about those features. That said, let\u2019s get to work.<\/p>\n<h4>Obtaining the MS-APP:\/\/ Value for the Client<\/h4>\n<p>As for the Windows Azure AD scenario, we need to register the client with our authority (in this case ADFS) before being able to request tokens from it. A key difference between the two scenarios is that whether Windows Azure AD requires you to explicitly allow a client to call a given Web API, in ADFS every registered client can call every registered Web API (modulo access policies, of course).<\/p>\n<p>Various ADFS features are available to Windows Store applications only if they use the WebAuthenticationBroker in SSO mode (if you are not familiar with the concept: backgrounder <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/apps\/jj856909.aspx\">here<\/a>). We\u2019ll see in a moment what that means for AAL, but for now this has a very practical consequence on the registration process: the client must use its ms-app:\/\/appSID address as returnURI, hence we need to find out that value before being able to register the client with ADFS.<br \/>The most common way of doing so is to temporarily add the following snippet in your Windows Store app (OnNavigatedTo is a good candidate), put a breakpoint and wait to hit that.<\/p>\n<pre class=\"csharpcode\">Uri redirectURI = \r\n Windows.Security.Authentication.Web.WebAuthenticationBroker.GetCurrentApplicationCallbackUri();<\/pre>\n<style type=\"text\/css\">.csharpcode, .csharpcode pre\n{\n\tfont-size: small;\n\tcolor: black;\n\tfont-family: consolas, \"Courier New\", courier, monospace;\n\tbackground-color: #ffffff;\n\t\/*white-space: pre;*\/\n}\n.csharpcode pre { margin: 0em; }\n.csharpcode .rem { color: #008000; }\n.csharpcode .kwrd { color: #0000ff; }\n.csharpcode .str { color: #006080; }\n.csharpcode .op { color: #0000c0; }\n.csharpcode .preproc { color: #cc6633; }\n.csharpcode .asp { background-color: #ffff00; }\n.csharpcode .html { color: #800000; }\n.csharpcode .attr { color: #ff0000; }\n.csharpcode .alt \n{\n\tbackground-color: #f4f4f4;\n\twidth: 100%;\n\tmargin: 0em;\n}\n.csharpcode .lnum { color: #606060; }\n<\/style>\n<p>Once there, you can copy the value of the ms-app:\/\/ URI, stop the debugger and remove the code.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image34.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb34.png\" width=\"640\" height=\"105\"><\/a><\/p>\n<p>Very good! With the ms-app:\/\/ value in place, we can now proceed with the registration. At the cost of being pedantic, I\u2019ll highlight that all of the above is not a strict requirement: I could have used an arbitrary URI (like I did for the Windows Azure AD case) but then I would not have been able to take advantage of the full gamut of new ADFS features.<\/p>\n<h4>Registering a Client in ADFS<\/h4>\n<p>You might have noticed from the screenshots of the ADFS MMC that there was no entry for clients. The reason is that clients ar enot managed throuhg UI, but via PowerShell. You can use the Server Manager to access the ADFS PowerShell module and open a prompt.<\/p>\n<p>The cmdlet we want to use is Add-AdfsClient. It is the most straightforward cmdlet you\u2019ll ever find in this space <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" style=\"border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none\" alt=\"Smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/wlEmoticon-smile2.png\">! Usage below:<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image35.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb35.png\" width=\"640\" height=\"208\"><\/a><\/p>\n<p>Basically we just need to pass the client name, the clientID (I will reuse the same GUID I used for the Windows Azure AD scenario) and the redirect URI (this is where you\u2019ll need the ms-app:\/\/).<br \/>Right after the Add-AdfsClient client I call Get-AdfsClient to ensure that the entry looks like I expect.<\/p>\n<pre class=\"csharpcode\">PS C:\\Users\\vittorio&gt; Add-ADFSClient -Name <span class=\"str\">\"KatanaADFS_W8ClientSample\"<\/span> -ClientI\r\nd <span class=\"str\">\"3fb2a37f-4ced-409c-937c-dddd776f4dfd\"<\/span> -RedirectUri <span class=\"str\">\"ms-app:\/\/s-1-15-2-1101140\r\n336-4090662585-1905587327-262951538-2732256205-1306401843-4235927180\/\"<\/span>\r\nPS C:\\Users\\vittorio&gt; Get-AdfsClient -ClientId <span class=\"str\">\"3fb2a37f-4ced-409c-937c-dddd776\r\nf4dfd\"<\/span>\r\n\r\nRedirectUri : {ms-app:<span class=\"rem\">\/\/s-1-15-2-1101140336-4090662585-1905587327-262951538-273<\/span>\r\n              2256205-1306401843-4235927180\/}\r\nName        : KatanaADFS_W8ClientSample\r\nDescription :\r\nClientId    : 3fb2a37f-4ced-409c-937c-dddd776f4dfd\r\nBuiltIn     : False\r\nEnabled     : True\r\nClientType  : Public\r\n\r\n<\/pre>\n<p>Believe it or not, that\u2019s it. your client is now registered.<\/p>\n<pre class=\"csharpcode\">&nbsp;<\/pre>\n<style type=\"text\/css\">.csharpcode, .csharpcode pre\n{\n\tfont-size: small;\n\tcolor: black;\n\tfont-family: consolas, \"Courier New\", courier, monospace;\n\tbackground-color: #ffffff;\n\t\/*white-space: pre;*\/\n}\n.csharpcode pre { margin: 0em; }\n.csharpcode .rem { color: #008000; }\n.csharpcode .kwrd { color: #0000ff; }\n.csharpcode .str { color: #006080; }\n.csharpcode .op { color: #0000c0; }\n.csharpcode .preproc { color: #cc6633; }\n.csharpcode .asp { background-color: #ffff00; }\n.csharpcode .html { color: #800000; }\n.csharpcode .attr { color: #ff0000; }\n.csharpcode .alt \n{\n\tbackground-color: #f4f4f4;\n\twidth: 100%;\n\tmargin: 0em;\n}\n.csharpcode .lnum { color: #606060; }\n<\/style>\n<h4>Update the Client Project<\/h4>\n<p>The last thing we need to do it tweaking the client. As I already mentioned, I could have left it (nearly) unchanged, but wanted to take the opportunity to show off some more features of AAL.<\/p>\n<p>The client UI and structure remain the same: all we change is the handler for the click event.<\/p>\n<div class=\"csharpcode\">\n<pre><span class=\"lnum\">   1:  <\/span><span class=\"kwrd\">private<\/span> async <span class=\"kwrd\">void<\/span> myButton_Click(<span class=\"kwrd\">object<\/span> sender, RoutedEventArgs e)<\/pre>\n<pre><span class=\"lnum\">   2:  <\/span>{<\/pre>\n<pre><span class=\"lnum\">   3:  <\/span> AuthenticationContext authenticationContext =<\/pre>\n<pre><span class=\"lnum\">   4:  <\/span>     <span class=\"kwrd\">new<\/span> AuthenticationContext(<span class=\"str\">\"https:\/\/sts.contoso100.com\/adfs\"<\/span>, <span class=\"kwrd\">false<\/span>);<\/pre>\n<pre><span class=\"lnum\">   5:  <\/span> AuthenticationResult _authResult =<\/pre>\n<pre><span class=\"lnum\">   6:  <\/span>     await authenticationContext.AcquireTokenAsync(<\/pre>\n<pre><span class=\"lnum\">   7:  <\/span>     <span class=\"str\">\"https:\/\/contoso100.com\/API\/KatanaADFS_WebAPISample\"<\/span>,<\/pre>\n<pre><span class=\"lnum\">   8:  <\/span>     <span class=\"str\">\"3fb2a37f-4ced-409c-937c-dddd776f4dfd\"<\/span>);<\/pre>\n<pre><span class=\"lnum\">   9:  <\/span>&nbsp;<\/pre>\n<pre><span class=\"lnum\">  10:  <\/span> <span class=\"kwrd\">string<\/span> result = <span class=\"kwrd\">string<\/span>.Empty;<\/pre>\n<pre><span class=\"lnum\">  11:  <\/span> HttpClient httpClient = <span class=\"kwrd\">new<\/span> HttpClient();<\/pre>\n<pre><span class=\"lnum\">  12:  <\/span> httpClient.DefaultRequestHeaders.Authorization =<\/pre>\n<pre><span class=\"lnum\">  13:  <\/span>     <span class=\"kwrd\">new<\/span> AuthenticationHeaderValue(<span class=\"str\">\"Bearer\"<\/span>, _authResult.AccessToken);<\/pre>\n<pre><span class=\"lnum\">  14:  <\/span> HttpResponseMessage response =<\/pre>\n<pre><span class=\"lnum\">  15:  <\/span>     await httpClient.GetAsync(<span class=\"str\">\"http:\/\/localhost:22102\/Api\/Values\"<\/span>);<\/pre>\n<pre><span class=\"lnum\">  16:  <\/span>&nbsp;<\/pre>\n<pre><span class=\"lnum\">  17:  <\/span> <span class=\"kwrd\">if<\/span> (response.IsSuccessStatusCode)<\/pre>\n<pre><span class=\"lnum\">  18:  <\/span> {<\/pre>\n<pre><span class=\"lnum\">  19:  <\/span>     result = await response.Content.ReadAsStringAsync();<\/pre>\n<pre><span class=\"lnum\">  20:  <\/span>&nbsp;<\/pre>\n<pre><span class=\"lnum\">  21:  <\/span> }<\/pre>\n<pre><span class=\"lnum\">  22:  <\/span> MessageDialog md = <span class=\"kwrd\">new<\/span> MessageDialog(result);<\/pre>\n<pre><span class=\"lnum\">  23:  <\/span> IUICommand x = await md.ShowAsync();<\/pre>\n<pre><span class=\"lnum\">  24:  <\/span>};<\/pre>\n<\/div>\n<style type=\"text\/css\">.csharpcode, .csharpcode pre\n{\n\tfont-size: small;\n\tcolor: black;\n\tfont-family: consolas, \"Courier New\", courier, monospace;\n\tbackground-color: #ffffff;\n\t\/*white-space: pre;*\/\n}\n.csharpcode pre { margin: 0em; }\n.csharpcode .rem { color: #008000; }\n.csharpcode .kwrd { color: #0000ff; }\n.csharpcode .str { color: #006080; }\n.csharpcode .op { color: #0000c0; }\n.csharpcode .preproc { color: #cc6633; }\n.csharpcode .asp { background-color: #ffff00; }\n.csharpcode .html { color: #800000; }\n.csharpcode .attr { color: #ff0000; }\n.csharpcode .alt \n{\n\tbackground-color: #f4f4f4;\n\twidth: 100%;\n\tmargin: 0em;\n}\n.csharpcode .lnum { color: #606060; }\n<\/style>\n<p>The first difference is in line 4. We initialize the AuthenticationContext with the address of the ADFS service (it <u>has<\/u> to end with <em>adfs <\/em>for AAL to recognize it as such) and we turn off authority validation. On the latter: Windows Azure AD exposes metadata for validating that the URL is of the right format, to prevent redirect attacks, as of today ADFS does not offer that feature.<\/p>\n<p>The second difference is in how we invoke AcquireTokenAsync, lines 5 to 8. Whereas in the Windows Azure AD scenario I used the richest overload of AcquireTokenAsync, here I am using the most essential one: all I am passing is the ID of the target resource, the Web API, and the client ID I used when registering the app in ADFS. Given that I omitted the redirect URI, AAL automatically uses the WebAuthenticationBroker in SSO mode which in turn is, as mentioned, a prerequisite for many new ADFS goodies we\u2019ll discuss in the future.<\/p>\n<h3>Testing the Solution<\/h3>\n<p>What\u2019s left? Spinning this bad boy, of course! Just like we did for the IWndows Azure AD tutorial, hit F5 for starting the Web API and right click-&gt;debug-&gt;start new instance to start the client.<\/p>\n<p>Once the UI shows up, hit the button on the left.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image36.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb36.png\" width=\"700\" height=\"394\"><\/a><\/p>\n<p>Here AAL will bring out the ADFS authentication experience: as you can see from the screenshot below, it scales gracefully to accommodate form factors as the WebAuthenitcationBroker\u2019s and is fully customizable (the fruity logo is courtesy of Sam, who\u2019s using the same instance (technically it\u2019s HIS instance <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" style=\"border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none\" alt=\"Smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/wlEmoticon-smile2.png\">) for demonstrating during his presentations how to modify ADFS\u2019 UI).<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image37.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb37.png\" width=\"700\" height=\"394\"><\/a><\/p>\n<p>Type your credentials and hit enter.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image38.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/07\/image_thumb38.png\" width=\"700\" height=\"394\"><\/a><\/p>\n<p>Success! Just like we saw in Windows Azure AD, we get a dump of the claim values the Web API received in the token from ADFS. If you squint a bit you can see that among those there is the email, as established by the issuance rule we created earlier; all the other ones are JWT structural claims.<\/p>\n<h2>Wrap<\/h2>\n<p>I might be biased, but I believe that the native client+Web API this scenario is the quintessential manifestation of the on-premises-cloud symmetry in our offering. Thanks to the common set of capabilities and consistent approach in Windows Azure AD and ADFS, and to the fact that one single library (AAL) serves both, you are basically taking the same solution and switching topology with just few adjustments in the parameters here and there. Most of the extra code in this post is to work around the current preview status of some of the moving parts, and the need for it will go away very soon.<\/p>\n<p>I hope you\u2019ll choose to play with this scenario too, the new ADFS has great features, here I just scratched the proverbial surface, and I am sure you\u2019ll appreciate how easily you can take advantage of its new capabilities. If you have feedback on the libraries I am all ears (note that this works with AAL .NET on classic desktop apps too!!) \u2013 and if you have feedback on ADFS I can\u2019t act directly on it, but I will make sure to relay it to the guys in the ADFS team. Have fun!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last week I wrote a post about how to use Katana and Windows Azure AD to secure an MVC4 Web API, and showed how to use AAL to build a Windows Store client in just few lines of code.This week I\u2019d like to show you how you can apply the exact same approach&#8230;<\/p>\n","protected":false},"author":1,"featured_media":2345,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2348","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/2348","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/comments?post=2348"}],"version-history":[{"count":3,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/2348\/revisions"}],"predecessor-version":[{"id":2351,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/2348\/revisions\/2351"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/media\/2345"}],"wp:attachment":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/media?parent=2348"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/categories?post=2348"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/tags?post=2348"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}