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:

When moving existing applications to the cloud, it can be difficult to modernize the underlying authentication system to support single sign‑on.

When building a microservices‑style application, it is preferable to centralize the authentication and access control functions rather than repeat them over and over in each microservice.

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.

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.

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:

The signature can be validated with the key found in the auth_jwt_key_file (matching on the kid header field if present).

The JWT is presented inside the validity period, when defined by one or both of the nbf (not before) and exp (expires) claims. (See lines 7–8 of the sample JWT above; the times are representend in UNIX epoch time.)

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.

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.

Have a Cookie? :)

Our site uses cookies to provide functionality and performance as well as for social media and advertising purposes. Social media and advertising cookies of third parties are used to offer you social media functionalities and personalized ads for NGINX content and offers. To get more information about these cookies and how we process personal data, check our Privacy Policy. Do you accept the use of cookies and the processing of personal data involved?

Your Cookie Settings

Site functionality and performance

These cookies are required for NGINX site functionality and are therefore always enabled. These include cookies that allow you to be remembered as you explore the NGINX site, help make the shopping cart and checkout process possible as well as assist in security issues and conforming to regulations. To use the NGINX website, you have to consent to these cookies and the processing of personal data according to the NGINX website terms of use and privacy policy.

Social media and advertising

Social media cookies offer the possibility to connect you to your social networks and share content from our website through social media. Advertising cookies (of third parties) collect information to help better tailor NGINX advertising to your interests, both within and beyond NGINX websites. De-selecting these cookies may result in seeing advertising that is not as relevant to you or you not being able to link effectively with Facebook, Twitter, or other social networks and/or not allowing you to share content on social media.