Advanced usage of authentication and authorization in Azure App Service

This article shows you how to customize the built-in authentication and authorization in App Service, and to manage identity from your application.

To get started quickly, see one of the following tutorials:

Use multiple sign-in providers

The portal configuration doesn't offer a turn-key way to present multiple sign-in providers to your users (such as both Facebook and Twitter). However, it isn't difficult to add the functionality to your app. The steps are outlined as follows:

First, in the Authentication / Authorization page in the Azure portal, configure each of the identity provider you want to enable.

In Action to take when request is not authenticated, select Allow Anonymous requests (no action).

In the sign-in page, or the navigation bar, or any other location of your app, add a sign-in link to each of the providers you enabled (/.auth/login/<provider>). For example:

<a href="/.auth/login/aad">Log in with Azure AD</a>
<a href="/.auth/login/microsoftaccount">Log in with Microsoft Account</a>
<a href="/.auth/login/facebook">Log in with Facebook</a>
<a href="/.auth/login/google">Log in with Google</a>
<a href="/.auth/login/twitter">Log in with Twitter</a>

When the user clicks on one of the links, the respective sign-in page opens to sign in the user.

To redirect the user post-sign-in to a custom URL, use the post_login_redirect_url query string parameter (not to be confused with the Redirect URI in your identity provider configuration). For example, to navigate the user to /Home/Index after sign-in, use the following HTML code:

<a href="/.auth/login/<provider>?post_login_redirect_url=/Home/Index">Log in</a>

Validate tokens from providers

In a client-directed sign-in, the application signs in the user to the provider manually and then submits the authentication token to App Service for validation (see Authentication flow). This validation itself doesn't actually grant you access to the desired app resources, but a successful validation will give you a session token that you can use to access app resources.

To validate the provider token, App Service app must first be configured with the desired provider. At runtime, after you retrieve the authentication token from your provider, post the token to /.auth/login/<provider> for validation. For example:

POST https://<appname>.azurewebsites.net/.auth/login/aad HTTP/1.1
Content-Type: application/json

{"id_token":"<token>","access_token":"<token>"}

The token format varies slightly according to the provider. See the following table for details:

Provider value Required in request body Comments
aad {"access_token":"<access_token>"}
microsoftaccount {"access_token":"<token>"} The expires_in property is optional.
When requesting the token from Live services, always request the wl.basic scope.
google {"id_token":"<id_token>"} The authorization_code property is optional. When specified, it can also optionally be accompanied by the redirect_uri property.
facebook {"access_token":"<user_access_token>"} Use a valid user access token from Facebook.
twitter {"access_token":"<access_token>", "access_token_secret":"<acces_token_secret>"}

If the provider token is validated successfully, the API returns with an authenticationToken in the response body, which is your session token.

{
    "authenticationToken": "...",
    "user": {
        "userId": "sid:..."
    }
}

Once you have this session token, you can access protected app resources by adding the X-ZUMO-AUTH header to your HTTP requests. For example:

GET https://<appname>.azurewebsites.net/api/products/1
X-ZUMO-AUTH: <authenticationToken_value>

Sign out of a session

Users can initiate a sign-out by sending a GET request to the app's /.auth/logout endpoint. The GET request does the following:

  • Clears authentication cookies from the current session.
  • Deletes the current user's tokens from the token store.
  • For Azure Active Directory and Google, performs a server-side sign-out on the identity provider.

Here's a simple sign-out link in a webpage:

<a href="/.auth/logout">Sign out</a>

By default, a successful sign-out redirects the client to the URL /.auth/logout/done. You can change the post-sign-out redirect page by adding the post_logout_redirect_uri query parameter. For example:

GET /.auth/logout?post_logout_redirect_uri=/index.html

It's recommended that you encode the value of post_logout_redirect_uri.

When using fully qualified URLs, the URL must be either hosted in the same domain or configured as an allowed external redirect URL for your app. In the following example, to redirect to https://myexternalurl.com that's not hosted in the same domain:

GET /.auth/logout?post_logout_redirect_uri=https%3A%2F%2Fmyexternalurl.com

Run the following command in the Azure Cloud Shell:

az webapp auth update --name <app_name> --resource-group <group_name> --allowed-external-redirect-urls "https://myexternalurl.com"

Preserve URL fragments

After users sign in to your app, they usually want to be redirected to the same section of the same page, such as /wiki/Main_Page#SectionZ. However, because URL fragments (for example, #SectionZ) are never sent to the server, they are not preserved by default after the OAuth sign-in completes and redirects back to your app. Users then get a suboptimal experience when they need to navigate to the desired anchor again. This limitation applies to all server-side authentication solutions.

In App Service authentication, you can preserve URL fragments across the OAuth sign-in. To do this, set an app setting called WEBSITE_AUTH_PRESERVE_URL_FRAGMENT to true. You can do it in the Azure portal, or simply run the following command in the Azure Cloud Shell:

az webapp config appsettings set --name <app_name> --resource-group <group_name> --settings WEBSITE_AUTH_PRESERVE_URL_FRAGMENT="true"

Access user claims

App Service passes user claims to your application by using special headers. External requests aren't allowed to set these headers, so they are present only if set by App Service. Some example headers include:

  • X-MS-CLIENT-PRINCIPAL-NAME
  • X-MS-CLIENT-PRINCIPAL-ID

Code that is written in any language or framework can get the information that it needs from these headers. For ASP.NET 4.6 apps, the ClaimsPrincipal is automatically set with the appropriate values. ASP.NET Core, however, doesn't provide an authentication middleware that integrates with App Service user claims. For a workaround, see MaximeRouiller.Azure.AppService.EasyAuth.

Your application can also obtain additional details on the authenticated user by calling /.auth/me. The Mobile Apps server SDKs provide helper methods to work with this data. For more information, see How to use the Azure Mobile Apps Node.js SDK, and Work with the .NET backend server SDK for Azure Mobile Apps.

Retrieve tokens in app code

From your server code, the provider-specific tokens are injected into the request header, so you can easily access them. The following table shows possible token header names:

Provider Header names
Azure Active Directory X-MS-TOKEN-AAD-ID-TOKEN
X-MS-TOKEN-AAD-ACCESS-TOKEN
X-MS-TOKEN-AAD-EXPIRES-ON
X-MS-TOKEN-AAD-REFRESH-TOKEN
Facebook Token X-MS-TOKEN-FACEBOOK-ACCESS-TOKEN
X-MS-TOKEN-FACEBOOK-EXPIRES-ON
Google X-MS-TOKEN-GOOGLE-ID-TOKEN
X-MS-TOKEN-GOOGLE-ACCESS-TOKEN
X-MS-TOKEN-GOOGLE-EXPIRES-ON
X-MS-TOKEN-GOOGLE-REFRESH-TOKEN
Microsoft Account X-MS-TOKEN-MICROSOFTACCOUNT-ACCESS-TOKEN
X-MS-TOKEN-MICROSOFTACCOUNT-EXPIRES-ON
X-MS-TOKEN-MICROSOFTACCOUNT-AUTHENTICATION-TOKEN
X-MS-TOKEN-MICROSOFTACCOUNT-REFRESH-TOKEN
Twitter X-MS-TOKEN-TWITTER-ACCESS-TOKEN
X-MS-TOKEN-TWITTER-ACCESS-TOKEN-SECRET

From your client code (such as a mobile app or in-browser JavaScript), send an HTTP GET request to /.auth/me. The returned JSON has the provider-specific tokens.

Note

Access tokens are for accessing provider resources, so they are present only if you configure your provider with a client secret. To see how to get refresh tokens, see Refresh access tokens.

Refresh identity provider tokens

When your provider's access token (not the session token) expires, you need to reauthenticate the user before you use that token again. You can avoid token expiration by making a GET call to the /.auth/refresh endpoint of your application. When called, App Service automatically refreshes the access tokens in the token store for the authenticated user. Subsequent requests for tokens by your app code get the refreshed tokens. However, for token refresh to work, the token store must contain refresh tokens for your provider. The way to get refresh tokens are documented by each provider, but the following list is a brief summary:

  • Google: Append an access_type=offline query string parameter to your /.auth/login/google API call. If using the Mobile Apps SDK, you can add the parameter to one of the LogicAsync overloads (see Google Refresh Tokens).
  • Facebook: Doesn't provide refresh tokens. Long-lived tokens expire in 60 days (see Facebook Expiration and Extension of Access Tokens).
  • Twitter: Access tokens don't expire (see Twitter OAuth FAQ).
  • Microsoft Account: When configuring Microsoft Account Authentication Settings, select the wl.offline_access scope.
  • Azure Active Directory: In https://resources.azure.com, do the following steps:
    1. At the top of the page, select Read/Write.

    2. In the left browser, navigate to subscriptions > <subscription_name > resourceGroups > <resource_group_name> > providers > Microsoft.Web > sites > <app_name> > config > authsettings.

    3. Click Edit.

    4. Modify the following property. Replace <app_id> with the Azure Active Directory application ID of the service you want to access.

      "additionalLoginParams": ["response_type=code id_token", "resource=<app_id>"]
      
    5. Click Put.

Once your provider is configured, you can find the refresh token and the expiration time for the access token in the token store.

To refresh your access token at any time, just call /.auth/refresh in any language. The following snippet uses jQuery to refresh your access tokens from a JavaScript client.

function refreshTokens() {
  let refreshUrl = "/.auth/refresh";
  $.ajax(refreshUrl) .done(function() {
    console.log("Token refresh completed successfully.");
  }) .fail(function() {
    console.log("Token refresh failed. See application logs for details.");
  });
}

If a user revokes the permissions granted to your app, your call to /.auth/me may fail with a 403 Forbidden response. To diagnose errors, check your application logs for details.

Extend session token expiration grace period

The authenticated session expires after 8 hours. After an authenticated session expires, there is a 72-hour grace period by default. Within this grace period, you're allowed to refresh the session token with App Service without reauthenticating the user. You can just call /.auth/refresh when your session token becomes invalid, and you don't need to track token expiration yourself. Once the 72-hour grace period is lapses, the user must sign in again to get a valid session token.

If 72 hours isn't enough time for you, you can extend this expiration window. Extending the expiration over a long period could have significant security implications (such as when an authentication token is leaked or stolen). So you should leave it at the default 72 hours or set the extension period to the smallest value.

To extend the default expiration window, run the following command in the Cloud Shell.

az webapp auth update --resource-group <group_name> --name <app_name> --token-refresh-extension-hours <hours>

Note

The grace period only applies to the App Service authenticated session, not the tokens from the identity providers. There is no grace period for the expired provider tokens.

Limit the domain of sign-in accounts

Both Microsoft Account and Azure Active Directory lets you sign in from multiple domains. For example, Microsoft Account allows outlook.com, live.com, and hotmail.com accounts. Azure AD allows any number of custom domains for the sign-in accounts. However, you may want to accelerate your users straight to your own branded Azure AD sign-in page (such as contoso.com). To suggest the domain name of the sign-in accounts, follow these steps.

In https://resources.azure.com, navigate to subscriptions > <subscription_name > resourceGroups > <resource_group_name> > providers > Microsoft.Web > sites > <app_name> > config > authsettings.

Click Edit, modify the following property, and then click Put. Be sure to replace <domain_name> with the domain you want.

"additionalLoginParams": ["domain_hint=<domain_name>"]

This setting appends the domain_hint query string parameter to the login redirect URL.

Important

It's possible for the client to remove the domain_hint parameter after receiving the redirect URL, and then login with a different domain. So while this function is convenient, it's not a security feature.

Authorize or deny users

While App Service takes care of the simplest authorization case (i.e. reject unauthenticated requests), your app may require more fine-grained authorization behavior, such as limiting access to only a specific group of users. In certain cases, you need to write custom application code to allow or deny access to the signed-in user. In other cases, App Service or your identity provider may be able to help without requiring code changes.

Server level (Windows apps only)

For any Windows app, you can define authorization behavior of the IIS web server, by editing the Web.config file. Linux apps don't use IIS and can't be configured through Web.config.

  1. Navigate to https://<app-name>.scm.azurewebsites.net/DebugConsole

  2. In the browser explorer of your App Service files, navigate to site/wwwroot. If a Web.config doesn't exist, create it by selecting + > New File.

  3. Select the pencil for Web.config to edit it. Add the following configuration code and click Save. If Web.config already exists, just add the <authorization> element with everything in it. Add the accounts you want to allow in the <allow> element.

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
       <system.web>
          <authorization>
            <allow users="user1@contoso.com,user2@contoso.com"/>
            <deny users="*"/>
          </authorization>
       </system.web>
    </configuration>
    

Identity provider level

The identity provider may provide certain turn-key authorization. For example:

Application level

If either of the other levels don't provide the authorization you need, or if your platform or identity provider isn't supported, you must write custom code to authorize users based on the user claims.

Configure using a file (preview)

Your auth settings can optionally be configured via a file that is provided by your deployment. This may be required by certain preview capabilities of App Service Authentication / Authorization.

Important

Remember that your app payload, and therefore this file, may move between environments, as with slots. It is likely you would want a different app registration pinned to each slot, and in these cases, you should continue to use the standard configuration method instead of using the configuration file.

Enabling file-based configuration

Caution

During preview, enabling file-based configuration will disable management of the App Service Authentication / Authorization feature for your application through some clients, such as the Azure portal, Azure CLI, and Azure PowerShell.

  1. Create a new JSON file for your configuration at the root of your project (deployed to D:\home\site\wwwroot in your web / function app). Fill in your desired configuration according to the file-based configuration reference. If modifying an existing Azure Resource Manager configuration, make sure to translate the properties captured in the authsettings collection into your configuration file.

  2. Modify the existing configuration, which is captured in the Azure Resource Manager APIs under Microsoft.Web/sites/<siteName>/config/authsettings. To modify this, you can use an Azure Resource Manager template or a tool like Azure Resource Explorer. Within the authsettings collection, you will need to set three properties (and may remove others):

    1. Set enabled to "true"
    2. Set isAuthFromFile to "true"
    3. Set authFilePath to the name of the file (for example, "auth.json")

Once you have made this configuration update, the contents of the file will be used to define the behavior of App Service Authentication / Authorization for that site. If you ever wish to return to Azure Resource Manager configuration, you can do so by setting isAuthFromFile back to "false".

Configuration file reference

Any secrets that will be referenced from your configuration file must be stored as application settings. You may name the settings anything you wish. Just make sure that the references from the configuration file uses the same keys.

The following exhausts possible configuration options within the file:

{
    "platform": {
        "enabled": <true|false>
    },
    "globalValidation": {
        "requireAuthentication": <true|false>,
        "unauthenticatedClientAction": "RedirectToLoginPage|AllowAnonymous|Return401|Return403",
        "redirectToProvider": "<default provider alias>",
        "excludedPaths": [
            "/path1",
            "/path2"
        ]
    },
    "identityProviders": {
        "azureActiveDirectory": {
            "enabled": <true|false>,
            "registration": {
                "openIdIssuer": "<issuer url>",
                "clientId": "<app id>",
                "clientSecretSettingName": "APP_SETTING_CONTAINING_AAD_SECRET",
            },
            "login": {
                "loginParameters": [
                    "paramName1=value1",
                    "paramName2=value2"
                ]
            },
            "validation": {
                "allowedAudiences": [
                    "audience1",
                    "audience2"
                ]
            }
        },
        "facebook": {
            "enabled": <true|false>,
            "registration": {
                "appId": "<app id>",
                "appSecretSettingName": "APP_SETTING_CONTAINING_FACEBOOK_SECRET"
            },
            "graphApiVersion": "v3.3",
            "login": {
                "scopes": [
                    "profile",
                    "email"
                ]
            },
        },
        "gitHub": {
            "enabled": <true|false>,
            "registration": {
                "clientId": "<client id>",
                "clientSecretSettingName": "APP_SETTING_CONTAINING_GITHUB_SECRET"
            },
            "login": {
                "scopes": [
                    "profile",
                    "email"
                ]
            }
        },
        "google": {
            "enabled": true,
            "registration": {
                "clientId": "<client id>",
                "clientSecretSettingName": "APP_SETTING_CONTAINING_GOOGLE_SECRET"
            },
            "login": {
                "scopes": [
                    "profile",
                    "email"
                ]
            },
            "validation": {
                "allowedAudiences": [
                    "audience1",
                    "audience2"
                ]
            }
        },
        "twitter": {
            "enabled": <true|false>,
            "registration": {
                "consumerKey": "<consumer key>",
                "consumerSecretSettingName": "APP_SETTING_CONTAINING TWITTER_CONSUMER_SECRET"
            }
        },
        "openIdConnectProviders": {
            "provider name": {
                "enabled": <true|false>,
                "registration": {
                    "clientId": "<client id>",
                    "clientCredential": {
                        "secretSettingName": "<name of app setting containing client secret>"
                    },
                    "openIdConnectConfiguration": {
                        "authorizationEndpoint": "<url specifying authorization endpoint>",
                        "tokenEndpoint": "<url specifying token endpoint>",
                        "issuer": "<url specifying issuer>",
                        "certificationUri": "<url specifying jwks endpoint>",
                        "wellKnownOpenIdConfiguration": "<url specifying .well-known/open-id-configuration endpoint - if this property is set, the other properties of this object are ignored, and authorizationEndpoint, tokenEndpoint, issuer, and certificationUri are set to the corresponding values listed at this endpoint>"
                    }
                },
                "login": {
                    "nameClaimType": "<name of claim containing name>",
                    "scope": [
                        "openid",
                        "profile",
                        "email"
                    ],
                    "loginParameterNames": [
                        "paramName1=value1",
                        "paramName2=value2"
                    ],
                }
            },
            //...
        },
        "login": {
            "routes": {
                "logoutEndpoint": "<logout endpoint>"
            },
            "tokenStore": {
                "enabled": <true|false>,
                "tokenRefreshExtensionHours": "<double>",
                "fileSystem": {
                    "directory": "<directory to store the tokens in if using a file system token store (default)>"
                },
                "azureBlobStorage": {
                    "sasUrlSettingName": "<app setting name containing the sas url for the Azure Blob Storage if opting to use that for a token store>"
                }
            },
            "preserveUrlFragmentsForLogins": <true|false>,
            "allowedExternalRedirectUrls": [
                "https://uri1.azurewebsites.net/",
                "https://uri2.azurewebsites.net/"
            ],
            "cookieExpiration": {
                "convention": "FixedTime|IdentityProviderDerived",
                "timeToExpiration": "<timespan>"
            },
            "nonce": {
                "validateNonce": <true|false>,
                "nonceExpirationInterval": "<timespan>"
            }
        },
        "httpSettings": {
            "requireHttps": <true|false>,
            "routes": {
                "apiPrefix": "<api prefix>"
            },
            "forwardProxy": {
                "convention": "NoProxy|Standard|Custom",
                "customHostHeaderName": "<host header value>",
                "customProtoHeaderName": "<proto header value>"
            }
        }
    }
}

Pin your app to a specific authentication runtime version

When you enable Authentication / Authorization, platform middleware is injected into your HTTP request pipeline as described in the feature overview. This platform middleware is periodically updated with new features and improvements as part of routine platform updates. By default, your web or function app will run on the latest version of this platform middleware. These automatic updates are always backwards compatible. However, in the rare event that this automatic update introduces a runtime issue for your web or function app, you can temporarily roll back to the previous middleware version. This article explains how to temporarily pin an app to a specific version of the authentication middleware.

Automatic and manual version updates

You can pin your app to a specific version of the platform middleware by setting a runtimeVersion setting for the app. Your app always runs on the latest version unless you choose to explicitly pin it back to a specific version. There will be a few versions supported at a time. If you pin to an invalid version that is no longer supported, your app will use the latest version instead. To always run the latest version, set runtimeVersion to ~1.

View and update the current runtime version

You can change the runtime version used by your app. The new runtime version should take effect after restarting the app.

View the current runtime version

You can view the current version of the platform authentication middleware either using the Azure CLI or via one of the built0-in version HTTP endpoints in your app.

From the Azure CLI

Using the Azure CLI, view the current middleware version with the az webapp auth show command.

az webapp auth show --name <my_app_name> \
--resource-group <my_resource_group>

In this code, replace <my_app_name> with the name of your app. Also replace <my_resource_group> with the name of the resource group for your app.

You will see the runtimeVersion field in the CLI output. It will resemble the following example output, which has been truncated for clarity:

{
  "additionalLoginParams": null,
  "allowedAudiences": null,
    ...
  "runtimeVersion": "1.3.2",
    ...
}
From the version endpoint

You can also hit /.auth/version endpoint on an app also to view the current middleware version that the app is running on. It will resemble the following example output:

{
"version": "1.3.2"
}

Update the current runtime version

Using the Azure CLI, you can update the runtimeVersion setting in the app with the az webapp auth update command.

az webapp auth update --name <my_app_name> \
--resource-group <my_resource_group> \
--runtime-version <version>

Replace <my_app_name> with the name of your app. Also replace <my_resource_group> with the name of the resource group for your app. Also, replace <version> with a valid version of the 1.x runtime or ~1 for the latest version. You can find the release notes on the different runtime versions [here] (https://github.com/Azure/app-service-announcements) to help determine the version to pin to.

You can run this command from the Azure Cloud Shell by choosing Try it in the preceding code sample. You can also use the Azure CLI locally to execute this command after executing az login to sign in.

Next steps