URL Authorization Rules

7 minute read • November 17, 2016

Chris Gillum (MSFT)
One of the goals of Azure App Service Authentication / Authorization is to make it very easy to add "auth" to your App Service apps (which is why we often refer to it as Easy Auth). Most of our investments so far have been focused on creating a streamlined authentication setup experience. However, up until now authorization was something developers had to implement mostly on their own. Typically authorization rules involve restricting access to certain resources within your app. Ideally, such authorization rules can be just as simple to set up without writing a bunch of custom code. To that end, we're happy to announce the initial preview of URL Authorization Rules in App Service.


In the initial preview, URL Authorization Rules are defined in an authorization.json file (if you prefer, we also support the YAML syntax inside an authorization.yaml file).  The feature is enabled automatically when you configure Easy Auth in the management portal and place either an authorization.json file or an authorization.yaml file in the D:\home\site\wwwroot directory of your App Service app. This means you can include the file in your app's source and easily deploy it via Git, Web Deploy, FTP, or any of the continuous deployment mechanisms supported by App Service. Here is the basic schema of the configuration file in the JSON syntax:
  "routes": [
      "http_methods": [ "GET", "POST", "PUT", ... ],
      "path_prefix": "/some/url/prefix",
      "policies": {
        "unauthenticated_action": "AllowAnonymous|RedirectToLoginPage|RejectWith401|RejectWith404"
Now let's talk about what these properties are for:
  • routes: Required. A collection of URL authorization rules for your App Service app.  In this preview release, each route contains an http_methods, a path_prefix, and a policies property, described below.
      • http_methods: Optional. An array of HTTP verbs that can be used to trigger a route policy. If not specified, all HTTP verbs can be used to trigger the policy.
      • path_prefix: Required. A relative URL path under which the specified policies will be enforced. More details on this in a subsequent section.
      • policies: A collection of policies to enforce when an HTTP request matches a route. At the time of writing, the only supported sub-property is unauthenticated_action.
      • unauthenticated_action: Required. One of the following values:
        • AllowAnonymous: Allows anonymous clients to access the resource.
        • RedirectToLoginPage: If a user is unauthenticated, they will be redirected to the login page of the default identity provider that was configured in the portal (for example, Azure Active Directory).
        • RejectWith401: Unauthenticated requests will fail with an HTTP 401 status.
        • RejectWith404: Unauthenticated requests will fail with an HTTP 404 status. You would choose this over RejectWith401 when you want to hide the existence of HTTP routes within your app.
As you can see, there are quite a few ways in which you can configure URL-based authorization policies for your app.

Route Evaluation Order and Wildcards

In the simplest case, your authorization.json (or authorization.yaml) file will contain one or more static routes. For example, consider the following authorization.json file contents, which represent authorization rules for an Azure Functions app with two HTTP triggers:
  "routes": [{
      "path_prefix": "/api/AnonymousHttpFunction",
      "policies": { "unauthenticated_action": "AllowAnonymous" }
      "path_prefix": "/api/ProtectedHttpFunction",
      "policies": { "unauthenticated_action": "RejectWith401" }
All HTTP requests to /api/AnonymousHttpFunction will be allowed, but requests to /api/ProtectedHttpFunction will require a client to be authenticated (e.g. using an access token).  This works well for simple, static APIs but many modern REST-ful APIs will have parameterized URL segments. In those cases, you'll want to take advantage of wildcard segments for your authorization routes. For example, consider a REST-ful API for viewing chapters of books. The name of the book and the chapter may be embedded in the URL:
  "routes": [{
      "http_methods": [ "GET", "HEAD" ],
      "path_prefix": "/api/book/*/chapter/*/content",
      "policies": { "unauthenticated_action": "RejectWith401" }
In the above example there is a wildcard * for both the name of the book and the chapter number. The * will match any single value within that segment. If your routes represent a resource hierarchy, you will likely need to configure separate policies for parent and child resources. Consider then the following example:
  "routes": [{
      "path_prefix": "/",
      "policies": { "unauthenticated_action": "AllowAnonymous" }
      "path_prefix": "/admin",
      "policies": { "unauthenticated_action": "RedirectToLoginPage" }
In this case, all URLs will be publicly accessible to anyone on the internet except those at or under /admin, which require a user to log in first. In the case of a browser client, navigating to /admin or a sub-path like /admin/edit_content.php will result in an automatic redirect to the configured login page. Now, technically a request such as GET /admin/edit_content.php which I used as an example matches both "/" and "/admin" routes. How do we decide which policy to execute? The answer is that the authorization runtime will always execute the policy with the most specific matching URL path (sometimes known as "longest-prefix matching") and only that policy will be executed.

Example: File Share

The nice thing about the example below is that it literally requires no code - at all. The idea is that you want to put some files on the web and protect access to them. It's also very easy to set up. The first thing you'll want to do is create a web app and configure Easy Auth with Azure Active Directory login. You could choose another provider, but AAD is the simplest to set up. This can be done in a few clicks in the portal as shown in the following screenshot. Make sure to select Allow Anonymous requests (no action) under the "Action to take when request is not authenticated". [caption id="attachment_2095" align="alignnone" width="851"]Setting up Easy Auth with Azure AD authentication. Setting up Easy Auth with Azure AD authentication.[/caption] Now that your AAD setup is ready, it's time to create the app. Because we're only dealing with file contents, this is easy to setup manually using the Kudu console, which can be found under Settings -> Advanced Tools. Or, you can navigate to it directly at https://{web-app-name}.scm.azurewebsites.net/DebugConsole. Use the following commands to create the necessary files.
 cd site\wwwroot
 del hostingstart.html
 mkdir public
 echo "hello, public!" > public\file1.txt
 mkdir admin
 echo "hello, admin!" > admin\file2.txt
This will set up your file system structure with some folders and files.  Next we want to enable directory browsing, which is disabled by default. We do this by creating a web.config file in the wwwroot directory. You can create it using the touch web.config command and edit by clicking the edit button and populating it with the following configuration:
<!--?xml version="1.0"?-->
    <directoryBrowse enabled="true"/>
Lastly, we need to create the authorization.json file.  You can use the command touch authorization.json to create the file. Then, click the edit button and add the following file contents:
        "path_prefix" : "/public",
        "policies" : { "unauthenticated_action" : "AllowAnonymous" }
        "path_prefix" : "/admin",
        "policies": { "unauthenticated_action" : "RedirectToLoginPage" }
To test this, open a new In-Private/Incognito browser session and navigate to your web app. You should see a list of the directories you created as well as the web.config and authorization.json files (note that if you want to hide these infrastructure files, you can do so using the attrib +h {filename} command in the Kudu console). You can then navigate to the directories in your browser and observe the authorization behavior. To help understand what's going on under the covers, I recommend enabling Application Logging in the portal and then opening the Log Stream blade to see the authorization logs in real-time. [caption id="attachment_2165" align="alignnone" width="1227"]Streaming Easy Auth logs in the Azure Portal Streaming Easy Auth logs in the Azure Portal[/caption]


While the example above was a web apps example, this feature works the same whether you're writing a web app, a mobile app backend, an API app, or a Azure Functions app. However, remember that this is an early preview so there are a few things that need to be smoothed out before the feature can be considered GA. For example, when changing the authorization.json (or authorization.yaml) file, you must restart the site before the changes take effect. In the future we would like to have these updates be reflected immediately. We are also working on additional authorization policies that can be configured. As an example, in a coming update you can expect to see support for restricting access to specified URL routes to Azure AD Security Groups (we actually support this in the runtime today, but need to smooth out the setup experience before we blog about it). So give this a try and let us know how it goes. If you have questions, please head over to StackOverflow and tag them with azure-app-service or azure-web-sites, so that our automated processes can find them.