Conditional Access Control with Microsoft Azure Active Directory - NGINX

Original: https://www.nginx.com/blog/conditional-access-control-with-microsoft-azure-active-directory/


For many organizations, Microsoft Active Directory represents the single, canonical source of truth for the identities of employees and trusted users. When building and deploying cloud‑based business applications, the Azure platform is particularly attractive due to its native integration with Active Directory.

Microsoft Azure supports a wide range of standards‑based federated identity and single sign‑on technologies to help developers authenticate, consume, and make decisions based on the identities of users defined in Active Directory. However, there are some scenarios where it is challenging or undesirable to implement these standards‑based technologies at the application layer, such as:

OpenID Connect has emerged as a technology that is equally applicable to both single sign‑on for applications and to API client authentication. When NGINX Plus is deployed as a reverse proxy or API gateway for these scenarios, we can offload the validation of OpenID Connect tokens to NGINX Plus. This approach means that authentication happens in one place, and the application only deals with successfully authenticated clients.

Figure 1. Overview of NGINX Plus validating Azure Active Directory identity tokens

In this blog post we show how to use NGINX Plus to validate OpenID Connect tokens issued by Azure, and also to apply fine‑grained access control based on group membership assignments made in Azure Active Directory.

Validating OpenID Connect Logins with NGINX Plus

The OpenID Connect authentication process ultimately issues an identity token to the user/client, which can then be presented as a proof of authentication when accessing protected resources. The identity token uses the JSON Web Token (JWT) standard, which is an extremely flexible and portable data format for carrying user information.

Note: For an introduction to JWTs, see Authentication and Content‑Based Routing with JWTs and NGINX Plus.

Once a web browser or API client is successfully authenticated by the Azure login system, Azure can issue it an identity token (as a JWT). A sample, decoded Azure identity token (Id_token) is shown below. The token contains several useful pieces of user information, including the email address and the user’s real name, which can be used by an application to provide a personalized user experience. Note that this Azure identity token also contains a list of group memberships (lines 13–17) as a JSON array.

Validating Azure JWTs with NGINX Plus

Basic configuration for validating Azure JWTs is as simple as adding two lines to your NGINX Plus configuration.

This configuration can apply to an entire site or to specific URIs on a per‑location basis. The auth_jwt directive specifies that a valid JWT must be provided by the client. A JWT is considered to be valid when the following conditions are met:

By having NGINX Plus perform JWT validation, we can offload the authentication process from the backend applications and APIs. Not only does this increase the backends’ capacity for processing requests, it also ensures that only properly authenticated requests ever reach the backend.

To validate Azure identity tokens, we need to provide NGINX Plus with Microsoft’s public JWT signing keys. We store these signing keys adjacent to our NGINX Plus configuration so the target filename matches that specified by the auth_jwt_key_file directive.

$ curl -sS https://login.microsoftonline.com/common/discovery/keys > /etc/nginx/azure.jwk

Microsoft maintains several signing keys simultaneously so that keys can be rotated without invalidating tokens already issued. It is therefore prudent to routinely refresh this file with a cron(8) job.

Role-Based Access Control

As seen in the sample JWT above, a significant advantage of using OpenID Connect identity tokens with Microsoft Azure is that they can also contain group membership information. With this information present in the identity token we can use NGINX Plus not only to validate the token but also to perform role‑based access control based on the group memberships.

To ensure that group memberships appear in the groups JWT claim, set the value of groupMembershipClaims in the App registration manifest to "SecurityGroup", or to "All" to also include Office 365 groups.

Figure 2. Editing the App registration manifest to include group memberships in JWT claims

For our demonstration scenario, we are using NGINX Plus to protect a web application that is only available to the Finance group. This is just one of a number of groups defined in Azure Active Directory.

Figure 3. Groups in Microsoft Azure Active Directory

When examining the properties of the Finance group, the unique Object ID is the value that appears in the groups claim for users assigned to this group (highlighted in Figure 4).

Figure 4. Obtaining the Object ID for the Finance group

The Object ID for our Finance group is 566a3987-eff4-4c6d-9df3-0b23e5f49b1e. We use this value in the NGINX Plus configuration (line 4 below) to ensure that only members of the Finance group can get access to our backend application.

On line 1 we define a variable, $jwt_groups, that evaluates to the value of the groups claim in the JWT. NGINX Plus automatically provides variables for JWT claims; however, if the claim contains special characters, or if the value is an array or a nested JSON object, then the auth_jwt_claim_set directive must be used to access that value. For ease of processing, JSON arrays are evaluated as a comma‑separated list.

We pass this comma‑separated list of group memberships into the map starting on line 3. The result of the map is a new variable, $isFinance, which by default evaluates to 0. A regular expression is used to test whether the $jwt_groups variable contains the Azure Object ID for the Finance group.

The server block starting on line 8 describes a simple reverse proxy for the Finance app. The auth_jwt directive on line 12 specifies that the entire application requires JWT validation and that the JWT itself must be supplied as a cookie named auth_token.

On line 17 we test whether the JWT includes the Finance group by evaluating the $isFinance variable. Users not in the Finance group receive an HTTP 403 error. The user experience for this error can be improved by creating a custom error page (commented line 20).

Requests with a successfully validated JWT containing the Finance group are ultimately proxied to the backend application (line 25). Also, the email claim is proxied to the backend as an additional HTTP header (line 24), where it can be easily consumed by the application.

Summary

The OpenID Connect and JWT standards offer huge flexibility for building applications that require single sign‑on and consume identity information. The example above shows how NGINX Plus can be used as a centralized security service to offload token validation and fine‑grained access control from the backends. The same solution can be used for reverse proxy and load balancing to a web application, or as an API gateway at the heart of a microservices application architecture.

For more information about using JWT with NGINX Plus, please see:

To explore the native JWT support in NGINX Plus for yourself, start your free 30‑day trial today or contact us.

Retrieved by Nick Shadrin from nginx.com website.