This is the second part of a series of blog posts related to Azure AD best practices. They are all related to a talk I gave at Tech Days Finland as well as in the Microsoft Identity Developer Community Office Hours.

A requirement that often comes up is that after a user logs in via Azure AD (or any identity provider really), they should be redirected to the page they tried to access. Meaning a flow like this:

  1. User tries to access www.contoso.com/products
  2. App redirects them to log in with Azure AD
  3. User is redirected back to the application, and should now see the product list

This is a really common requirement, and makes the user experience nicer. Instead of the user being bounced back to the index page after logging in, they get to where they wanted to go right away.

You can find a sample of the better approach presented here on GitHub: https://github.com/juunas11/7-deadly-sins-in-azure-ad-app-development/tree/master/MSALNoWildcardReplyURL.

There are multiple ways to implement this. Let's look at the wrong way first.

Using a wildcard reply URL

As a developer, you might want Azure AD to redirect the user to the page they wanted to go. So you could specify redirect_uri=https%3A%2F%2Fwww.contoso.com%2Fproducts in the URL when redirecting the user to log in.

Oh, but you can't do that unless you specify https://www.contoso.com/products as a valid reply URL for the app in Azure AD. Any URL you put in the redirect_uri must be specified in the app's config in Azure AD. And you'd have to do that for every possible path in your app. We don't need to discuss why this is not maintainable/smart right?

The not so good way of solving this issue is to use a wildcard. This means specifying the reply URL in Azure AD as https://www.contoso.com/*. Now you can specify any URL under that domain and it'll be accepted.

There are a couple reasons why this is not a good idea:

  • Potential for abuse if your application has an open redirect vulnerability
    • A bad actor could potentially craft a link that would look real (it points to your app), but after login would actually redirect to an identical phishing site
  • The new application registration experience in Azure AD does not allow wildcard reply URLs, support is being removed

I've seen this approach mainly used in front-end Single Page Apps. MSAL.js even does this by default. You can however turn that behaviour off and instead use the approach in the next section.

Avoiding wildcard reply URLs

Instead of using a wildcard reply URL, we can use the following slightly more complex approach:

  1. User tries to access www.contoso.com/products
  2. App stores /products in browser session storage
  3. App redirects them to log in with Azure AD
  4. User is redirected back to the application's Azure AD callback endpoint (like /aad-callback)
  5. /products fetched from session storage, validated, user redirected there

So in this case we define a single reply URL in Azure AD: https://www.contoso.com/aad-callback. We define that as the redirect_uri every time, but store a "local redirect URL" in session storage before the login redirect.

Now some of you may be thinking this doesn't solve the open redirect issue. And you would be right, it doesn't. You still need to validate the local redirect URL against a whitelist of allowed URLs.

This approach allows us to do the validation in a central location before redirecting to it after a successful authentication. It also gives us a nice point where we can handle tokens returned and possibly acquire additional tokens. With the wildcard approach, we'd need some kind of global handler that picks up tokens returned from Azure AD.

You can see this approach in action in this sample on GitHub.

Summary

Don't use wildcards in reply URLs if at all possible. Instead use a central authentication callback endpoint, and redirect to the destination from there. Do not forget to validate the local redirect URL.

Not only is it not recommended to use wildcards, but the new registration experience in Azure AD does not even allow them.

I hope this has been helpful! The next article will be on the Resource Owner Password Credentials grant flow.