{"id":39283,"date":"2020-05-19T06:00:53","date_gmt":"2020-05-19T13:00:53","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/premier-developer\/?p=39283"},"modified":"2020-05-11T08:42:56","modified_gmt":"2020-05-11T15:42:56","slug":"moving-legacy-asp-net-apps-with-windows-authentication-to-azure-app-service-part-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/premier-developer\/moving-legacy-asp-net-apps-with-windows-authentication-to-azure-app-service-part-2\/","title":{"rendered":"Moving legacy ASP.NET apps with Windows authentication to Azure App Service (Part 2)"},"content":{"rendered":"<p>Application Development Manager <a href=\"https:\/\/www.linkedin.com\/in\/mikelapierre\/\">Mike Lapierre<\/a> explores moving backend services using Windows authentication to Azure App Service.<\/p>\n<hr \/>\n<p>In my <a href=\"https:\/\/devblogs.microsoft.com\/premier-developer\/moving-legacy-asp-net-apps-with-windows-authentication-to-azure-app-service-part-1\/\">previous blog post<\/a>, I covered how to move legacy two-tier applications using Windows Authentication to Azure App Service. Let\u2019s now talk about moving legacy backend services that use Windows authentication over to an Azure App Service.<\/p>\n<h2>Web Service Stacks<\/h2>\n<p>Most legacy apps will use either the older web services stack (ASMX) or Windows Communication Foundation (WCF). Newer apps could also be using the ASP.NET Web API stack. Let\u2019s break them down.<\/p>\n<h3>Web Services (ASMX)<\/h3>\n<p>Introduced with .NET 1.0 (2002), the web services stack, located in the <code>System.Web.Services<\/code> namespace, is a basic SOAP implementation over HTTP\/S. It\u2019s a good fit for App Service, and should be easy to move with the techniques I\u2019ll describe later on.<\/p>\n<h3>Windows Communication Foundation (WCF)<\/h3>\n<p>Introduced with .NET 3.0 (2006), Windows Communication Foundation, found in the <code>System.ServiceModel<\/code> namespace, is a broader stack that can be used with several different communication protocols. Unfortunately, App Service is a HTTP-only service, so non-HTTP bindings like NetTcp cannot be used. In some cases, it may be simple to move to HTTP since WCF does a good job of abstracting the underlying communication protocols. In other cases, it may be more difficult, for example when using Duplex protocols. You\u2019ll also have to make sure your bindings use HTTPS, but once that\u2019s done you\u2019ll be able to use the techniques described later.<\/p>\n<h3>ASP.NET Web API<\/h3>\n<p>Introduced with .NET 4.5 (2012), the ASP.NET Web API stack, located in the <code>System.Web.Http<\/code> namespace, adopted the REST architectural style over HTTP\/S. Since it\u2019s HTTP-based and very close to the web stack, it\u2019s probably the best fit for App Service.<\/p>\n<h2>Authentication &amp; Authorization Models<\/h2>\n<p>Now that we\u2019ve looked at the different stacks, let\u2019s discuss the different authentication &amp; authorization models used for web services. The two most common ones are Trusted subsystem and Delegation; let\u2019s cover them both.<\/p>\n<h3>Trusted subsystem model<\/h3>\n<p>With the trusted subsystem model, the user is authenticated and authorized on the frontend only. When the frontend application calls the backend services, it is authenticated and authorized using its own identity. In the ASP.NET world, the Application Pool identity is used for this purpose. Unfortunately, the Application Pool identity is not usable in App Service.<\/p>\n<p>A client ID and secret are needed to authenticate as a client (Client Credentials flow) using OAuth (shown below). The downside of this approach is additional work to protect and change the secret on a regular basis. It also surfaces in plain text in the code, which can present a security risk.<\/p>\n<p><img decoding=\"async\" width=\"2500\" height=\"1511\" class=\"wp-image-39284\" src=\"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-content\/uploads\/sites\/31\/2020\/05\/word-image-11.png\" srcset=\"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-content\/uploads\/sites\/31\/2020\/05\/word-image-11.png 2500w, https:\/\/devblogs.microsoft.com\/premier-developer\/wp-content\/uploads\/sites\/31\/2020\/05\/word-image-11-300x181.png 300w, https:\/\/devblogs.microsoft.com\/premier-developer\/wp-content\/uploads\/sites\/31\/2020\/05\/word-image-11-1024x619.png 1024w, https:\/\/devblogs.microsoft.com\/premier-developer\/wp-content\/uploads\/sites\/31\/2020\/05\/word-image-11-768x464.png 768w, https:\/\/devblogs.microsoft.com\/premier-developer\/wp-content\/uploads\/sites\/31\/2020\/05\/word-image-11-1536x928.png 1536w, https:\/\/devblogs.microsoft.com\/premier-developer\/wp-content\/uploads\/sites\/31\/2020\/05\/word-image-11-2048x1238.png 2048w\" sizes=\"(max-width: 2500px) 100vw, 2500px\" \/><\/p>\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/v2-oauth2-client-creds-grant-flow\"><em>https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/v2-oauth2-client-creds-grant-flow<\/em><\/a><\/p>\n<p>Fortunately, there\u2019s a concept in Azure called a <a href=\"https:\/\/docs.microsoft.com\/azure\/app-service\/overview-managed-identity\">managed identity<\/a> that can be used to authenticate the service without requiring the use of a client secret. Let\u2019s see how it works.<\/p>\n<p>The first step is to create a managed identity for the frontend App Service. You can choose between a system assigned (auto-generated) or user assigned identity. A managed identity creates a service principal for your application, which acts like a service account.<\/p>\n<p>The next step is to enable App Service Authentication for the backend App Service, just like we did in <a href=\"https:\/\/devblogs.microsoft.com\/premier-developer\/moving-legacy-asp-net-apps-with-windows-authentication-to-azure-app-service-part-1\/\">Part 1 of this series<\/a>. The operation will create an Azure AD application we can now use for the backend service. Although the express creation mode will create both for you, we&#8217;ll be making use of the Application ID URI instead of the Redirect URL in this case.<\/p>\n<p>Let\u2019s now look at the frontend code to call the backend service. I\u2019ve created a <a href=\"https:\/\/github.com\/mikelapierre\/LegacyAppService\">GitHub repo<\/a> with different sample applications based on three technology stacks (ASMX, WCF and Web API). I\u2019ve grouped the relevant caller code in the <a href=\"https:\/\/github.com\/mikelapierre\/LegacyAppService\/blob\/master\/LegacyWebApp\/AuthHelper.cs\">AuthHelper class<\/a>. The <strong>GetAccessTokenMI<\/strong> method uses the <code>Microsoft.Azure.Services.AppAuthentication<\/code> library to acquire an access token using the managed identity. I\u2019ve also included the <strong>GetAccessToken<\/strong> method, which uses a client secret, for completeness.<\/p>\n<p>The returned access token is a regular bearer token that must be sent through the authorization header to the backend service. The code for this will vary by client technology stack, but it\u2019s easy to implement since it\u2019s just a regular HTTP header. Take a look at the at the different pages (<a href=\"https:\/\/github.com\/mikelapierre\/LegacyAppService\/blob\/master\/LegacyWebApp\/ASMX.aspx.cs\">ASMX<\/a>, <a href=\"https:\/\/github.com\/mikelapierre\/LegacyAppService\/blob\/master\/LegacyWebApp\/WCF.aspx.cs\">WCF<\/a> and <a href=\"https:\/\/github.com\/mikelapierre\/LegacyAppService\/blob\/master\/LegacyWebApp\/WebAPI.aspx.cs\">Web API<\/a>) for examples.<\/p>\n<p>In a trusted subsystem model, the user authorization is delegated to the frontend. Authorization on the backend service is usually handled just by validating the caller\u2019s identity. A simple claim lookup, based on the appid claim (corresponding to the client id) should do the trick.<\/p>\n<h3>Delegation model<\/h3>\n<p>With the delegation model, user authentication and authorization is handled both at the frontend and backend services. In the ASP.NET world, this is usually done using impersonation or delegation. Since we cannot use Windows authentication or Kerberos delegation in App Service, we must look elsewhere. OAuth has an On-Behalf-Of (OBO) flow (shown below) that allows a client application to request a ticket on-behalf of a user, achieving the delegation model. You can also do multiple hops, just like Kerberos delegation. Let\u2019s see how it works.<\/p>\n<p><img decoding=\"async\" width=\"595\" height=\"295\" class=\"wp-image-39285\" src=\"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-content\/uploads\/sites\/31\/2020\/05\/a-screenshot-of-a-social-media-post-description-a.png\" alt=\"A screenshot of a social media post Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-content\/uploads\/sites\/31\/2020\/05\/a-screenshot-of-a-social-media-post-description-a.png 595w, https:\/\/devblogs.microsoft.com\/premier-developer\/wp-content\/uploads\/sites\/31\/2020\/05\/a-screenshot-of-a-social-media-post-description-a-300x149.png 300w\" sizes=\"(max-width: 595px) 100vw, 595px\" \/><\/p>\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/v2-oauth2-on-behalf-of-flow\"><em>https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/v2-oauth2-on-behalf-of-flow<\/em><\/a><\/p>\n<p>In this flow, the frontend service sends both its client secret and the user\u2019s token (called an assertion). This authenticates the service using the client secret and confirms successful user authentication, assuming the user\u2019s id token is still valid. If the user has not previously connected to the backend service, then their consent is also needed.<\/p>\n<p>User consent management is beyond the scope of this article. Since we\u2019re covering legacy applications that never used consent anyway, the easiest way to handle this is with the Authorized client applications feature in Azure AD that indicates that the API trusts the calling application (frontend) and no consent is required.<\/p>\n<p>Keep in mind this scenario is more complex because, in addition to consent management, it also requires handing the user\u2019s token expiration by using refresh tokens. App Service offers an authentication <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/app-service\/app-service-authentication-how-to#refresh-identity-provider-tokens\">refresh endpoint<\/a> (.auth\/refresh), which can be called when necessary to ensure the frontend always has a valid ID Token. A valid ID Token is a requirement for the OBO flow.<\/p>\n<p>Now let\u2019s talk about the code for this scenario. I recommend using the <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.Identity.Client\/\">Microsoft Authentication Library for .NET<\/a> (MSAL.NET) to request tokens on the client side. It makes handling things like caching easier. Again, you can refer to the <a href=\"https:\/\/github.com\/mikelapierre\/LegacyAppService\/blob\/master\/LegacyWebApp\/AuthHelper.cs\">AuthHelper class<\/a>, but this time focus on the <strong>GetAccessTokenOBO<\/strong> method.<\/p>\n<p>Note that a client secret is required for this flow. Unfortunately, we cannot use managed identity in this case since it doesn\u2019t support the OBO flow at this time. You can put your client secret in Azure Key Vault and then use a managed identity to query the Key Vault, making the implementation more secure.<\/p>\n<p>For authorization, you can use the same techniques described in <a href=\"https:\/\/devblogs.microsoft.com\/premier-developer\/moving-legacy-asp-net-apps-with-windows-authentication-to-azure-app-service-part-1\/\">Part 1 of this series<\/a>, since we\u2019re authorizing the end user and all their claims will be available in the Bearer access token received by the backend service.<\/p>\n<h2>Hands On<\/h2>\n<p>If you wish to play with this yourself, I encourage you to clone my <a href=\"https:\/\/github.com\/mikelapierre\/LegacyAppService\">GitHub repo<\/a> that includes sample applications and scripts to deploy them to your own Azure subscription. The scripts handle the creation of the different resources and required settings, all in a free Azure App Service plan. Just follow the instructions in the <a href=\"https:\/\/github.com\/mikelapierre\/LegacyAppService\/blob\/master\/Readme.md\">Readme<\/a> and have fun!<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We discuss moving legacy backend services that use Windows authentication over to an Azure App Service, with emphasis on web service stack and authentication &#038; authorization considerations.<\/p>\n","protected":false},"author":582,"featured_media":39284,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[25,6197,1],"tags":[53,149,24,3],"class_list":["post-39283","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure","category-migration","category-permierdev","tag-app-services","tag-authentication","tag-azure","tag-team"],"acf":[],"blog_post_summary":"<p>We discuss moving legacy backend services that use Windows authentication over to an Azure App Service, with emphasis on web service stack and authentication &#038; authorization considerations.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/39283","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/users\/582"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/comments?post=39283"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/39283\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media\/39284"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media?parent=39283"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/categories?post=39283"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/tags?post=39283"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}