services | platforms | author | level | client | service | endpoint | page_type | languages | products | description | |||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
active-directory |
java |
ramya25 |
300 |
Java web app |
Java Web API |
Microsoft identity platform |
sample |
|
|
This sample demonstrates a Java web app application calling a Java Web API that is secured using Azure Active Directory using the On-Behalf-Of flow |
A Java Web API that calls another web API with the Microsoft identity platform using the On-Behalf-Of flow
This sample demonstrates a Java web application signing-in a user with the Microsoft Identity Platform and also obtaining an access token for the Web API. The Web API, in turn calls the Microsoft Graph using an access token obtained using the on-behalf-of flow. All these are secured using the Microsoft identity platform (formerly Azure Active Directory for developers).
- The Java web application uses the Microsoft Authentication Library for Java (MSAL4J) to obtain an Access token from the Microsoft identity platform for the authenticated user.
- The access token is then used as a bearer token to the request to the Java web API. The web API validates the access token using Spring Security, exchanges the incoming access token for a Microsoft Graph access token using OAuth2.0 On-behalf-of flow, and uses the new access token to request information from the Graph Me endpoint.
The flow is as follows:
- Sign-in the user in the client(web) application.
- Acquire an access token for the Java Web API and call it.
- The Java Web API validates the access token using Spring Security and then calls another downstream Web API (The Microsoft Graph) after obtaining another access token using the on-behalf-of flow.
To run this sample, you'll need:
- Working installation of Java and Maven
- An Azure Active Directory (Azure AD) tenant. For more information on how to get an Azure AD tenant, see How to get an Azure AD tenant
- An user account in your Azure AD tenant.
To successfully use this sample, you need a working installation of Java and Maven.
From your shell or command line:
git clone https://github.com/Azure-Samples/ms-identity-java-webapi.git
There are two projects in this sample. Each needs to be registered separately in your Azure AD tenant. To register these projects:
As a first step you'll need to:
- Sign in to the Azure portal using either a work or school account or a personal Microsoft account.
- If your account is present in more than one Azure AD tenant, select your profile at the top right corner in the menu on top of the page, and then switch directory. Change your portal session to the desired Azure AD tenant.
- In the portal menu, select the Azure Active Directory service, and then select App registrations.
In the next steps, you might need the tenant name (or directory name) or the tenant ID (or directory ID). These are presented in the Properties of the Azure Active Directory window respectively as Name and Directory ID
- Navigate to the Microsoft identity platform for developers App registrations page.
- Click New registration.
- In the Register an application page that appears, enter your application's registration information:
- In the Name section, enter a meaningful application name that will be displayed to users of the app, for example
Java-webapi
. - Change Supported account types to Accounts in any organizational directory and personal Microsoft accounts (e.g. Skype, Xbox, Outlook.com).
- In the Name section, enter a meaningful application name that will be displayed to users of the app, for example
- Click on the Register button to create the application.
- In the app's registration Overview page, find the Application (client) ID and Directory (tenant) ID values and record it for use later. You'll need them to configure the configuration file(s) later in your code.
- In the Application menu blade, click on the Certificates & secrets to open the page where we can generate secrets and upload certificates.
- In the Client secrets section, click on New client secret:
- Type a key description (for instance
app secret
), - Select one of the available key durations (In 1 year, In 2 years, or Never Expires) as per your security concerns.
- The generated key value will be displayed when you click the Add button. Copy the generated value for use in the steps later.
- You'll need this key later in your code's configuration files. This key value will not be displayed again, and is not retrievable by any other means, so make sure to note it from the Azure portal before navigating to any other screen or blade.
- Type a key description (for instance
- In the Application menu blade, click on the API permissions to open the page where we add access to the Apis that your application needs.
- Click the Add a permission button and then,
- Ensure that the Microsoft APIs tab is selected.
- In the Commonly used Microsoft APIs section, click on Microsoft Graph
- In the Delegated permissions section, select the User.Read in the list. Use the search box if necessary.
- Click on the Add permissions button in the bottom.
- In the Application menu blade, click on the Expose an API to open the page where declare the parameters to expose this app as an Api for which client applications can obtain access tokens for.
The first thing that we need to do is to declare the unique resource URI that the clients will be using to obtain access tokens for this Api. To declare an resource URI, follow the following steps:
- Click
Set
next to the Application ID URI to generate a URI that is unique for this app. - For this sample, accept the proposed Application ID URI (api://{clientId}) by selecting Save, and record the URI for later reference.
- Click
- All Apis have to publish a minimum of one scope for the client's to obtain an access token successfully. To publish a scope, follow the following steps:
- Select Add a scope button open the Add a scope screen and Enter the values as indicated below:
- For Scope name, use
access_as_user
. - Select Admins and users options for Who can consent?
- For Admin consent display name type
Access Java-webapi
- For Admin consent description type
Allows the app to access Java-webapi as the signed-in user.
- For User consent display name type
Access Java-webapi
- For User consent description type
Allow the application to access Java-webapi on your behalf.
- Keep State as Enabled
- Click on the Add scope button on the bottom to save this scope.
- Record the scope's URI (api://{clientid}/access_as_user) for later reference.
- For Scope name, use
- Select Add a scope button open the Add a scope screen and Enter the values as indicated below:
Open application.properties
in the src/main/resources folder. Fill in with your tenant and app registration information noted in the above registration step.
- Replace Enter_the_Tenant_Info_Here with Directory (tenant) ID.
- Enter_the_Application_Id_here with the Application (client) ID.
- Enter_the_Client_Secret_Here with the key value noted earlier.
- Navigate to the Microsoft identity platform for developers App registrations page.
- Click New registration.
- In the Register an application page that appears, enter your application's registration information:
- In the Name section, enter a meaningful application name that will be displayed to users of the app, for example
java-webapp
. - Change Supported account types to Accounts in any organizational directory and personal Microsoft accounts (e.g. Skype, Xbox, Outlook.com).
Note that there are more than one redirect URIs used in this sample. You'll need to add them from the Authentication tab later after the app has been created successfully.
- In the Name section, enter a meaningful application name that will be displayed to users of the app, for example
- Click on the Register button to create the application.
- In the app's registration Overview page, find the Application (client) ID value and record it for later. You'll need it to configure the configuration file(s) later in your code.
- In the app's registration screen, click on the Authentication blade in the left and:
- In the Platform configurations section select Add a platform and create a new Web application
- Enter the following as the redirect URI:
http://localhost:8080/msal4jsample/secure/aad
- Click on Configure to save your changes.
- Click the Save button to save the the redirect URI changes.
- In the Application menu blade, click on the Certificates & secrets to open the page where we can generate secrets and upload certificates.
- In the Client secrets section, click on New client secret:
- Type a key description (for instance
app secret
), - Select one of the available key durations (In 1 year, In 2 years, or Never Expires) as per your security concerns.
- The generated key value will be displayed when you click the Add button. Copy the generated value for use in the steps later.
- You'll need this key later in your code's configuration files. This key value will not be displayed again, and is not retrievable by any other means, so make sure to note it from the Azure portal before navigating to any other screen or blade.
- Type a key description (for instance
- In the Application menu blade, click on the API permissions to open the page where we add access to the Apis that your application needs.
- Click the Add a permission button and then,
- Ensure that the My APIs tab is selected.
- In the list of APIs, select the API you created previously,
Java-webapi
. - In the Delegated permissions section, select the access_as_user in the list.
- Click on the Add permissions button in the bottom.
Open application.properties
in the msal-web-sample/src/main/resources folder. Fill in with your tenant and app registration information noted in registration step.
- Replace Enter_the_Application_Id_here with the Application (client) ID.
- Replace Enter_the_Client_Secret_Here with the key value noted earlier.
- Replace Enter_the_Api_Scope_Here with the API exposed in the
Web Api app
(api://{clientId}).
If you are only testing locally, you may skip this step. If you deploy your app to Azure App Service (for production or for testing), https is handled by Azure and you may skip this step. Note that https is essential for providing critical security and data integrity to your applications, and http should not be used outside of testing scenarios. If you need to configure your application to handle https, complete the instructions in this section.
-
Use the
keytool
utility (included in JRE) if you want to generate self-signed certificate.keytool -genkeypair -alias testCert -keyalg RSA -storetype PKCS12 -keystore keystore.p12 -storepass password
-
Put the following key-value pairs into your application.properties file.
server.ssl.key-store-type=PKCS12 server.ssl.key-store=classpath:keystore.p12 server.ssl.key-store-password=password server.ssl.key-alias=testCert
-
Change both occurrences of
8080
to8443
in the msal-web-sample's application.properties file. -
Update your java_webapp Azure AD application registration redirects (e.g.,
https://localhost:8443/msal4jsample/secure/aad
andhttps://localhost:8443/msal4jsample/graph/me
) on the Azure Portal.
For a middle tier web API (Java-webapi
) to be able to call a downstream web API, the middle tier app needs to be granted the required permissions as well.
However, since the middle tier cannot interact with the signed-in user, it needs to be explicitly bound to the client app in its Azure AD registration.
This binding merges the permissions required by both the client and the middle tier WebApi and and presents it to the end user in a single consent dialog. The user than then consent to this combined set of permissions.
To achieve this, you need to add the "Client ID" of the client app, in the manifest of the web API in the knownClientApplications property. Here's how:
In the Azure portal, navigate to your Java-webapi
app registration:
- In the Application menu blade, select Manifest.
- Find the attribute knownClientApplications and add your client application's(
Java-webapp
) Application (client) Id as its element. - Click Save.
To run the project, you can either:
Run it directly from your IDE by using the embedded spring boot server or package it to a WAR file using maven and deploy it a J2EE container solution for example Tomcat
If you are running the application from an IDE, follow the steps below.
The following steps are for IntelliJ IDEA. But you can choose and work with any editor of your choice.
- Navigate to Run --> Edit Configurations from menu bar.
- Click on '+' (Add new configuration) and select Application.
- Enter name of the application for example
webapp
- Go to main class and select from the dropdown, for example
MsalWebSampleApplication
also go to Use classpath of the module and select from the dropdown, for examplemsal-web-sample
. - Click on Apply. Follow the same instructions for adding the other application.
- Click on '+' (Add new configuration) and select Compound.
- Enter a friendly name for in the Name for example
Msal-webapi-sample
. - Click on '+' and select the application names you have created in the above steps one at a time.
- Click on Apply. Select the created configuration and click Run. Now both the projects will run at a time.
- Now navigate to the home page of the project. For this sample, the standard home page URL is https://localhost:8080/msal4jsample
If you would like to deploy the sample to Tomcat, you will need to make a couple of changes to the source code in both modules.
-
Open msal-webapp-sample/pom.xml
-
Under
<name>msal-web-sample</name>
add<packaging>war</packaging>
-
Add dependency:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency>
-
-
Open msal-web-sample/src/main/java/com.microsoft.azure.msalwebsample/MsalWebSampleApplication
- Delete all source code and replace with
package com.microsoft.azure.msalwebsample; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; @SpringBootApplication public class MsalWebSampleApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(MsalWebSampleApplication.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(MsalWebSampleApplication.class); } }
-
Open a command prompt, go to the root folder of the project, and run
mvn package
- This will generate a
msal-web-sample-0.1.0.war
file in your /targets directory. - Rename this file to
msal4jsample.war
- Deploy this war file using Tomcat or any other J2EE container solution.
- To deploy on Tomcat container, copy the .war file to the webapp's folder under your Tomcat installation and then start the Tomcat server.
- Repeat these steps for the
msal-obo-sample
also.
This WAR will automatically be hosted at http:<yourserverhost>:<yourserverport>/
Tomcats default port is 8080. This can be changed by - Going to tomcat/conf/server.xml - Search "Connector Port" - Replace "8080" with your desired port number
Example: http://localhost:8080/msal4jsample
Click on "Login" to start the process of logging in. Once logged in, you'll see the account information for the user that is logged in and a Button "Call OBO API" , which will call the Microsoft Graph API with the OBO token and display the basic information of the signed-in user. You'll then have the option to "Sign out".
There are many key points in this sample to make the On-Behalf-Of-(OBO) flow work properly and in this section we will explain these key points for each project.
-
AuthPageController class
Contains the api to interact with the web app. The
securePage
method handles the authentication part and signs in the user using microsoft authentication. -
AuthHelper class
Contains helper methods to handle authentication.
A code snippet showing how to obtain auth result by silent flow.
private ConfidentialClientApplication createClientApplication() throws MalformedURLException { return ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.create(clientSecret)) .authority(authority) .build(); }... SilentParameters parameters = SilentParameters.builder( Collections.singleton(scope), result.account()).build(); CompletableFuture<IAuthenticationResult> future = app.acquireTokenSilently(parameters); ... storeTokenCacheInSession(httpRequest, app.tokenCache().serialize()); ...
Important things to notice:
- We create a
ConfidentialClientApplication
using MSAL Build Pattern passing theclientId
,clientSecret
andauthority
in the builder. ThisConfidentialClientApplication
will be responsible of acquiring access tokens later in the code. ConfidentialClientApplication
also has a token cache, that will cache access tokens and refresh tokens for the signed-in user. This is done so that the application can fetch access tokens after they have expired without prompting the user to sign-in again.
- We create a
-
AuthFilter class
Contains methods for session and state management.
-
ApiController class
Uses the Java Microsoft Graph SDK to call the the api(graphMeApi). The
GraphServiceClient
uses theoboAuthProvider
to acquire the necessary tokens to access the Graph Me endpoint. -
SecurityResourceServerConfig class
Token Validation of the caller happens in this class, where the access token presented by the client app is validated using Spring Security.
http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/*") .access("#oauth2.hasScope('" + accessAsUserScope + "')"); // required scope to access /api URL
-
OboAuthProvider class
Contains the methods to exchange the incoming token for an access token for Microsoft Graph.
A code snippet showing how to obtain obo token
IAuthenticationResult authResult; ConfidentialClientApplication application = null; try { application = ConfidentialClientApplication .builder(clientId, ClientCredentialFactory.createFromSecret(secret)) .authority(authority) .build(); String cachedTokens = cacheManager.getCache("tokens").get(cacheKey, String.class); if (cachedTokens != null) { application.tokenCache().deserialize(cachedTokens); } SilentParameters silentParameters = SilentParameters.builder(Collections.singleton(scope)) .build(); authResult = application.acquireTokenSilently(silentParameters).join(); } catch (Exception ex) { if (ex.getCause() instanceof MsalException) { OnBehalfOfParameters parameters = OnBehalfOfParameters.builder(Collections.singleton(scope), new UserAssertion(authToken)) .build(); authResult = application.acquireToken(parameters).join(); } else { throw new AuthException(String.format("Error acquiring token from AAD: %s", ex.getMessage()), ex.getCause()); } }
Important things to notice:
-
application.acquireTokenSilently
is attempted first to try and use the cached tokens. If the silent call fails, the sample falls back to trying to acquire a token via obo. -
The scope .default is a built-in scope for every application that refers to the static list of permissions configured on the application registration. In our scenario here, it enables the user to grant consent for permissions for both the Web API and the downstream API (Microsoft Graph). For example, the permissions for the Web API and the downstream API (Microsoft Graph) are listed below: - Web Api sample (access_as_user) - Microsoft Graph (user.read)
-
When you use the
.default
scope, the end user is prompted for a combined set of permissions that include scopes from both the Web Api and Microsoft Graph.
-
Use Stack Overflow to get support from the community.
Ask your questions on Stack Overflow first and browse existing issues to see if someone has asked your question before.
Make sure that your questions or comments are tagged with [msal4j
Java
].
If you find a bug in the sample, please raise the issue on GitHub Issues.
To provide a recommendation, visit the following User Voice page.
If you'd like to contribute to this sample, see CONTRIBUTING.MD.
This project has adopted the Microsoft Open Source Code of Conduct. For more information, see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.
- For more information, see MSAL4J conceptual documentation
- Other samples for Microsoft identity platform are available from https://aka.ms/aaddevsamplesv2
- Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow
- The documentation for Microsoft identity platform is available from https://aka.ms/aadv2
- For more information about web apps scenarios on the Microsoft identity platform see Scenario: Web app that signs in users and Scenario: Web app that calls web APIs
- Why update to Microsoft identity platform?
- For more information about how OAuth 2.0 protocols work in this scenario and other scenarios, see Authentication Scenarios for Azure AD.