Devil is in the (RedirectUri) detail
When using Azure
Active Directory (AAD) as Identity
Provider for your Azure App Services, you will set up App Registrations to tell AAD how to handle your app
authentication.
One important bit of this is the ReplyURL (RedirectUri) that
you need to specify for AAD to
redirect the user back to your app after valid authentication.
The usual flow is:
- User requests your app URL (ie: https://myappservice.azurewebsites.net)
- User is redirected to the AAD Login page (https://login.microsoftonline.com/.../oauth2/authorize)
- User inserts valid credentials
- User is redirected back to your defined RedirectUri as a logged on User (https://myappservice.azurewebsites.net)
For this to happen, you will need to specify in your application as AppSettings in the web.config
file:
And in the Startup.Auth.cs
file (here I am using a .NET MVC Web App and OpenID Connect
Authentication):
app.UseOpenIdConnectAuthentication( new OpenIdConnectAuthenticationOptions { RedirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"], PostLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"],
Now, let’s assume that as a security requirement in your
organization, your App Service must
reside behind an F5 LoadBalancer, and all traffic must go through it, and that also Mutual Client Authentication must be in
place between the F5 and your App Service.
For this scenario to occur, you need to set up a few parts
in the Azure App Service and AAD (the
F5 setup and DNS entries are out of scope for this post):
- Enable Client Certificates in the Resource Explorer of the App Service: "clientCertEnabled": true (this will make sure that the App Service expects a SSL Certificate for each request).
- Define a Custom Domain on the App Service as specified in the SSL Client Certificate, ie: https://myappdomain.corporateurl.com
- Upload the valid SSL Client Certificate to the App Service, and create a SSL binding to the Custom Domain
- Define a Custom Domain on the App Service as the F5 public endpoint for this web app, ie: https://myappsf5domain.corporateurl.com
- Add the new App Service Custom Domains URLs to the AAD App Registration as ReplyURLs: https://myappdomain.corporateurl.com and https://myappsf5domain.corporateurl.com
- Implement custom Certificate Validation code inheriting from System.Web.Mvc.IAuthorizeFilter and FilterAttribute (ie: public class ClientCertificateValidatorFilter : FilterAttribute, IAuthorizationFilter)
- Add the corresponding [CustomAuthorizeAttribute] to the desired Controller classes (or Actions) (ie: [ClientCertificateValidatorFilter])
- Since the security requirement is that all traffic must go through the F5, specify the F5 URL as RedirectUri and PostLogoutRedirectUri in the web.config file:
So now the “Happy Path” flow is:
- User requests your app URL to the F5 (https://myappsf5domain.corporateurl.com)
- Since the F5 provides the SSL Certificate in the HTTP Header, no popup shows in the browser
- User is redirected to the AAD Login page (https://login.microsoftonline.com/.../oauth2/authorize)
- User inserts valid credentials
- User is redirected back to your app as a logged on User, going again through the F5 (https://myappsf5domain.corporateurl.com)
- The Certificate validation code kicks in and validates the right SSL Certificate provided by the F5 (keep in mind that the Authorize filters will execute only AFTER authentication, hence User login)
So far so good, right?
Now, let’s test some less happy flow.
We know that all traffic must go through the F5, so let’s try to call the Azure URL (https://myappservice.azurewebsites.net)
directly.
- User requests the Azure URL (https://myappservice.azurewebsites.net)
- User is prompted for a SSL Certificate
- User cannot provide a SSL Certificate, so hits Cancel on the certificate popup
- User receives a 403 – Forbidden response (correctly)
Again, so far so good.
But what happens if the User
provides the wrong SSL Certificate,
instead of canceling the popup?
- User requests the Azure URL (https://myappservice.azurewebsites.net)
- User is prompted for a SSL Certificate
- User provides any (wrong) SSL Certificate
- User is redirected to the AAD Login page (https://login.microsoftonline.com/.../oauth2/authorize)
- User inserts valid credentials
- User is redirected back to your app as a logged on User (incorrectly)
- The Certificate validation code kicks in and validates the right SSL Certificate provided by the F5
A few things will go wrong in this scenario:
- Since the defined RedirectUri is the F5 URL, now the User is redirected to this endpoint (https://myappsf5domain.corporateurl.com), however the request was coming from the Azure URL (https://myappservice.azurewebsites.net), so it will result in a AuthenticationFailed error, and the User will land on the Error page of your app, showing the message: “IDX10311: RequireNonce is 'true' (default) but validationContext.Nonce is null. A nonce cannot be validated. If you don't need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to 'false'.”
- Even though the User provided a wrong SSL Certificate, since now he’s going through the F5 after authenticating, the right SSL Certificate is provided and the validation succeed.
So, after several hours spent on the phone with Microsoft Support (thankfully to the Premier level of support, this was
possible in the first place), the solution to this scenario has been identified
in a small code snippet to be added to the OpenIdAuthenticationOptions
in Startup.Auth.cs.
Within the Notifications =
new OpenIdConnectAuthenticationNotifications
section, let’s add the following code:
RedirectToIdentityProvider = async n => { n.ProtocolMessage.RedirectUri = "https://" + n.OwinContext.Request.Uri.Host + "/"; n.ProtocolMessage.PostLogoutRedirectUri = "https://" + n.OwinContext.Request.Uri.Host + "/"; },
},
What this code does, it is simply to force the RedirectUri to whatever URL the request was coming from
originally, no matter what has been defined in the AppSettings (either in the web.config
file, or in some Application Setting
in one of the many Deployment Slots
that your app might have… and good luck here).
So now the flow in this scenario becomes:
- User requests the Azure URL (https://myappservice.azurewebsites.net)
- User is prompted for a SSL Certificate
- User provides any (wrong) SSL Certificate
- User is redirected to the AAD Login page (https://login.microsoftonline.com/.../oauth2/authorize)
- User inserts valid credentials
- User is redirected back to your app on the specific requested URL (https://myappservice.azurewebsites.net)
- The Certificate validation code kicks in and validates the wrong SSL Certificate provided by the User, and returns a 403 – Forbidden response
- User receives a 403 – Forbidden response (correctly)
Phew, that was easy, wasn’t it?
(:
Now there’s only one last bit remaining: remember we said
that all traffic must go through the F5
load balancer?
So what happens now if the User can somehow provide a valid SSL Certificate, and also figures out the Azure URL and calls it directly?
Surprise surprise… He will log onto the web app bypassing
altogether the F5!
So for this you will need to implement IP Whitelisting, and so allow ONLY the F5 incoming traffic to the App
Service.
Here is a sort of cheat-sheet of the parts needed in this
post:
Comments
Post a Comment