Sunday, April 5, 2020

Azure Application Gateway: HTTP headers rewrite rules for App Service with AAD authentication

As you probably already know, you can use Azure App Service as backend pool for Application Gateway. The general configuration procedure can be found in the Microsoft documentation. This configuration works fine for simple sites, but in case you App Service uses Azure Active Directory (AAD) for authentication and authorization extra steps required to deal with HTTP redirections related to the AAD authentication flow.

The problem

Azure App Services configured with AAD authentication like this
two HTTP redirects happen during login process.

The first redirect happens when App Service sends un-authenticated user to AAD authorize endpoint  to allow user to login and obtain the ID token from AAD. The redirect URL will be like this one:

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id={clieent_id}
&response_type=id_token
&redirect_uri=https%3A%2F%2Fsomeapp.azurewebsites.net%2F.auth%2Flogin%2Faad%2Fcallback
&response_mode=form_post
&scope=openid
&state=12345
&nonce=678910



This URL (also known as callback URL) contains the address where Azure AD will direct user's browser to POST authentication response after successful login. You can find more details about this process here

The second redirect happens as response from the HTTP POST to the authentication callback URL when App Service redirects authenticated user to the initially requested app URL.

Below are examples of these redirects extracted from the browser's development tools.
First redirect (browser accesses address waf.dg20.net that resolves into the Application Gateway frontend IP):

Second redirect:

As you can see, even if the browser tried to access our app using an address assigned to the Application Gateway, after login we will end-up sending HTTP requests directly to App Service by-passing Application Gateway. This would defeat the whole purpose of putting the app behind the Application Gateway. In the case when App Service is properly locked down and have enabled static IP restrictions to enforce access only through Application Gateway, user potentially will see 403 HTTP error after logon:

The solution

The Azure documentation describes this issue here and offers solution (HTTP headers rewrite) here. Unfortunately, the prescribed procedure doesn't account for Azure AD authentication process and only offers a method to 'fix' the second redirect. Honestly speaking, this could be considered a "good enough" solution, but it still exposes App Service native address to the client and will work only if client can hit this address directly after AAD logon process.
The solution below will hide backend address from client and will work with locked down App Service. It will rewrite "Location" header in the both redirection 302 responses using two rules in the single Rewrite Set on the Application Gateway. Both rules check and rewrite 'Location' header in the HTTP response.

1. First redirect rewrite - login redirect to AAD
Condition (If): header "Pattern to match"
(.*)(redirect_uri=https%3A%2F%2F).*\.azurewebsites\.net(.*)$

Action (then): set header value
{http_resp_Location_1}{http_resp_Location_2}{var_host}{http_resp_Location_3}

2. Second redirect rewrite - callback from AAD
Condition (If): header "Pattern to match"
(https:\/\/).*\.azurewebsites\.net(.*)$

Action (then): set header value
https://{var_host}{http_resp_Location_2}
This Rewrite Set must be associated with Application Gateway routing rule to be effective. The set can be used with any routing rule that uses Azure App Service with AAD authentication as a backend and it can significantly simplify gateway configuration, especially in the scenario where multiple sites are hosted.

11 comments:

  1. I set these up, but when I try to access my app at the custom domain, I get a "ERR_TOO_MANY_REDIRECTS" error. Looking at it in the browser developer tools, I just see a lengthy list of 307's.

    What are the requirements other than these rewrite rules?

    ReplyDelete
    Replies
    1. I solved this. In my configuration, I was using HTTPS from the client browser to the application gateway, but I had the gateway to app service setup to use HTTP (there are performance benefits to doing that and few security risks because that traffic is completely within Azure).

      When doing this though, the app service does a 307 to the *.azurewebsites.net URL. The rewrite rule was changing the URL and the app service would send another 307; i.e. endless loop.

      I configured the app service for only HTTPS and the problem went away.

      The solution as described in the post works great. Thanks!

      Delete
  2. Hi Wayne,

    I am glad this post helped!

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Hi Kyle. You web app experiences underhanded exception. Use app service diagnostics to find room cause - https://docs.microsoft.com/en-us/azure/app-service/overview-diagnostics

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. Double check you configuration and ensure auth works without WAF.
      BTW, this is beyond the scope of this blog. Thank you for understanding.

      Delete
  5. Hello,

    A great thanks for this article, may I ask some help here please?
    I did raise a Microsoft ticket to publish my App Service with Azure AD authentication and the solution proposed was to configure my custom domain on my App Service, this solution works and doesn’t need url rewrite but it’s I’m not comfortable with it.

    I would have preferred to keep the public custom domain, certificate and dns staff fully managed on the Application Gateway which could then be managed by a unique Cyber Security team for example. The App Service could then be fully managed by an App team which doesn’t have to take care about the company custom domains, dns and certificate management.

    I tried your solution; the redirection and the Azure AD works but my app service displays an error.
    Would mind please check my configuration and advise if you see any error, my PowerShell script is here: https://github.com/JamesDLD/AzureRm-PowerShell/blob/master/Publish-AzAppServiceOnAppGw.ps1

    The script part with the url rewrite -->

    ```
    $LoginRedirectToAADcondition = New-AzApplicationGatewayRewriteRuleCondition -Variable "http_resp_Location" -Pattern "(.*)(redirect_uri=https%3A%2F%2F).*\.azurewebsites\.net(.*)$" -IgnoreCase
    $LoginRedirectToAADresponseHeaderConfiguration = New-AzApplicationGatewayRewriteRuleHeaderConfiguration -HeaderName "Location" -HeaderValue "{http_resp_Location_1}{http_resp_Location_2}{var_host}{http_resp_Location_3}"
    $LoginRedirectToAADactionSet = New-AzApplicationGatewayRewriteRuleActionSet -ResponseHeaderConfiguration $LoginRedirectToAADresponseHeaderConfiguration
    $LoginRedirectToAADrewriteRule = New-AzApplicationGatewayRewriteRule -Name LoginRedirectToAAD -ActionSet $LoginRedirectToAADactionSet -Condition $LoginRedirectToAADcondition

    $CallbackFromAADcondition = New-AzApplicationGatewayRewriteRuleCondition -Variable "http_resp_Location" -Pattern "(https:\/\/).*\.azurewebsites\.net(.*)$" -IgnoreCase
    $CallbackFromAADresponseHeaderConfiguration = New-AzApplicationGatewayRewriteRuleHeaderConfiguration -HeaderName "Location" -HeaderValue "https://{var_host}{http_resp_Location_2}"
    $CallbackFromAADactionSet = New-AzApplicationGatewayRewriteRuleActionSet -ResponseHeaderConfiguration $CallbackFromAADresponseHeaderConfiguration
    $CallbackFromAADrewriteRule = New-AzApplicationGatewayRewriteRule -Name CallbackFromAAD -ActionSet $CallbackFromAADactionSet -Condition $CallbackFromAADcondition
    ```

    Thank you for your help,
    James

    ReplyDelete
  6. Hi James,

    You can verify your script results in the Azure App Gateway UI. Your rules must match to the screenshots in my post.
    This post is explicitly about the scenario, where backend pool is Azure App Service. Your script uses custom FQDN as a backend pool. This is where issue is coming from I suspect.

    ReplyDelete
    Replies
    1. I did review the conf from the UI, its the same. My backup from the UI is the App Service directly instead of the custom FQDN, I did recreate the all manually from the portal and i'm receiving the same error from the App service, Microsoft told me that the recommended method is to declare the custom Domain on my app service with no rewrite rules. I'am starting to give up...

      Delete
    2. Hi James,

      I am not sure what is the error your mention. Verify you DNS configuration, ensure you use AppGW V2 (custom domain name on app service was V1 workaround).
      In any case, this type of troubleshooting is beyond the scope of this blog. Thank you for understanding.
      Find me on LinkedIn if you want to engage me privately.

      Delete

How to backup Azure DevOps code repositories

Under " shared responsibility in the cloud " model, the client is always responsible for its own data. Azure DevOps, as a SaaS off...