OAuth 2.0 Implicit Grant Flow Implementation
Overview
Microsoft’s Active Directory is a product that has been around the market for several years, however, in the old days, it was designed to keep an on-premises approach, until Microsoft decided to launch Azure Active Directory (AAD). What would be the difference? Well, it is a cloud (PAAS) solution that now widens to a new set of opportunities. One of the new features of having Azure Active Directory is that it can be used to secure any REST API, all by leveraging the integration between AAD and OAuth 1.0 or OAuth 2.0 protocols. Plus, if a certain company already has unified their SSO with AAD this would work like a charm.
OpenID Connect is another protocol, built on top of OAuth 2.0, which is also supported by Microsoft Identity Platform.
According to Microsoft:
The Microsoft identity platform implementation of OAuth 2.0 and OpenID Connect makes extensive use of bearer tokens, including bearer tokens represented as JWTs. A bearer token is a lightweight security token that grants the “bearer” access to a protected resource. In this sense, the “bearer” is any party that can present the token.
Source: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#tokens
To use AAD OAuth 1.0 endpoints, it is simple in terms of configuration, as AAD is already OAuth 1.0 compliant.
AAD Oauth 2.0 endpoints have a different story. Out of the box, it is not OpenID Connect compliant, and some extra configurations are required to make it compliant. This has to do with the introduction of Microsoft Identity Platform, which according to many readings and GitHub posts, all of the retrieved access_token’s are prepared primarily for querying Microsoft Graph (Graph audience) and not for securing REST API’s (Application Client ID audience). This is because Microsoft Graph’s JWT access_token have extra JWT information which is not OIDC compliant, the extra information is the nonce claim to the header, the complete GitHub explanation is here.
Fortunately, there is a way to make an OAuth 2.0 OIDC compliant. In this article, it will be presented a way to create a RESTful API using Spring Boot along with Spring Boot Starters for Microsoft’s Azure Active Directory (AD).
The approach indicated in this document is, according to Microsoft, the best option for Single Page Applications (SPA) that require to consume REST APIs.
Assumptions.
The tutorial assumes the reader already has a subscription to Azure, hence the details about it will not be covered.
Git public repository.
https://gitlab.com/dou.armandom/aad-springboot-secured-restful-service
Configure an Active Directory instance.
- Go to https://portal.azure.com and provide the login credentials.
- Once logged in, at the top search bar look for Azure Active Directory.

- An Organization Name and an Initial Domain Name will be asked.

- Click on Review + create button, and a summary will be presented. Click on the Create Once completed, it will take a few minutes to allocate the resources.

- Now Active Directory instance was created, it should look like as follows.

Register an application.
- At the top, on the search bar, type App registrations, the first option presented will be the needed one, click on it.

- 1. Click on New registration.

- Type in secured-restful in the Name field.

- Take note of the Application (client) ID and Directory (tenant) ID fields. They will be used later on to configure the Spring Boot application and Postman.

Authentication section.
- On the same page, click on
- Click on Add a platform
- Click on Web Applications.

- Configure the value for the Redirect URI field as http://localhost:8080, also check the Access tokens and ID tokens.

Manifest section.
- Still, in the App Registration screen, change two values in the app manifest, hence click on Manifest menu entry, the value for oauth2AllowImplicitFlow needs to be set to true, the value for accessTokenAcceptedVersion will be set to .

Exposing an API section.
- Since Azure Active Directory will always return Microsoft Graph access_tokens an extra configuration is required. It consists of creating a custom scope. Again, still inside the App Registration page, look for the Expose an API section in the left bar, then click on Add a scope button. Fill the Application ID URI with the value api://digitalonus.oidccompliant, then hit Save and continue button. The value that was written could be changed to any other value it is not relevant.

- In the upcoming screen, some new fields will be presented, set the values as follows:
-
- Scope name: dou.read
- Who can consent? Admins and users
- Admin consent display name: Scope that causes AD to return OIDC compliant access_tokens.
- Admin consent description: Scope that causes AD to return OIDC compliant access_tokens.
- State: Enabled

- Hit Add scope button.
Wiring up the Spring Boot project.
To make it easier to start, the Spring Initializr will be used.
- Go to https://start.spring.io.
- Selected options will be:
- Project type: Gradle 5.x
- Language: Java
- Spring Boot version: 2.2.6
- Metadata:
- Group: com.digitalonus
- Artifact: secured-restful-service
- Name: secured-restful-service
- Description: Secured RESTFul API service for Spring Boot
- Package name: com.digitalonus.secured-restful-service
- Packaging: Jar
- Java version: 11

- In the dependencies section, add the following ones:
- Lombok
- Spring Web
- Spring Security
- Azure Active Directory

- Once indicated project dependencies and artifacts specs, hit the Generate button at the bottom of the web page, it must download a .zip file, it contains the project itself.
Spring Boot app configuration.
- Open up the ZIP file that was created with Spring Initializr and extract its contents to a folder in the hard drive.
- Change the following line to cause an upgrade to the latest (2.2.4) stable version of Azure spring boot starter:
set(‘azureVersion’, “2.2.4”) - Identify the package com.digitalonus.securedrestfulservice inside the src/main/java directory and create the package called com.digitalonus.securedrestfulservice.dto
- Create a java class called Contact inside com.digitalonus.securedrestfulservice.dto package. The content is as follows:
package com.digitalonus.securedrestfulservice.dto; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @Data @AllArgsConstructor @Builder public class Contact { private final String name; private final String url; private final String email; }
- Create the package com.digitalonus.securedrestfulservice.controller.
- Inside the just created controller package, create a file with the name SampleController. The content is as follows:
package com.digitalonus.securedrestfulservice.controller; import com.digitalonus.securedrestfulservice.dto.Contact; import com.digitalonus.securedrestfulservice.services.SampleService; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @RequiredArgsConstructor @RestController public class SampleController { private final SampleService sampleService; @GetMapping("/samples") @PreAuthorize("hasRole('ROLE_USER')") // This is validating against Active Directory's User role granted to the current user. @ResponseStatus(HttpStatus.OK) public Contact getContactDetails() { return sampleService.printDetails(); } }
- Create the package com.digitalonus.securedrestfulservice.services.
- Create a file called SampleService inside the just created services package.
package com.digitalonus.securedrestfulservice.services; import com.digitalonus.securedrestfulservice.dto.Contact; import org.springframework.stereotype.Service; @Service public class SampleService { public static final Contact DEFAULT_CONTACT = new Contact("Digital OnUs", "http://digitalonus.com", "gitlab@digitalonus.com"); public Contact printDetails() { return DEFAULT_CONTACT; } }
- Create the package com.digitalonus.securedrestfulservice.config.
- Create the java class WebSecurityConfig inside com.digitalonus.securedrestfulservice.config package. The content is as follows:
package com.dou.people.config; import com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter; import lombok.RequiredArgsConstructor; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @RequiredArgsConstructor @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final AADAppRoleStatelessAuthenticationFilter aadAuthFilter; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER); http.authorizeRequests() .antMatchers("/", "/index.html", "/public").permitAll() .anyRequest().authenticated(); http.addFilterBefore(aadAuthFilter, UsernamePasswordAuthenticationFilter.class); } }
- Finally, look for the application.properties file, it is located under project root folder src/main/resources. Its contents should include the following properties.
#logging levels for different packages # it is suggested to use DEBUG while developing only logging.level.org.springframework.data=DEBUG logging.level.com.dou.people=DEBUG logging.level.org.springframework.web=DEBUG logging.level.org.springframework.security=DEBUG logging.level.com.microsoft.azure=DEBUG azure.activedirectory.client-id=${AD_APP_CLIENT_ID} azure.activedirectory.session-stateless=true
Running the application.
Once the previous configurations are in place, following Gradle commands should be executed:
- On a command line, go to the root of the project where the build.properties file is located.
- Execute following gradle tasks to run the application
export AD_APP_CLIENT_ID=your_secret_id_goes_here
gradle clean bootRun
Implicit RESTful service testing with Postman.
- Open Postman app, for further details about setup, go to:

- Click on New Button, select Collection type.

- Set the Name to Secured RESTful Service test.

- Click on Type dropdown and choose option OAuth 2.0.
- Click in the orange button with the legend Get New Access Token.
- A new floating dialog will show up, the following values will be required:
- Grant Type: Implicit
- Callback URL: http://localhost:8080
- See the Authentication section, the last step.
- Auth: URL: https://login.microsoftonline.com/{{tenant_id}}/oauth2/v2.0/authorize?
- See Register an application, last step.
- Additionally, the endpoints might be obtained from the following procedure:
- In the search bar, type App registrations, click on that option once it shows up.

- Click on Endpoints

- All of the available endpoints will be presented.

- Client ID: {{client_id}} // this value comes from Azure AD config
- See Register an application, last step.
- Scope: openid api://digitalonus.oidccompliant/dou.read
- See Exposing an API section, the last step.
- State: <any random string>.
- Client Authentication: Send as Basic Auth header

- Click on the Request Token
- A new token will be presented, click on Orange Button with the legend Use Token.


- Add a request to the Secured RESTful Service test collection on Postman. Click in the three dots (or right-click on it), then select Add Request.

- Give any name to the field Request name.

- Open up the newly created request, add the following URL, and make sure authorization is set to Inherit auth from the parent before hitting the Send button.

- Validate the output.
Note: Using the Authorization Type as No Auth will cause to get a 403-Forbidden error response.
References
- https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols
- https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-implicit-grant-flow
- https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad
- https://medium.com/@abhinavsonkar/making-azure-ad-oidc-compliant-5734b70c43ff
- https://tsmatz.wordpress.com/2017/06/22/web-api-and-custom-scope-with-azure-ad-v2-endpoint/
- https://tsmatz.wordpress.com/2018/02/07/azure-active-directory-v2-endpoint-custom-scope-for-admin-consent-and-application-permissions/
- http://www.redbaronofazure.com/?p=7607
- https://www.taithienbo.com/how-can-you-trust-a-jwt-to-be-authentic/